package commands

import (
	"strings"
	"testing"

	"c65gm/internal/compiler"
	"c65gm/internal/preproc"
)

func TestAddCommand_WillHandle(t *testing.T) {
	tests := []struct {
		name string
		text string
		want bool
	}{
		// Old syntax
		{"old syntax with TO/GIVING", "ADD a TO b GIVING c", true},
		{"old syntax with +/->", "ADD a + b -> c", true},
		{"old syntax mixed case", "add x to y giving z", true},

		// New syntax
		{"new syntax basic", "result = a + b", true},
		{"new syntax with literals", "x = 10 + 20", true},

		// Should not handle
		{"not add - subtract", "result = a - b", false},
		{"not add - multiply", "result = a * b", false},
		{"not add - wrong params", "ADD a b c", false},
		{"empty", "", false},
		{"just ADD", "ADD", false},
		{"assignment without add", "x = y", false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cmd := &AddCommand{}
			line := preproc.Line{Text: tt.text}
			if got := cmd.WillHandle(line); got != tt.want {
				t.Errorf("WillHandle() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestAddCommand_Interpret_OldSyntax(t *testing.T) {
	tests := []struct {
		name    string
		setup   func(*compiler.CompilerContext)
		text    string
		wantErr bool
		check   func(*testing.T, *AddCommand)
	}{
		{
			name: "byte + byte -> byte",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "ADD a TO b GIVING c",
			wantErr: false,
			check: func(t *testing.T, cmd *AddCommand) {
				if !cmd.param1IsVar || cmd.param1VarName != "a" {
					t.Errorf("param1 should be var 'a'")
				}
				if !cmd.param2IsVar || cmd.param2VarName != "b" {
					t.Errorf("param2 should be var 'b'")
				}
				if cmd.destVarName != "c" || cmd.destVarKind != compiler.KindByte {
					t.Errorf("dest should be byte 'c'")
				}
			},
		},
		{
			name: "word + word -> word",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("x", "", compiler.KindWord, 1000, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("y", "", compiler.KindWord, 2000, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("z", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "ADD x TO y GIVING z",
			wantErr: false,
			check: func(t *testing.T, cmd *AddCommand) {
				if cmd.destVarKind != compiler.KindWord {
					t.Errorf("dest should be word")
				}
			},
		},
		{
			name: "byte + word -> word",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("b", "", compiler.KindWord, 1000, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("c", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "ADD a TO b GIVING c",
			wantErr: false,
			check: func(t *testing.T, cmd *AddCommand) {
				if cmd.param1VarKind != compiler.KindByte {
					t.Errorf("param1 should be byte")
				}
				if cmd.param2VarKind != compiler.KindWord {
					t.Errorf("param2 should be word")
				}
				if cmd.destVarKind != compiler.KindWord {
					t.Errorf("dest should be word")
				}
			},
		},
		{
			name: "literal + var -> var",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "ADD 10 TO b GIVING c",
			wantErr: false,
			check: func(t *testing.T, cmd *AddCommand) {
				if cmd.param1IsVar {
					t.Errorf("param1 should be literal")
				}
				if cmd.param1Value != 10 {
					t.Errorf("param1 value = %d, want 10", cmd.param1Value)
				}
			},
		},
		{
			name: "var + literal -> var",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "ADD a TO 20 GIVING c",
			wantErr: false,
			check: func(t *testing.T, cmd *AddCommand) {
				if cmd.param2IsVar {
					t.Errorf("param2 should be literal")
				}
				if cmd.param2Value != 20 {
					t.Errorf("param2 value = %d, want 20", cmd.param2Value)
				}
			},
		},
		{
			name: "literal + literal -> var",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "ADD 15 TO 25 GIVING c",
			wantErr: false,
			check: func(t *testing.T, cmd *AddCommand) {
				if cmd.param1IsVar || cmd.param2IsVar {
					t.Errorf("both params should be literals")
				}
				if cmd.param1Value != 15 || cmd.param2Value != 25 {
					t.Errorf("param values = %d, %d, want 15, 25", cmd.param1Value, cmd.param2Value)
				}
			},
		},
		{
			name: "hex literal",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "ADD $10 TO $20 GIVING c",
			wantErr: false,
			check: func(t *testing.T, cmd *AddCommand) {
				if cmd.param1Value != 0x10 || cmd.param2Value != 0x20 {
					t.Errorf("param values = %d, %d, want 16, 32", cmd.param1Value, cmd.param2Value)
				}
			},
		},
		{
			name: "constant usage",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 100, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "ADD MAX TO a GIVING c",
			wantErr: false,
			check: func(t *testing.T, cmd *AddCommand) {
				if cmd.param1IsVar {
					t.Errorf("param1 should be literal (constant inlined), got isVar=true")
				}
				if cmd.param1Value != 100 {
					t.Errorf("param1 value = %d, want 100", cmd.param1Value)
				}
			},
		},
		{
			name: "alternative syntax +/->",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "ADD a + b -> c",
			wantErr: false,
			check: func(t *testing.T, cmd *AddCommand) {
				if cmd.destVarName != "c" {
					t.Errorf("dest should be c")
				}
			},
		},
		{
			name: "unknown variable",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "ADD a TO b GIVING c",
			wantErr: true,
		},
		{
			name: "assign to constant",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 100, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "ADD a TO b GIVING MAX",
			wantErr: true,
		},
		{
			name: "wrong parameter count",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "ADD a TO b",
			wantErr: true,
		},
		{
			name: "wrong separator #3",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "ADD a AND b GIVING c",
			wantErr: true,
		},
		{
			name: "wrong separator #5",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "ADD a TO b INTO c",
			wantErr: true,
		},
		{
			name: "invalid expression",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "ADD @#$% TO 10 GIVING c",
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ctx := compiler.NewCompilerContext(preproc.NewPragma())
			if tt.setup != nil {
				tt.setup(ctx)
			}

			cmd := &AddCommand{}
			line := preproc.Line{Text: tt.text}

			err := cmd.Interpret(line, ctx)

			if (err != nil) != tt.wantErr {
				t.Errorf("Interpret() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			if !tt.wantErr && tt.check != nil {
				tt.check(t, cmd)
			}
		})
	}
}

func TestAddCommand_Interpret_NewSyntax(t *testing.T) {
	tests := []struct {
		name    string
		setup   func(*compiler.CompilerContext)
		text    string
		wantErr bool
		check   func(*testing.T, *AddCommand)
	}{
		{
			name: "dest = var1 + var2",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "result = a + b",
			wantErr: false,
			check: func(t *testing.T, cmd *AddCommand) {
				if cmd.destVarName != "result" {
					t.Errorf("dest = %q, want 'result'", cmd.destVarName)
				}
				if !cmd.param1IsVar || cmd.param1VarName != "a" {
					t.Errorf("param1 should be var 'a'")
				}
				if !cmd.param2IsVar || cmd.param2VarName != "b" {
					t.Errorf("param2 should be var 'b'")
				}
			},
		},
		{
			name: "dest = literal + var",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 5, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "result = 100 + x",
			wantErr: false,
			check: func(t *testing.T, cmd *AddCommand) {
				if cmd.param1IsVar {
					t.Errorf("param1 should be literal")
				}
				if cmd.param1Value != 100 {
					t.Errorf("param1 = %d, want 100", cmd.param1Value)
				}
			},
		},
		{
			name: "dest = var + literal",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 5, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "result = x + 50",
			wantErr: false,
			check: func(t *testing.T, cmd *AddCommand) {
				if cmd.param2IsVar {
					t.Errorf("param2 should be literal")
				}
				if cmd.param2Value != 50 {
					t.Errorf("param2 = %d, want 50", cmd.param2Value)
				}
			},
		},
		{
			name: "dest = literal + literal",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "result = 30 + 70",
			wantErr: false,
			check: func(t *testing.T, cmd *AddCommand) {
				if cmd.param1IsVar || cmd.param2IsVar {
					t.Errorf("both params should be literals")
				}
			},
		},
		{
			name: "word destination",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("a", "", compiler.KindWord, 1000, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("b", "", compiler.KindWord, 2000, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "result = a + b",
			wantErr: false,
			check: func(t *testing.T, cmd *AddCommand) {
				if cmd.destVarKind != compiler.KindWord {
					t.Errorf("dest should be word")
				}
			},
		},
		{
			name: "unknown dest variable",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "result = a + b",
			wantErr: true,
		},
		{
			name: "assign to constant",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 100, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "MAX = a + b",
			wantErr: true,
		},
		{
			name: "wrong operator (not +)",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "result = a - b",
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ctx := compiler.NewCompilerContext(preproc.NewPragma())
			if tt.setup != nil {
				tt.setup(ctx)
			}

			cmd := &AddCommand{}
			line := preproc.Line{Text: tt.text}

			err := cmd.Interpret(line, ctx)

			if (err != nil) != tt.wantErr {
				t.Errorf("Interpret() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			if !tt.wantErr && tt.check != nil {
				tt.check(t, cmd)
			}
		})
	}
}

func TestAddCommand_Generate(t *testing.T) {
	tests := []struct {
		name      string
		setup     func(*compiler.CompilerContext) *AddCommand
		wantLines []string
	}{
		{
			name: "constant folding - both literals to byte",
			setup: func(ctx *compiler.CompilerContext) *AddCommand {
				ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
				return &AddCommand{
					param1IsVar: false,
					param1Value: 10,
					param2IsVar: false,
					param2Value: 20,
					destVarName: "result",
					destVarKind: compiler.KindByte,
				}
			},
			wantLines: []string{
				"\tlda #$1e",
				"\tsta result",
			},
		},
		{
			name: "constant folding - both literals to word",
			setup: func(ctx *compiler.CompilerContext) *AddCommand {
				ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
				return &AddCommand{
					param1IsVar: false,
					param1Value: 100,
					param2IsVar: false,
					param2Value: 200,
					destVarName: "result",
					destVarKind: compiler.KindWord,
				}
			},
			wantLines: []string{
				"\tlda #$2c",
				"\tsta result",
				"\tlda #$01",
				"\tsta result+1",
			},
		},
		{
			name: "constant folding with overflow",
			setup: func(ctx *compiler.CompilerContext) *AddCommand {
				ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
				return &AddCommand{
					param1IsVar: false,
					param1Value: 200,
					param2IsVar: false,
					param2Value: 100,
					destVarName: "result",
					destVarKind: compiler.KindWord,
				}
			},
			wantLines: []string{
				"\tlda #$2c",
				"\tsta result",
				"\tlda #$01",
				"\tsta result+1",
			},
		},
		{
			name: "byte + byte -> byte",
			setup: func(ctx *compiler.CompilerContext) *AddCommand {
				ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
				return &AddCommand{
					param1IsVar:   true,
					param1VarName: "a",
					param1VarKind: compiler.KindByte,
					param2IsVar:   true,
					param2VarName: "b",
					param2VarKind: compiler.KindByte,
					destVarName:   "result",
					destVarKind:   compiler.KindByte,
				}
			},
			wantLines: []string{
				"\tclc",
				"\tlda a",
				"\tadc b",
				"\tsta result",
			},
		},
		{
			name: "word + word -> word",
			setup: func(ctx *compiler.CompilerContext) *AddCommand {
				ctx.SymbolTable.AddVar("x", "", compiler.KindWord, 1000, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("y", "", compiler.KindWord, 2000, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("z", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
				return &AddCommand{
					param1IsVar:   true,
					param1VarName: "x",
					param1VarKind: compiler.KindWord,
					param2IsVar:   true,
					param2VarName: "y",
					param2VarKind: compiler.KindWord,
					destVarName:   "z",
					destVarKind:   compiler.KindWord,
				}
			},
			wantLines: []string{
				"\tclc",
				"\tlda x",
				"\tadc y",
				"\tsta z",
				"\tlda x+1",
				"\tadc y+1",
				"\tsta z+1",
			},
		},
		{
			name: "byte + word -> word",
			setup: func(ctx *compiler.CompilerContext) *AddCommand {
				ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("w", "", compiler.KindWord, 1000, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
				return &AddCommand{
					param1IsVar:   true,
					param1VarName: "a",
					param1VarKind: compiler.KindByte,
					param2IsVar:   true,
					param2VarName: "w",
					param2VarKind: compiler.KindWord,
					destVarName:   "result",
					destVarKind:   compiler.KindWord,
				}
			},
			wantLines: []string{
				"\tclc",
				"\tlda a",
				"\tadc w",
				"\tsta result",
				"\tlda #0",
				"\tadc w+1",
				"\tsta result+1",
			},
		},
		{
			name: "literal + var -> byte",
			setup: func(ctx *compiler.CompilerContext) *AddCommand {
				ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
				return &AddCommand{
					param1IsVar:   false,
					param1Value:   5,
					param2IsVar:   true,
					param2VarName: "a",
					param2VarKind: compiler.KindByte,
					destVarName:   "result",
					destVarKind:   compiler.KindByte,
				}
			},
			wantLines: []string{
				"\tclc",
				"\tlda #$05",
				"\tadc a",
				"\tsta result",
			},
		},
		{
			name: "var + literal -> word",
			setup: func(ctx *compiler.CompilerContext) *AddCommand {
				ctx.SymbolTable.AddVar("w", "", compiler.KindWord, 1000, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
				return &AddCommand{
					param1IsVar:   true,
					param1VarName: "w",
					param1VarKind: compiler.KindWord,
					param2IsVar:   false,
					param2Value:   300,
					destVarName:   "result",
					destVarKind:   compiler.KindWord,
				}
			},
			wantLines: []string{
				"\tclc",
				"\tlda w",
				"\tadc #$2c",
				"\tsta result",
				"\tlda w+1",
				"\tadc #$01",
				"\tsta result+1",
			},
		},
		{
			name: "byte + byte -> word (promotion)",
			setup: func(ctx *compiler.CompilerContext) *AddCommand {
				ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
				return &AddCommand{
					param1IsVar:   true,
					param1VarName: "a",
					param1VarKind: compiler.KindByte,
					param2IsVar:   true,
					param2VarName: "b",
					param2VarKind: compiler.KindByte,
					destVarName:   "result",
					destVarKind:   compiler.KindWord,
				}
			},
			wantLines: []string{
				"\tclc",
				"\tlda a",
				"\tadc b",
				"\tsta result",
				"\tlda #0",
				"\tadc #0",
				"\tsta result+1",
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ctx := compiler.NewCompilerContext(preproc.NewPragma())
			cmd := tt.setup(ctx)

			got, err := cmd.Generate(ctx)
			if err != nil {
				t.Fatalf("Generate() error = %v", err)
			}

			if len(got) != len(tt.wantLines) {
				t.Errorf("Generate() got %d lines, want %d lines\nGot:\n%s\nWant:\n%s",
					len(got), len(tt.wantLines),
					strings.Join(got, "\n"), strings.Join(tt.wantLines, "\n"))
				return
			}

			for i := range got {
				if got[i] != tt.wantLines[i] {
					t.Errorf("Line %d:\ngot:  %q\nwant: %q", i, got[i], tt.wantLines[i])
				}
			}
		})
	}
}

func TestAddCommand_Scopes(t *testing.T) {
	ctx := compiler.NewCompilerContext(preproc.NewPragma())

	// Global variables
	ctx.SymbolTable.AddVar("globalA", "", compiler.KindByte, 10, preproc.Line{Filename: "test.c65", LineNo: 1})
	ctx.SymbolTable.AddVar("globalB", "", compiler.KindByte, 20, preproc.Line{Filename: "test.c65", LineNo: 1})
	ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})

	// Simulate function declaration to enter scope
	line := preproc.Line{Text: "FUNC myFunc"}
	_, err := ctx.FunctionHandler.HandleFuncDecl(line)
	if err != nil {
		t.Fatalf("HandleFuncDecl() error = %v", err)
	}

	// Function scope variables
	ctx.SymbolTable.AddVar("localA", "myFunc", compiler.KindByte, 5, preproc.Line{Filename: "test.c65", LineNo: 1})
	ctx.SymbolTable.AddVar("localB", "myFunc", compiler.KindByte, 15, preproc.Line{Filename: "test.c65", LineNo: 1})

	// Test local variables
	cmd := &AddCommand{}
	cmdLine := preproc.Line{Text: "result = localA + localB"}
	if err := cmd.Interpret(cmdLine, ctx); err != nil {
		t.Fatalf("Interpret() error = %v", err)
	}

	if cmd.param1VarName != "myFunc_localA" {
		t.Errorf("param1 = %q, want 'myFunc_localA'", cmd.param1VarName)
	}
	if cmd.param2VarName != "myFunc_localB" {
		t.Errorf("param2 = %q, want 'myFunc_localB'", cmd.param2VarName)
	}

	// Test global variables while in function scope
	cmd2 := &AddCommand{}
	cmdLine2 := preproc.Line{Text: "result = globalA + globalB"}
	if err := cmd2.Interpret(cmdLine2, ctx); err != nil {
		t.Fatalf("Interpret() error = %v", err)
	}

	if cmd2.param1VarName != "globalA" {
		t.Errorf("param1 = %q, want 'globalA'", cmd2.param1VarName)
	}
}
