package compiler

import (
	"fmt"
	"strings"
	"testing"

	"c65gm/internal/preproc"
	"c65gm/internal/utils"
)

// TestBreakCommand is a simple command implementation for testing
type TestBreakCommand struct {
	line preproc.Line
}

func (c *TestBreakCommand) WillHandle(line preproc.Line) bool {
	params, err := utils.ParseParams(line.Text)
	if err != nil || len(params) == 0 {
		return false
	}
	return strings.ToUpper(params[0]) == "BREAK"
}

func (c *TestBreakCommand) Interpret(line preproc.Line, ctx *CompilerContext) error {
	c.line = line

	params, err := utils.ParseParams(line.Text)
	if err != nil {
		return err
	}

	if len(params) != 1 {
		return fmt.Errorf("BREAK does not expect parameters")
	}

	return nil
}

func (c *TestBreakCommand) Generate(ctx *CompilerContext) ([]string, error) {
	// BREAK jumps to end of WHILE loop
	label, err := ctx.LoopEndStack.Peek()
	if err != nil {
		return nil, fmt.Errorf("BREAK outside of WHILE loop")
	}

	return []string{
		fmt.Sprintf("  jmp %s_end", label),
	}, nil
}

func TestCompilerArchitecture(t *testing.T) {
	// Create pragma
	pragma := preproc.NewPragma()

	// Create compiler
	comp := NewCompiler(pragma)

	// Register BREAK command
	comp.Registry().Register(&TestBreakCommand{})

	// Create test input - BREAK inside a simulated WHILE
	lines := []preproc.Line{
		{
			Text:           "BREAK",
			Filename:       "test.c65",
			LineNo:         1,
			Kind:           preproc.Source,
			PragmaSetIndex: 0,
		},
	}

	// Manually push a WHILE label so BREAK has something to reference
	comp.Context().LoopEndStack.Push()

	// Compile
	output, err := comp.Compile(lines)

	// Should fail because BREAK needs proper WHILE context
	// But this tests the basic flow: WillHandle -> Interpret -> Generate
	if err != nil {
		t.Logf("Expected controlled error: %v", err)
	}

	// Check we got some output structure
	if len(output) == 0 {
		t.Logf("Got output lines: %d", len(output))
	}

	t.Logf("Output:\n%s", strings.Join(output, "\n"))
}

func TestCommandRegistry(t *testing.T) {
	registry := NewCommandRegistry()

	breakCmd := &TestBreakCommand{}
	registry.Register(breakCmd)

	line := preproc.Line{
		Text:     "BREAK",
		Filename: "test.c65",
		LineNo:   1,
		Kind:     preproc.Source,
	}

	cmd, found := registry.FindHandler(line)
	if !found {
		t.Fatal("Expected to find BREAK handler")
	}

	if cmd != breakCmd {
		t.Fatal("Expected to get same command instance")
	}
}

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

	// Test that all resources are initialized
	if ctx.SymbolTable == nil {
		t.Error("SymbolTable not initialized")
	}
	if ctx.FunctionHandler == nil {
		t.Error("FunctionHandler not initialized")
	}
	if ctx.ConstStrHandler == nil {
		t.Error("ConstStrHandler not initialized")
	}
	if ctx.LoopEndStack == nil {
		t.Error("LoopEndStack not initialized")
	}
	if ctx.IfStack == nil {
		t.Error("IfStack not initialized")
	}
	if ctx.GeneralStack == nil {
		t.Error("GeneralStack not initialized")
	}
	if ctx.Pragma == nil {
		t.Error("Pragma not initialized")
	}

	// Test CurrentScope
	scope := ctx.CurrentScope()
	if scope != nil {
		t.Errorf("Expected nil scope in global context, got %v", scope)
	}
}
