package hcldec

import (
	"fmt"
	"reflect"
	"testing"

	"github.com/hashicorp/hcl/v2"
	"github.com/hashicorp/hcl/v2/hclsyntax"
	"github.com/zclconf/go-cty/cty"
)

func TestVariables(t *testing.T) {
	tests := []struct {
		config string
		spec   Spec
		want   []hcl.Traversal
	}{
		{
			``,
			&ObjectSpec{},
			nil,
		},
		{
			"a = foo\n",
			&ObjectSpec{},
			nil, // "a" is not actually used, so "foo" is not required
		},
		{
			"a = foo\n",
			&AttrSpec{
				Name: "a",
			},
			[]hcl.Traversal{
				{
					hcl.TraverseRoot{
						Name: "foo",
						SrcRange: hcl.Range{
							Start: hcl.Pos{Line: 1, Column: 5, Byte: 4},
							End:   hcl.Pos{Line: 1, Column: 8, Byte: 7},
						},
					},
				},
			},
		},
		{
			"a = foo\nb = bar\n",
			&DefaultSpec{
				Primary: &AttrSpec{
					Name: "a",
				},
				Default: &AttrSpec{
					Name: "b",
				},
			},
			[]hcl.Traversal{
				{
					hcl.TraverseRoot{
						Name: "foo",
						SrcRange: hcl.Range{
							Start: hcl.Pos{Line: 1, Column: 5, Byte: 4},
							End:   hcl.Pos{Line: 1, Column: 8, Byte: 7},
						},
					},
				},
				{
					hcl.TraverseRoot{
						Name: "bar",
						SrcRange: hcl.Range{
							Start: hcl.Pos{Line: 2, Column: 5, Byte: 12},
							End:   hcl.Pos{Line: 2, Column: 8, Byte: 15},
						},
					},
				},
			},
		},
		{
			"a = foo\n",
			&ObjectSpec{
				"a": &AttrSpec{
					Name: "a",
				},
			},
			[]hcl.Traversal{
				{
					hcl.TraverseRoot{
						Name: "foo",
						SrcRange: hcl.Range{
							Start: hcl.Pos{Line: 1, Column: 5, Byte: 4},
							End:   hcl.Pos{Line: 1, Column: 8, Byte: 7},
						},
					},
				},
			},
		},
		{
			`
b {
  a = foo
}
`,
			&BlockSpec{
				TypeName: "b",
				Nested: &AttrSpec{
					Name: "a",
				},
			},
			[]hcl.Traversal{
				{
					hcl.TraverseRoot{
						Name: "foo",
						SrcRange: hcl.Range{
							Start: hcl.Pos{Line: 3, Column: 7, Byte: 11},
							End:   hcl.Pos{Line: 3, Column: 10, Byte: 14},
						},
					},
				},
			},
		},
		{
			`
b {
  a = foo
  b = bar
}
`,
			&BlockAttrsSpec{
				TypeName:    "b",
				ElementType: cty.String,
			},
			[]hcl.Traversal{
				{
					hcl.TraverseRoot{
						Name: "foo",
						SrcRange: hcl.Range{
							Start: hcl.Pos{Line: 3, Column: 7, Byte: 11},
							End:   hcl.Pos{Line: 3, Column: 10, Byte: 14},
						},
					},
				},
				{
					hcl.TraverseRoot{
						Name: "bar",
						SrcRange: hcl.Range{
							Start: hcl.Pos{Line: 4, Column: 7, Byte: 21},
							End:   hcl.Pos{Line: 4, Column: 10, Byte: 24},
						},
					},
				},
			},
		},
		{
			`
b {
  a = foo
}
b {
  a = bar
}
c {
  a = baz
}
`,
			&BlockListSpec{
				TypeName: "b",
				Nested: &AttrSpec{
					Name: "a",
				},
			},
			[]hcl.Traversal{
				{
					hcl.TraverseRoot{
						Name: "foo",
						SrcRange: hcl.Range{
							Start: hcl.Pos{Line: 3, Column: 7, Byte: 11},
							End:   hcl.Pos{Line: 3, Column: 10, Byte: 14},
						},
					},
				},
				{
					hcl.TraverseRoot{
						Name: "bar",
						SrcRange: hcl.Range{
							Start: hcl.Pos{Line: 6, Column: 7, Byte: 27},
							End:   hcl.Pos{Line: 6, Column: 10, Byte: 30},
						},
					},
				},
			},
		},
	}

	for i, test := range tests {
		t.Run(fmt.Sprintf("%02d-%s", i, test.config), func(t *testing.T) {
			file, diags := hclsyntax.ParseConfig([]byte(test.config), "", hcl.Pos{Line: 1, Column: 1, Byte: 0})
			if len(diags) != 0 {
				t.Errorf("wrong number of diagnostics from ParseConfig %d; want %d", len(diags), 0)
				for _, diag := range diags {
					t.Logf(" - %s", diag.Error())
				}
			}
			body := file.Body

			got := Variables(body, test.spec)

			if !reflect.DeepEqual(got, test.want) {
				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.want)
			}
		})
	}

}
