package commands

import (
	"strings"
	"testing"

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

func TestShiftLCommand_WillHandle(t *testing.T) {
	tests := []struct {
		name string
		text string
		want bool
	}{
		// Old syntax
		{"old syntax BY/GIVING", "SHIFTL a BY b GIVING c", true},
		{"old syntax <</->", "SHIFTL a << b -> c", true},
		{"old syntax mixed case", "shiftl x by y giving z", true},

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

		// Should not handle
		{"not shiftl - shiftr", "SHIFTR a BY b GIVING c", false},
		{"not shiftl - add", "result = a + b", false},
		{"not shiftl - wrong params", "SHIFTL a b c", false},
		{"empty", "", false},
		{"just SHIFTL", "SHIFTL", false},
		{"assignment without shift", "x = y", false},
	}

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

func TestShiftLCommand_Interpret_OldSyntax(t *testing.T) {
	tests := []struct {
		name    string
		setup   func(*compiler.CompilerContext)
		text    string
		wantErr bool
		check   func(*testing.T, *ShiftLCommand)
	}{
		{
			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, 3, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "SHIFTL a BY b GIVING c",
			wantErr: false,
			check: func(t *testing.T, cmd *ShiftLCommand) {
				if !cmd.sourceIsVar || cmd.sourceVarName != "a" {
					t.Errorf("source should be var 'a'")
				}
				if !cmd.amountIsVar || cmd.amountVarName != "b" {
					t.Errorf("amount should be var 'b'")
				}
				if cmd.destVarName != "c" || cmd.destVarKind != compiler.KindByte {
					t.Errorf("dest should be byte 'c'")
				}
			},
		},
		{
			name: "word << byte -> word",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("x", "", compiler.KindWord, 1000, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("n", "", compiler.KindByte, 2, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "SHIFTL x BY n GIVING result",
			wantErr: false,
			check: func(t *testing.T, cmd *ShiftLCommand) {
				if cmd.sourceVarKind != compiler.KindWord {
					t.Errorf("source should be word")
				}
				if cmd.destVarKind != compiler.KindWord {
					t.Errorf("dest should be word")
				}
			},
		},
		{
			name: "literal << var -> byte",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("shift", "", compiler.KindByte, 2, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "SHIFTL $FF BY shift GIVING result",
			wantErr: false,
			check: func(t *testing.T, cmd *ShiftLCommand) {
				if cmd.sourceIsVar {
					t.Errorf("source should be literal")
				}
				if cmd.sourceValue != 0xFF {
					t.Errorf("source value = %d, want 255", cmd.sourceValue)
				}
			},
		},
		{
			name: "var << literal -> byte",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("value", "", compiler.KindByte, 1, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "SHIFTL value BY 3 GIVING result",
			wantErr: false,
			check: func(t *testing.T, cmd *ShiftLCommand) {
				if cmd.amountIsVar {
					t.Errorf("amount should be literal")
				}
				if cmd.amountValue != 3 {
					t.Errorf("amount value = %d, want 3", cmd.amountValue)
				}
			},
		},
		{
			name: "arrow 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, 2, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "SHIFTL a << b -> c",
			wantErr: false,
			check: func(t *testing.T, cmd *ShiftLCommand) {
				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:    "SHIFTL a BY 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, 2, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 255, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "SHIFTL a BY b GIVING MAX",
			wantErr: true,
		},
		{
			name: "word amount variable (error)",
			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, 2, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "SHIFTL a BY b GIVING c",
			wantErr: true,
		},
		{
			name: "amount constant > 255 (error)",
			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:    "SHIFTL a BY 300 GIVING c",
			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, 2, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "SHIFTL a WITH 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, 2, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "SHIFTL a BY b INTO 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 := &ShiftLCommand{}
			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 TestShiftLCommand_Interpret_NewSyntax(t *testing.T) {
	tests := []struct {
		name    string
		setup   func(*compiler.CompilerContext)
		text    string
		wantErr bool
		check   func(*testing.T, *ShiftLCommand)
	}{
		{
			name: "dest = var << var",
			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, 2, 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 *ShiftLCommand) {
				if cmd.destVarName != "result" {
					t.Errorf("dest = %q, want 'result'", cmd.destVarName)
				}
				if !cmd.sourceIsVar || cmd.sourceVarName != "a" {
					t.Errorf("source should be var 'a'")
				}
				if !cmd.amountIsVar || cmd.amountVarName != "b" {
					t.Errorf("amount should be var 'b'")
				}
			},
		},
		{
			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 = $01 << 3",
			wantErr: false,
			check: func(t *testing.T, cmd *ShiftLCommand) {
				if cmd.sourceIsVar || cmd.amountIsVar {
					t.Errorf("both params should be literals")
				}
				if cmd.sourceValue != 1 || cmd.amountValue != 3 {
					t.Errorf("source=%d, amount=%d, want 1,3", cmd.sourceValue, cmd.amountValue)
				}
			},
		},
		{
			name: "word destination",
			setup: func(ctx *compiler.CompilerContext) {
				ctx.SymbolTable.AddVar("value", "", compiler.KindWord, 1000, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("shift", "", compiler.KindByte, 1, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "result = value << shift",
			wantErr: false,
			check: func(t *testing.T, cmd *ShiftLCommand) {
				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, 2, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			text:    "result = 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, 2, 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 := &ShiftLCommand{}
			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 TestShiftLCommand_Generate(t *testing.T) {
	tests := []struct {
		name      string
		setup     func(*compiler.CompilerContext) *ShiftLCommand
		wantLines []string
	}{
		{
			name: "constant folding - byte << 0",
			setup: func(ctx *compiler.CompilerContext) *ShiftLCommand {
				ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
				return &ShiftLCommand{
					sourceIsVar:   false,
					sourceValue:   0x55,
					amountIsVar:   false,
					amountValue:   0,
					destVarName:   "result",
					destVarKind:   compiler.KindByte,
				}
			},
			wantLines: []string{
				"\tlda #$55",
				"\tsta result",
			},
		},
		{
			name: "constant folding - byte << 3",
			setup: func(ctx *compiler.CompilerContext) *ShiftLCommand {
				ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
				return &ShiftLCommand{
					sourceIsVar:   false,
					sourceValue:   0x01,
					amountIsVar:   false,
					amountValue:   3,
					destVarName:   "result",
					destVarKind:   compiler.KindByte,
				}
			},
			wantLines: []string{
				"\tlda #$01",
				"\tasl",
				"\tasl",
				"\tasl",
				"\tsta result",
			},
		},
		{
			name: "constant folding - byte << 8 (zero)",
			setup: func(ctx *compiler.CompilerContext) *ShiftLCommand {
				ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
				return &ShiftLCommand{
					sourceIsVar:   false,
					sourceValue:   0xFF,
					amountIsVar:   false,
					amountValue:   8,
					destVarName:   "result",
					destVarKind:   compiler.KindByte,
				}
			},
			wantLines: []string{
				"\tlda #0",
				"\tsta result",
			},
		},
		{
			name: "constant folding - word << 1",
			setup: func(ctx *compiler.CompilerContext) *ShiftLCommand {
				ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
				return &ShiftLCommand{
					sourceIsVar:   false,
					sourceValue:   0x1234,
					amountIsVar:   false,
					amountValue:   1,
					destVarName:   "result",
					destVarKind:   compiler.KindWord,
				}
			},
			wantLines: []string{
				"\tlda #$34",
				"\tsta result",
				"\tlda #$12",
				"\tsta result+1",
				"\tasl result",
				"\trol result+1",
			},
		},
		{
			name: "constant folding - word << 16 (zero)",
			setup: func(ctx *compiler.CompilerContext) *ShiftLCommand {
				ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
				return &ShiftLCommand{
					sourceIsVar:   false,
					sourceValue:   0xFFFF,
					amountIsVar:   false,
					amountValue:   16,
					destVarName:   "result",
					destVarKind:   compiler.KindWord,
				}
			},
			wantLines: []string{
				"\tlda #0",
				"\tsta result",
				"\tsta result+1",
			},
		},
		{
			name: "byte variable << byte variable",
			setup: func(ctx *compiler.CompilerContext) *ShiftLCommand {
				ctx.SymbolTable.AddVar("value", "", compiler.KindByte, 10, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("shift", "", compiler.KindByte, 2, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
				return &ShiftLCommand{
					sourceIsVar:   true,
					sourceVarName: "value",
					sourceVarKind: compiler.KindByte,
					amountIsVar:   true,
					amountVarName: "shift",
					amountVarKind: compiler.KindByte,
					destVarName:   "result",
					destVarKind:   compiler.KindByte,
				}
			},
			wantLines: []string{
				"\tlda value",
				"\tldx shift",
				"\tbeq _L2",
				"_L1",
				"\tasl",
				"\tdex",
				"\tbne _L1",
				"_L2",
				"\tsta result",
			},
		},
		{
			name: "word variable << byte variable",
			setup: func(ctx *compiler.CompilerContext) *ShiftLCommand {
				ctx.SymbolTable.AddVar("value", "", compiler.KindWord, 1000, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("shift", "", compiler.KindByte, 3, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
				return &ShiftLCommand{
					sourceIsVar:   true,
					sourceVarName: "value",
					sourceVarKind: compiler.KindWord,
					amountIsVar:   true,
					amountVarName: "shift",
					amountVarKind: compiler.KindByte,
					destVarName:   "result",
					destVarKind:   compiler.KindWord,
				}
			},
			wantLines: []string{
				"\tlda value",
				"\tsta result",
				"\tlda value+1",
				"\tsta result+1",
				"\tldx shift",
				"\tbeq _L2",
				"_L1",
				"\tasl result",
				"\trol result+1",
				"\tdex",
				"\tbne _L1",
				"_L2",
			},
		},
		{
			name: "same source and dest",
			setup: func(ctx *compiler.CompilerContext) *ShiftLCommand {
				ctx.SymbolTable.AddVar("value", "", compiler.KindByte, 10, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("shift", "", compiler.KindByte, 2, preproc.Line{Filename: "test.c65", LineNo: 1})
				return &ShiftLCommand{
					sourceIsVar:   true,
					sourceVarName: "value",
					sourceVarKind: compiler.KindByte,
					amountIsVar:   true,
					amountVarName: "shift",
					amountVarKind: compiler.KindByte,
					destVarName:   "value",
					destVarKind:   compiler.KindByte,
				}
			},
			wantLines: []string{
				"\tlda value",
				"\tldx shift",
				"\tbeq _L2",
				"_L1",
				"\tasl",
				"\tdex",
				"\tbne _L1",
				"_L2",
				"\tsta value",
			},
		},
		{
			name: "WORD optimization - shift by 8",
			setup: func(ctx *compiler.CompilerContext) *ShiftLCommand {
				ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
				return &ShiftLCommand{
					sourceIsVar:   false,
					sourceValue:   0x1234,
					amountIsVar:   false,
					amountValue:   8,
					destVarName:   "result",
					destVarKind:   compiler.KindWord,
				}
			},
			wantLines: []string{
				"\tlda #$34",
				"\tsta result+1",
				"\tlda #0",
				"\tsta result",
			},
		},
		{
			name: "WORD optimization - shift by 12 (8 + 4)",
			setup: func(ctx *compiler.CompilerContext) *ShiftLCommand {
				ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
				return &ShiftLCommand{
					sourceIsVar:   false,
					sourceValue:   0x00AB,
					amountIsVar:   false,
					amountValue:   12,
					destVarName:   "result",
					destVarKind:   compiler.KindWord,
				}
			},
			wantLines: []string{
				"\tlda #$ab",
				"\tsta result+1",
				"\tlda #0",
				"\tsta result",
				"\tasl result+1",
				"\tasl result+1",
				"\tasl result+1",
				"\tasl result+1",
			},
		},
		{
			name: "BYTE to WORD conversion with shift",
			setup: func(ctx *compiler.CompilerContext) *ShiftLCommand {
				ctx.SymbolTable.AddVar("byteval", "", compiler.KindByte, 0x55, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
				return &ShiftLCommand{
					sourceIsVar:   true,
					sourceVarName: "byteval",
					sourceVarKind: compiler.KindByte,
					amountIsVar:   false,
					amountValue:   4,
					destVarName:   "result",
					destVarKind:   compiler.KindWord,
				}
			},
			wantLines: []string{
				"\tlda byteval",
				"\tsta result",
				"\tlda #0",
				"\tsta result+1",
				"\tasl result",
				"\trol result+1",
				"\tasl result",
				"\trol result+1",
				"\tasl result",
				"\trol result+1",
				"\tasl result",
				"\trol result+1",
			},
		},
		{
			name: "WORD to BYTE conversion with shift (truncation)",
			setup: func(ctx *compiler.CompilerContext) *ShiftLCommand {
				ctx.SymbolTable.AddVar("wordval", "", compiler.KindWord, 0x1234, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
				return &ShiftLCommand{
					sourceIsVar:   true,
					sourceVarName: "wordval",
					sourceVarKind: compiler.KindWord,
					amountIsVar:   false,
					amountValue:   4,
					destVarName:   "result",
					destVarKind:   compiler.KindByte,
				}
			},
			wantLines: []string{
				"\tlda wordval",
				"\tasl",
				"\tasl",
				"\tasl",
				"\tasl",
				"\tsta result",
			},
		},
		{
			name: "WORD same source and destination",
			setup: func(ctx *compiler.CompilerContext) *ShiftLCommand {
				ctx.SymbolTable.AddVar("value", "", compiler.KindWord, 0x1234, preproc.Line{Filename: "test.c65", LineNo: 1})
				ctx.SymbolTable.AddVar("shift", "", compiler.KindByte, 2, preproc.Line{Filename: "test.c65", LineNo: 1})
				return &ShiftLCommand{
					sourceIsVar:   true,
					sourceVarName: "value",
					sourceVarKind: compiler.KindWord,
					amountIsVar:   true,
					amountVarName: "shift",
					amountVarKind: compiler.KindByte,
					destVarName:   "value",
					destVarKind:   compiler.KindWord,
				}
			},
			wantLines: []string{
				"\tldx shift",
				"\tbeq _L2",
				"_L1",
				"\tasl value",
				"\trol value+1",
				"\tdex",
				"\tbne _L1",
				"_L2",
			},
		},
	}

	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 {
				// Skip exact label comparison (they're generated dynamically)
				if strings.HasPrefix(got[i], "_L") && strings.HasPrefix(tt.wantLines[i], "_L") {
					continue
				}
				if got[i] != tt.wantLines[i] {
					t.Errorf("Line %d:\ngot:  %q\nwant: %q", i, got[i], tt.wantLines[i])
				}
			}
		})
	}
}