package json2go

import (
	"strings"
	"testing"
)

func TestTranspiler_Transpile_WithTags(t *testing.T) {
	tests := map[string]struct {
		v     Value
		name  string
		check func(t *testing.T, result string)
	}{
		"simple object": {
			name: "User",
			v: Value{
				Kind: ObjectValue,
				Object: []Field{
					{K: "name", V: Value{Kind: StringValue, Str: "John"}},
					{K: "age", V: Value{Kind: NumberValue, Int: 30}},
				},
			},
			check: func(t *testing.T, result string) {
				if !strings.Contains(result, "type User struct") {
					t.Errorf("missing User struct")
				}
				if !strings.Contains(result, "Name string `json:\"name\"`") {
					t.Errorf("missing Name field")
				}
				if !strings.Contains(result, "Age int `json:\"age\"`") {
					t.Errorf("missing Age field")
				}
			},
		},
		"nested object": {
			name: "Response",
			v: Value{
				Kind: ObjectValue,
				Object: []Field{{
					K: "user",
					V: Value{
						Kind: ObjectValue,
						Object: []Field{
							{K: "name", V: Value{Kind: StringValue, Str: "Alice"}},
							{K: "active", V: Value{Kind: BoolValue, Bool: true}},
						},
					},
				}},
			},
			check: func(t *testing.T, result string) {
				if !strings.Contains(result, "type Response struct") {
					t.Errorf("missing Response struct")
				}
				if !strings.Contains(result, "Name string `json:\"name\"`") {
					t.Errorf("missing Name field")
				}
				if !strings.Contains(result, "Active bool `json:\"active\"`") {
					t.Errorf("missing Active field")
				}
				if !strings.Contains(result, "User struct {") {
					t.Errorf("missing inline User struct")
				}
			},
		},
		"array of scalars": {
			name: "Tags",
			v: Value{
				Kind:  ArrayValue,
				Array: []Value{{Kind: StringValue, Str: "go"}},
			},
			check: func(t *testing.T, result string) {
				if result != "type Tags []string" {
					t.Errorf("expected scalar array type, got: %s", result)
				}
			},
		},
		"array of objects": {
			name: "Users",
			v: Value{
				Kind: ArrayValue,
				Array: []Value{{
					Kind: ObjectValue,
					Object: []Field{
						{K: "id", V: Value{Kind: NumberValue, Int: 1}},
						{K: "name", V: Value{Kind: StringValue, Str: "Bob"}},
					},
				}},
			},
			check: func(t *testing.T, result string) {
				if !strings.Contains(result, "type Users []struct {") {
					t.Errorf("missing Users array type with inline struct, got: %s", result)
				}
				if !strings.Contains(result, "Id int `json:\"id\"`") {
					t.Errorf("missing Id field")
				}
			},
		},
		"snake_case fields": {
			name: "Data",
			v: Value{
				Kind: ObjectValue,
				Object: []Field{
					{K: "first_name", V: Value{Kind: StringValue, Str: "Jane"}},
					{K: "last_name", V: Value{Kind: StringValue, Str: "Doe"}},
				},
			},
			check: func(t *testing.T, result string) {
				if !strings.Contains(result, "FirstName string `json:\"first_name\"`") {
					t.Errorf("missing FirstName field")
				}
				if !strings.Contains(result, "LastName string `json:\"last_name\"`") {
					t.Errorf("missing LastName field")
				}
			},
		},
	}
	for tname, tt := range tests {
		t.Run(tname, func(t *testing.T) {
			result, err := NewTranspiler().Transpile(tt.name, tt.v, true)
			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}
			tt.check(t, result)
		})
	}
}

func TestTranspiler_Transpile_WithoutTags(t *testing.T) {
	tests := map[string]struct {
		v     Value
		name  string
		check func(t *testing.T, result string)
	}{
		"simple object": {
			name: "User",
			v: Value{
				Kind: ObjectValue,
				Object: []Field{
					{K: "name", V: Value{Kind: StringValue, Str: "John"}},
					{K: "age", V: Value{Kind: NumberValue, Int: 30}},
				},
			},
			check: func(t *testing.T, result string) {
				if !strings.Contains(result, "type User struct") {
					t.Errorf("missing User struct")
				}
				if !strings.Contains(result, "Name string\n") {
					t.Errorf("missing Name field without tag")
				}
				if !strings.Contains(result, "Age int\n") {
					t.Errorf("missing Age field without tag")
				}
				if strings.Contains(result, "`json:") {
					t.Errorf("should not have json tags")
				}
			},
		},
		"nested object": {
			name: "Response",
			v: Value{
				Kind: ObjectValue,
				Object: []Field{{
					K: "user",
					V: Value{
						Kind: ObjectValue,
						Object: []Field{
							{K: "name", V: Value{Kind: StringValue, Str: "Alice"}},
							{K: "active", V: Value{Kind: BoolValue, Bool: true}},
						},
					},
				}},
			},
			check: func(t *testing.T, result string) {
				if !strings.Contains(result, "type Response struct") {
					t.Errorf("missing Response struct")
				}
				if !strings.Contains(result, "User struct {") {
					t.Errorf("missing inline User struct")
				}
				if !strings.Contains(result, "Name string\n") {
					t.Errorf("missing Name field")
				}
				if !strings.Contains(result, "Active bool\n") {
					t.Errorf("missing Active field")
				}
				if strings.Contains(result, "`json:") {
					t.Errorf("should not have json tags")
				}
			},
		},
		"snake_case fields preserved": {
			name: "Data",
			v: Value{
				Kind: ObjectValue,
				Object: []Field{
					{K: "first_name", V: Value{Kind: StringValue, Str: "Jane"}},
					{K: "last_name", V: Value{Kind: StringValue, Str: "Doe"}},
				},
			},
			check: func(t *testing.T, result string) {
				if !strings.Contains(result, "FirstName string\n") {
					t.Errorf("missing FirstName field")
				}
				if !strings.Contains(result, "LastName string\n") {
					t.Errorf("missing LastName field")
				}
			},
		},
	}
	for tname, tt := range tests {
		t.Run(tname, func(t *testing.T) {
			result, err := NewTranspiler().Transpile(tt.name, tt.v, false)
			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}
			tt.check(t, result)
		})
	}
}

func TestTranspiler_ParserIntegration(t *testing.T) {
	tests := map[string]struct {
		input       string
		structName  string
		includeTags bool
		expectTags  bool
	}{
		"parse and transpile with tags": {
			input:       `{"user_name": "alice", "user_age": 25}`,
			structName:  "Profile",
			includeTags: true,
			expectTags:  true,
		},
		"parse and transpile without tags": {
			input:       `{"user_name": "bob", "user_age": 30}`,
			structName:  "Account",
			includeTags: false,
			expectTags:  false,
		},
	}
	for tname, tt := range tests {
		t.Run(tname, func(t *testing.T) {
			lexer := NewLexer([]byte(tt.input))
			parser := NewParser(lexer)
			v, err := parser.Parse()
			if err != nil {
				t.Fatalf("parse error: %v", err)
			}

			result, err := NewTranspiler().Transpile(tt.structName, v, tt.includeTags)
			if err != nil {
				t.Fatalf("transpile error: %v", err)
			}

			hasTag := strings.Contains(result, "`json:")
			if tt.expectTags != hasTag {
				t.Errorf("expected tags=%v, got=%v\n%s", tt.expectTags, hasTag, result)
			}

			if !strings.Contains(result, "type "+tt.structName) {
				t.Errorf("struct name %q not found in output", tt.structName)
			}
		})
	}
}
