package commands

import (
	"strings"
	"testing"

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

func TestSwitchBasicByte(t *testing.T) {
	tests := []struct {
		name         string
		setupVars    func(*compiler.SymbolTable)
		caseValue    string
		wantSwitch   []string
		wantCase     []string
		wantEndswitch []string
	}{
		{
			name: "byte var with byte literal case",
			setupVars: func(st *compiler.SymbolTable) {
				st.AddVar("x", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			},
			caseValue: "10",
			wantSwitch: []string{},
			wantCase: []string{
				"\tlda x",
				"\tcmp #$0a",
				"\tbne ",
			},
			wantEndswitch: []string{},
		},
	}

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

			switchCmd := &SwitchCommand{}
			caseCmd := &CaseCommand{}
			endswitchCmd := &EndSwitchCommand{}

			pragmaIdx := pragma.GetCurrentPragmaSetIndex()

			if err := switchCmd.Interpret(preproc.Line{Text: "SWITCH x", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
				t.Fatalf("SWITCH Interpret() error = %v", err)
			}

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

			if err := caseCmd.Interpret(preproc.Line{Text: "CASE " + tt.caseValue, Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
				t.Fatalf("CASE Interpret() error = %v", err)
			}

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

			if err := endswitchCmd.Interpret(preproc.Line{Text: "ENDSWITCH", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
				t.Fatalf("ENDSWITCH Interpret() error = %v", err)
			}

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

			if !equalAsmSwitch(switchAsm, tt.wantSwitch) {
				t.Errorf("SWITCH Generate() mismatch\ngot:\n%s\nwant:\n%s",
					strings.Join(switchAsm, "\n"),
					strings.Join(tt.wantSwitch, "\n"))
			}

			// For CASE, check that expected instructions are present
			if !containsInstructions(caseAsm, tt.wantCase) {
				t.Errorf("CASE Generate() missing expected instructions\ngot:\n%s\nwant to contain:\n%s",
					strings.Join(caseAsm, "\n"),
					strings.Join(tt.wantCase, "\n"))
			}

			// ENDSWITCH should emit at least one label
			if len(endswitchAsm) == 0 {
				t.Error("ENDSWITCH should generate at least end label")
			}
		})
	}
}

func TestSwitchMultipleCases(t *testing.T) {
	pragma := preproc.NewPragma()
	ctx := compiler.NewCompilerContext(pragma)
	ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})

	pragmaIdx := pragma.GetCurrentPragmaSetIndex()

	switchCmd := &SwitchCommand{}
	case1Cmd := &CaseCommand{}
	case2Cmd := &CaseCommand{}
	case3Cmd := &CaseCommand{}
	endswitchCmd := &EndSwitchCommand{}

	if err := switchCmd.Interpret(preproc.Line{Text: "SWITCH x", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("SWITCH error = %v", err)
	}
	switchCmd.Generate(ctx)

	if err := case1Cmd.Interpret(preproc.Line{Text: "CASE 1", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("CASE 1 error = %v", err)
	}
	case1Asm, _ := case1Cmd.Generate(ctx)

	if err := case2Cmd.Interpret(preproc.Line{Text: "CASE 2", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("CASE 2 error = %v", err)
	}
	case2Asm, _ := case2Cmd.Generate(ctx)

	if err := case3Cmd.Interpret(preproc.Line{Text: "CASE 3", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("CASE 3 error = %v", err)
	}
	_, _ = case3Cmd.Generate(ctx)

	if err := endswitchCmd.Interpret(preproc.Line{Text: "ENDSWITCH", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("ENDSWITCH error = %v", err)
	}
	endswitchAsm, _ := endswitchCmd.Generate(ctx)

	// First CASE should not have JMP at the beginning
	if len(case1Asm) > 0 && strings.Contains(case1Asm[0], "jmp") {
		t.Error("First CASE should not start with JMP")
	}

	// Second CASE should have implicit break (JMP) from previous case
	foundJmp := false
	for _, line := range case2Asm {
		if strings.Contains(line, "jmp") {
			foundJmp = true
			break
		}
	}
	if !foundJmp {
		t.Error("Second CASE should have implicit break JMP from previous case")
	}

	// ENDSWITCH should have end label
	if len(endswitchAsm) == 0 {
		t.Error("ENDSWITCH should have end label")
	}
}

func TestSwitchWithDefault(t *testing.T) {
	pragma := preproc.NewPragma()
	ctx := compiler.NewCompilerContext(pragma)
	ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})

	pragmaIdx := pragma.GetCurrentPragmaSetIndex()

	switchCmd := &SwitchCommand{}
	case1Cmd := &CaseCommand{}
	defaultCmd := &DefaultCommand{}
	endswitchCmd := &EndSwitchCommand{}

	if err := switchCmd.Interpret(preproc.Line{Text: "SWITCH x", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("SWITCH error = %v", err)
	}
	switchCmd.Generate(ctx)

	if err := case1Cmd.Interpret(preproc.Line{Text: "CASE 1", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("CASE error = %v", err)
	}
	case1Cmd.Generate(ctx)

	if err := defaultCmd.Interpret(preproc.Line{Text: "DEFAULT", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("DEFAULT error = %v", err)
	}
	defaultAsm, err := defaultCmd.Generate(ctx)
	if err != nil {
		t.Fatalf("DEFAULT Generate() error = %v", err)
	}

	if err := endswitchCmd.Interpret(preproc.Line{Text: "ENDSWITCH", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("ENDSWITCH error = %v", err)
	}
	endswitchCmd.Generate(ctx)

	// DEFAULT should emit implicit break and skip label
	if len(defaultAsm) == 0 {
		t.Error("DEFAULT should emit code for implicit break")
	}
}

func TestSwitchCaseAfterDefault(t *testing.T) {
	pragma := preproc.NewPragma()
	ctx := compiler.NewCompilerContext(pragma)
	ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})

	pragmaIdx := pragma.GetCurrentPragmaSetIndex()

	switchCmd := &SwitchCommand{}
	case1Cmd := &CaseCommand{}
	defaultCmd := &DefaultCommand{}
	case2Cmd := &CaseCommand{}

	switchCmd.Interpret(preproc.Line{Text: "SWITCH x", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
	switchCmd.Generate(ctx)

	case1Cmd.Interpret(preproc.Line{Text: "CASE 1", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
	case1Cmd.Generate(ctx)

	defaultCmd.Interpret(preproc.Line{Text: "DEFAULT", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
	defaultCmd.Generate(ctx)

	// Try to add CASE after DEFAULT - should fail
	if err := case2Cmd.Interpret(preproc.Line{Text: "CASE 2", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		// This is expected to fail during Interpret
		return
	}

	_, err := case2Cmd.Generate(ctx)
	if err == nil {
		t.Fatal("CASE after DEFAULT should fail")
	}
	if !strings.Contains(err.Error(), "after DEFAULT") {
		t.Errorf("Wrong error message: %v", err)
	}
}

func TestSwitchMultipleDefaults(t *testing.T) {
	pragma := preproc.NewPragma()
	ctx := compiler.NewCompilerContext(pragma)
	ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})

	pragmaIdx := pragma.GetCurrentPragmaSetIndex()

	switchCmd := &SwitchCommand{}
	case1Cmd := &CaseCommand{}
	default1Cmd := &DefaultCommand{}
	default2Cmd := &DefaultCommand{}

	switchCmd.Interpret(preproc.Line{Text: "SWITCH x", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
	switchCmd.Generate(ctx)

	case1Cmd.Interpret(preproc.Line{Text: "CASE 1", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
	case1Cmd.Generate(ctx)

	default1Cmd.Interpret(preproc.Line{Text: "DEFAULT", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
	default1Cmd.Generate(ctx)

	// Try to add second DEFAULT - should fail
	err := default2Cmd.Interpret(preproc.Line{Text: "DEFAULT", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
	if err == nil {
		t.Fatal("Multiple DEFAULT statements should fail")
	}
	if !strings.Contains(err.Error(), "multiple DEFAULT") {
		t.Errorf("Wrong error message: %v", err)
	}
}

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

	pragmaIdx := pragma.GetCurrentPragmaSetIndex()

	caseCmd := &CaseCommand{}
	err := caseCmd.Interpret(preproc.Line{Text: "CASE 1", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
	if err == nil {
		t.Fatal("CASE without SWITCH should fail")
	}
	if !strings.Contains(err.Error(), "not inside") {
		t.Errorf("Wrong error message: %v", err)
	}
}

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

	pragmaIdx := pragma.GetCurrentPragmaSetIndex()

	defaultCmd := &DefaultCommand{}
	err := defaultCmd.Interpret(preproc.Line{Text: "DEFAULT", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
	if err == nil {
		t.Fatal("DEFAULT without SWITCH should fail")
	}
	if !strings.Contains(err.Error(), "not inside") {
		t.Errorf("Wrong error message: %v", err)
	}
}

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

	pragmaIdx := pragma.GetCurrentPragmaSetIndex()

	endswitchCmd := &EndSwitchCommand{}
	err := endswitchCmd.Interpret(preproc.Line{Text: "ENDSWITCH", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
	if err == nil {
		t.Fatal("ENDSWITCH without SWITCH should fail")
	}
	if !strings.Contains(err.Error(), "not inside") {
		t.Errorf("Wrong error message: %v", err)
	}
}

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

	pragmaIdx := pragma.GetCurrentPragmaSetIndex()

	tests := []string{
		"SWITCH",
		"SWITCH x y",
	}

	for _, text := range tests {
		cmd := &SwitchCommand{}
		err := cmd.Interpret(preproc.Line{Text: text, Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
		if err == nil {
			t.Errorf("Should fail with wrong param count: %s", text)
		}
	}
}

func TestCaseWrongParamCount(t *testing.T) {
	pragma := preproc.NewPragma()
	ctx := compiler.NewCompilerContext(pragma)
	ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})

	pragmaIdx := pragma.GetCurrentPragmaSetIndex()

	switchCmd := &SwitchCommand{}
	switchCmd.Interpret(preproc.Line{Text: "SWITCH x", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
	switchCmd.Generate(ctx)

	tests := []string{
		"CASE",
		"CASE 1 2",
	}

	for _, text := range tests {
		cmd := &CaseCommand{}
		err := cmd.Interpret(preproc.Line{Text: text, Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
		if err == nil {
			t.Errorf("Should fail with wrong param count: %s", text)
		}
	}
}

func TestDefaultWrongParamCount(t *testing.T) {
	pragma := preproc.NewPragma()
	ctx := compiler.NewCompilerContext(pragma)
	ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})

	pragmaIdx := pragma.GetCurrentPragmaSetIndex()

	switchCmd := &SwitchCommand{}
	switchCmd.Interpret(preproc.Line{Text: "SWITCH x", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
	switchCmd.Generate(ctx)

	cmd := &DefaultCommand{}
	err := cmd.Interpret(preproc.Line{Text: "DEFAULT extra", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
	if err == nil {
		t.Error("DEFAULT with extra params should fail")
	}
}

func TestEndswitchWrongParamCount(t *testing.T) {
	pragma := preproc.NewPragma()
	ctx := compiler.NewCompilerContext(pragma)
	ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})

	pragmaIdx := pragma.GetCurrentPragmaSetIndex()

	switchCmd := &SwitchCommand{}
	switchCmd.Interpret(preproc.Line{Text: "SWITCH x", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
	switchCmd.Generate(ctx)

	cmd := &EndSwitchCommand{}
	err := cmd.Interpret(preproc.Line{Text: "ENDSWITCH extra", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
	if err == nil {
		t.Error("ENDSWITCH with extra params should fail")
	}
}

func TestSwitchWordType(t *testing.T) {
	pragma := preproc.NewPragma()
	ctx := compiler.NewCompilerContext(pragma)
	ctx.SymbolTable.AddVar("big_val", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})

	pragmaIdx := pragma.GetCurrentPragmaSetIndex()

	switchCmd := &SwitchCommand{}
	caseCmd := &CaseCommand{}
	endswitchCmd := &EndSwitchCommand{}

	if err := switchCmd.Interpret(preproc.Line{Text: "SWITCH big_val", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("SWITCH error = %v", err)
	}
	switchCmd.Generate(ctx)

	if err := caseCmd.Interpret(preproc.Line{Text: "CASE 1000", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("CASE error = %v", err)
	}
	caseAsm, err := caseCmd.Generate(ctx)
	if err != nil {
		t.Fatalf("CASE Generate() error = %v", err)
	}

	endswitchCmd.Interpret(preproc.Line{Text: "ENDSWITCH", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
	endswitchCmd.Generate(ctx)

	// Should have high byte check for word
	foundHighByteCheck := false
	for _, inst := range caseAsm {
		if strings.Contains(inst, "big_val+1") {
			foundHighByteCheck = true
			break
		}
	}
	if !foundHighByteCheck {
		t.Error("Expected high byte check for word comparison")
	}
}

func TestSwitchWithConstant(t *testing.T) {
	pragma := preproc.NewPragma()
	ctx := compiler.NewCompilerContext(pragma)
	ctx.SymbolTable.AddConst("MAX_VAL", "", compiler.KindByte, 100, preproc.Line{Filename: "test.c65", LineNo: 1})
	ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})

	pragmaIdx := pragma.GetCurrentPragmaSetIndex()

	switchCmd := &SwitchCommand{}
	caseCmd := &CaseCommand{}
	endswitchCmd := &EndSwitchCommand{}

	if err := switchCmd.Interpret(preproc.Line{Text: "SWITCH x", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("SWITCH error = %v", err)
	}
	switchCmd.Generate(ctx)

	if err := caseCmd.Interpret(preproc.Line{Text: "CASE MAX_VAL", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("CASE error = %v", err)
	}
	caseAsm, err := caseCmd.Generate(ctx)
	if err != nil {
		t.Fatalf("CASE Generate() error = %v", err)
	}

	endswitchCmd.Interpret(preproc.Line{Text: "ENDSWITCH", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
	endswitchCmd.Generate(ctx)

	// Constant should be folded to immediate value
	found := false
	for _, inst := range caseAsm {
		if strings.Contains(inst, "#$64") { // 100 = 0x64
			found = true
			break
		}
	}
	if !found {
		t.Error("Constant should be folded to immediate value")
	}
}

func TestSwitchNested(t *testing.T) {
	pragma := preproc.NewPragma()
	ctx := compiler.NewCompilerContext(pragma)
	ctx.SymbolTable.AddVar("outer", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
	ctx.SymbolTable.AddVar("inner", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})

	pragmaIdx := pragma.GetCurrentPragmaSetIndex()

	switch1Cmd := &SwitchCommand{}
	case1Cmd := &CaseCommand{}
	switch2Cmd := &SwitchCommand{}
	case2Cmd := &CaseCommand{}
	endswitch2Cmd := &EndSwitchCommand{}
	endswitch1Cmd := &EndSwitchCommand{}

	if err := switch1Cmd.Interpret(preproc.Line{Text: "SWITCH outer", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("SWITCH 1 error = %v", err)
	}
	switch1Asm, _ := switch1Cmd.Generate(ctx)

	if err := case1Cmd.Interpret(preproc.Line{Text: "CASE 1", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("CASE 1 error = %v", err)
	}
	case1Cmd.Generate(ctx)

	if err := switch2Cmd.Interpret(preproc.Line{Text: "SWITCH inner", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("SWITCH 2 error = %v", err)
	}
	switch2Asm, _ := switch2Cmd.Generate(ctx)

	if err := case2Cmd.Interpret(preproc.Line{Text: "CASE 2", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("CASE 2 error = %v", err)
	}
	case2Cmd.Generate(ctx)

	if err := endswitch2Cmd.Interpret(preproc.Line{Text: "ENDSWITCH", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("ENDSWITCH 2 error = %v", err)
	}
	endswitch2Asm, _ := endswitch2Cmd.Generate(ctx)

	if err := endswitch1Cmd.Interpret(preproc.Line{Text: "ENDSWITCH", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("ENDSWITCH 1 error = %v", err)
	}
	endswitch1Asm, _ := endswitch1Cmd.Generate(ctx)

	// Both switches should generate assembly
	if len(switch1Asm) < 0 || len(switch2Asm) < 0 {
		// SWITCHes don't generate asm, just setup state
	}

	// Both ENDSWITCHes should generate labels
	if len(endswitch1Asm) == 0 || len(endswitch2Asm) == 0 {
		t.Error("Nested switches should both generate end labels")
	}

	// Labels should be different
	label1 := ""
	label2 := ""
	if len(endswitch1Asm) > 0 {
		label1 = endswitch1Asm[len(endswitch1Asm)-1]
	}
	if len(endswitch2Asm) > 0 {
		label2 = endswitch2Asm[len(endswitch2Asm)-1]
	}

	if label1 == label2 && label1 != "" {
		t.Error("Nested switches should have different end labels")
	}
}

func TestSwitchLongJump(t *testing.T) {
	pragma := preproc.NewPragma()
	pragma.AddPragma("_P_USE_LONG_JUMP", "1")
	ctx := compiler.NewCompilerContext(pragma)
	ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})

	pragmaIdx := pragma.GetCurrentPragmaSetIndex()

	switchCmd := &SwitchCommand{}
	case1Cmd := &CaseCommand{}
	endswitchCmd := &EndSwitchCommand{}

	if err := switchCmd.Interpret(preproc.Line{Text: "SWITCH x", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("SWITCH error = %v", err)
	}
	switchCmd.Generate(ctx)

	if err := case1Cmd.Interpret(preproc.Line{Text: "CASE 1", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("CASE 1 error = %v", err)
	}
	case1Asm, _ := case1Cmd.Generate(ctx)

	endswitchCmd.Interpret(preproc.Line{Text: "ENDSWITCH", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
	endswitchCmd.Generate(ctx)

	// In long jump mode, comparison should have pattern: short branch + jmp + label
	// Look for both a short branch (beq/bne) and a jmp to _SKIPCASE
	foundShortBranch := false
	foundJmpToSkip := false
	foundLabel := false

	for _, inst := range case1Asm {
		if strings.Contains(inst, "beq ") || strings.Contains(inst, "bne ") {
			foundShortBranch = true
		}
		if strings.Contains(inst, "jmp ") && strings.Contains(inst, "_SKIPCASE") {
			foundJmpToSkip = true
		}
		// Labels don't start with tab
		trimmed := strings.TrimSpace(inst)
		if !strings.HasPrefix(inst, "\t") && strings.Contains(inst, "_") && len(trimmed) > 0 {
			foundLabel = true
		}
	}

	if !foundShortBranch {
		t.Error("Long jump mode should have short branch (beq/bne)")
	}
	if !foundJmpToSkip {
		t.Error("Long jump mode should have JMP to skip label in comparison")
	}
	if !foundLabel {
		t.Error("Long jump mode should have success label in comparison")
	}

	// Verify the pattern is different from normal mode
	pragmaNormal := preproc.NewPragma()
	ctxNormal := compiler.NewCompilerContext(pragmaNormal)
	ctxNormal.SymbolTable.AddVar("x", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})

	pragmaIdxNormal := pragmaNormal.GetCurrentPragmaSetIndex()

	switchCmdNormal := &SwitchCommand{}
	caseCmdNormal := &CaseCommand{}

	switchCmdNormal.Interpret(preproc.Line{Text: "SWITCH x", Kind: preproc.Source, PragmaSetIndex: pragmaIdxNormal}, ctxNormal)
	switchCmdNormal.Generate(ctxNormal)

	caseCmdNormal.Interpret(preproc.Line{Text: "CASE 1", Kind: preproc.Source, PragmaSetIndex: pragmaIdxNormal}, ctxNormal)
	normalAsm, _ := caseCmdNormal.Generate(ctxNormal)

	// Normal mode should NOT have jmp in comparison (only short branch)
	normalHasJmp := false
	for _, inst := range normalAsm {
		if strings.Contains(inst, "\tjmp") {
			normalHasJmp = true
			break
		}
	}
	if normalHasJmp {
		t.Error("Normal mode should not have JMP in comparison code")
	}
}

func TestSwitchOnConstant(t *testing.T) {
	pragma := preproc.NewPragma()
	ctx := compiler.NewCompilerContext(pragma)
	ctx.SymbolTable.AddConst("VALUE", "", compiler.KindByte, 5, preproc.Line{Filename: "test.c65", LineNo: 1})

	pragmaIdx := pragma.GetCurrentPragmaSetIndex()

	switchCmd := &SwitchCommand{}
	case1Cmd := &CaseCommand{}
	case2Cmd := &CaseCommand{}
	endswitchCmd := &EndSwitchCommand{}

	if err := switchCmd.Interpret(preproc.Line{Text: "SWITCH VALUE", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("SWITCH on constant error = %v", err)
	}
	switchCmd.Generate(ctx)

	// CASE with matching constant - should be optimized away (constant folding)
	if err := case1Cmd.Interpret(preproc.Line{Text: "CASE 5", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("CASE 5 error = %v", err)
	}
	case1Asm, err := case1Cmd.Generate(ctx)
	if err != nil {
		t.Fatalf("CASE 5 Generate() error = %v", err)
	}

	// With constant folding, matching constant case generates no comparison code (optimization)
	if len(case1Asm) != 0 {
		t.Errorf("CASE with matching constant should be optimized away, got: %v", case1Asm)
	}

	// CASE with non-matching constant - should generate JMP to skip
	if err := case2Cmd.Interpret(preproc.Line{Text: "CASE 10", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("CASE 10 error = %v", err)
	}
	case2Asm, err := case2Cmd.Generate(ctx)
	if err != nil {
		t.Fatalf("CASE 10 Generate() error = %v", err)
	}

	// Non-matching constant should generate JMP to skip this case
	if len(case2Asm) == 0 {
		t.Error("CASE with non-matching constant should generate skip code")
	}
	foundJmp := false
	for _, line := range case2Asm {
		if strings.Contains(line, "jmp") {
			foundJmp = true
			break
		}
	}
	if !foundJmp {
		t.Error("Non-matching constant case should have JMP to skip")
	}

	endswitchCmd.Interpret(preproc.Line{Text: "ENDSWITCH", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
	endswitchCmd.Generate(ctx)
}

func TestSwitchEmptyWithOnlyDefault(t *testing.T) {
	pragma := preproc.NewPragma()
	ctx := compiler.NewCompilerContext(pragma)
	ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})

	pragmaIdx := pragma.GetCurrentPragmaSetIndex()

	switchCmd := &SwitchCommand{}
	defaultCmd := &DefaultCommand{}
	endswitchCmd := &EndSwitchCommand{}

	if err := switchCmd.Interpret(preproc.Line{Text: "SWITCH x", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("SWITCH error = %v", err)
	}
	switchCmd.Generate(ctx)

	if err := defaultCmd.Interpret(preproc.Line{Text: "DEFAULT", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("DEFAULT error = %v", err)
	}
	defaultAsm, err := defaultCmd.Generate(ctx)
	if err != nil {
		t.Fatalf("DEFAULT Generate() error = %v", err)
	}

	if err := endswitchCmd.Interpret(preproc.Line{Text: "ENDSWITCH", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
		t.Fatalf("ENDSWITCH error = %v", err)
	}
	endswitchAsm, err := endswitchCmd.Generate(ctx)
	if err != nil {
		t.Fatalf("ENDSWITCH Generate() error = %v", err)
	}

	// DEFAULT without previous CASE should not emit JMP
	hasJmp := false
	for _, line := range defaultAsm {
		if strings.Contains(line, "jmp") {
			hasJmp = true
			break
		}
	}
	if hasJmp {
		t.Error("DEFAULT without previous CASE should not emit JMP")
	}

	// ENDSWITCH should still emit end label
	if len(endswitchAsm) == 0 {
		t.Error("ENDSWITCH should emit end label")
	}
}

func TestSwitchComparisonTypes(t *testing.T) {
	tests := []struct {
		name       string
		varType    compiler.VarKind
		caseValue  string
		shouldWork bool
	}{
		{"byte var byte case", compiler.KindByte, "10", true},
		{"word var byte case", compiler.KindWord, "10", true},
		{"byte var word case", compiler.KindByte, "1000", false},
		{"word var word case", compiler.KindWord, "1000", true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			pragma := preproc.NewPragma()
			ctx := compiler.NewCompilerContext(pragma)
			ctx.SymbolTable.AddVar("x", "", tt.varType, 0, preproc.Line{Filename: "test.c65", LineNo: 1})

			pragmaIdx := pragma.GetCurrentPragmaSetIndex()

			switchCmd := &SwitchCommand{}
			caseCmd := &CaseCommand{}
			endswitchCmd := &EndSwitchCommand{}

			switchCmd.Interpret(preproc.Line{Text: "SWITCH x", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
			switchCmd.Generate(ctx)

			err := caseCmd.Interpret(preproc.Line{Text: "CASE " + tt.caseValue, Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
			if tt.shouldWork && err != nil {
				t.Fatalf("CASE Interpret() unexpected error = %v", err)
			}
			if !tt.shouldWork && err == nil {
				t.Fatalf("CASE Interpret() should have failed but didn't")
			}

			if tt.shouldWork {
				_, err := caseCmd.Generate(ctx)
				if err != nil {
					t.Fatalf("CASE Generate() error = %v", err)
				}
			}

			endswitchCmd.Interpret(preproc.Line{Text: "ENDSWITCH", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
			endswitchCmd.Generate(ctx)
		})
	}
}

func TestSwitchWithVariableCase(t *testing.T) {
	tests := []struct {
		name         string
		switchType   compiler.VarKind
		caseType     compiler.VarKind
		shouldWork   bool
	}{
		{"byte switch byte case", compiler.KindByte, compiler.KindByte, true},
		{"byte switch word case", compiler.KindByte, compiler.KindWord, true},
		{"word switch byte case", compiler.KindWord, compiler.KindByte, true},
		{"word switch word case", compiler.KindWord, compiler.KindWord, true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			pragma := preproc.NewPragma()
			ctx := compiler.NewCompilerContext(pragma)
			ctx.SymbolTable.AddVar("switch_var", "", tt.switchType, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
			ctx.SymbolTable.AddVar("case_var", "", tt.caseType, 0, preproc.Line{Filename: "test.c65", LineNo: 1})

			pragmaIdx := pragma.GetCurrentPragmaSetIndex()

			switchCmd := &SwitchCommand{}
			caseCmd := &CaseCommand{}
			endswitchCmd := &EndSwitchCommand{}

			if err := switchCmd.Interpret(preproc.Line{Text: "SWITCH switch_var", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx); err != nil {
				t.Fatalf("SWITCH error = %v", err)
			}
			switchCmd.Generate(ctx)

			err := caseCmd.Interpret(preproc.Line{Text: "CASE case_var", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
			if tt.shouldWork && err != nil {
				t.Fatalf("CASE with variable unexpected error = %v", err)
			}
			if !tt.shouldWork && err == nil {
				t.Fatal("CASE with variable should have failed but didn't")
			}

			if tt.shouldWork {
				caseAsm, err := caseCmd.Generate(ctx)
				if err != nil {
					t.Fatalf("CASE Generate() error = %v", err)
				}
				// Verify assembly was generated for comparison
				if len(caseAsm) == 0 {
					t.Error("CASE with variable should generate comparison code")
				}
			}

			endswitchCmd.Interpret(preproc.Line{Text: "ENDSWITCH", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
			endswitchCmd.Generate(ctx)
		})
	}
}

func TestSwitchByteRangeValidation(t *testing.T) {
	tests := []struct {
		name         string
		caseValue    string
		expectError  bool
		errorContains string
	}{
		{"valid byte 0", "0", false, ""},
		{"valid byte 255", "255", false, ""},
		{"valid byte 100", "100", false, ""},
		{"out of range 256", "256", true, "will never match BYTE variable"},
		{"out of range 1000", "1000", true, "will never match BYTE variable"},
		{"out of range 10000", "10000", true, "will never match BYTE variable"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			pragma := preproc.NewPragma()
			ctx := compiler.NewCompilerContext(pragma)
			ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})

			pragmaIdx := pragma.GetCurrentPragmaSetIndex()

			switchCmd := &SwitchCommand{}
			caseCmd := &CaseCommand{}

			switchCmd.Interpret(preproc.Line{Text: "SWITCH x", Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)
			switchCmd.Generate(ctx)

			err := caseCmd.Interpret(preproc.Line{Text: "CASE " + tt.caseValue, Kind: preproc.Source, PragmaSetIndex: pragmaIdx}, ctx)

			if tt.expectError {
				if err == nil {
					t.Fatalf("Expected error for CASE %s but got none", tt.caseValue)
				}
				if !strings.Contains(err.Error(), tt.errorContains) {
					t.Errorf("Error message '%s' should contain '%s'", err.Error(), tt.errorContains)
				}
			} else {
				if err != nil {
					t.Fatalf("Unexpected error for CASE %s: %v", tt.caseValue, err)
				}
			}
		})
	}
}

// Helper to compare assembly output
func equalAsmSwitch(got, want []string) bool {
	if len(got) != len(want) {
		return false
	}
	for i := range got {
		if got[i] != want[i] {
			return false
		}
	}
	return true
}

// Helper to check if assembly contains expected instructions
func containsInstructions(asm []string, expected []string) bool {
	for _, exp := range expected {
		found := false
		for _, line := range asm {
			if strings.Contains(line, strings.TrimSpace(exp)) {
				found = true
				break
			}
		}
		if !found {
			return false
		}
	}
	return true
}
