package commands

import (
	"fmt"
	"strings"

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

// GotoCommand handles GOTO statements
// Syntax: GOTO <target>
// Where target is:
//   - Label name (naked identifier)
//   - Word variable (indirect jump via jmp (var))
//   - Expression/address (numeric constant or expression)
//
// Note: Byte variable jumps not supported - use inline asm for zero-page jumps
type GotoCommand struct {
	targetName string
	targetKind compiler.VarKind
	address    uint16
	isVar      bool
	isLabel    bool // true if neither var nor expression
}

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

func (c *GotoCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext) error {
	// Clear state
	c.targetName = ""
	c.address = 0
	c.isVar = false
	c.isLabel = false

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

	if len(params) != 2 {
		return fmt.Errorf("GOTO: expected 2 parameters, got %d", len(params))
	}

	targetParam := params[1]
	scope := ctx.CurrentScope()

	constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)

	// Try ParseOperandParam - handles variables, constants, expressions
	varName, varKind, value, isVar, err := compiler.ParseOperandParam(
		targetParam, ctx.SymbolTable, scope, constLookup)

	if err != nil {
		// Not a variable or expression -> treat as label
		c.targetName = targetParam
		c.isLabel = true
		return nil
	}

	// It's either a variable or expression/constant
	if isVar {
		// It's a variable
		if varKind == compiler.KindByte {
			return fmt.Errorf("GOTO: cannot jump to byte variable %q (use inline assembler for zero-page jumps)", targetParam)
		}
		c.targetName = varName
		c.targetKind = varKind
		c.isVar = true
		return nil
	}

	// It's an expression/constant
	c.address = value
	c.isVar = false
	c.isLabel = false
	return nil
}

func (c *GotoCommand) Generate(_ *compiler.CompilerContext) ([]string, error) {
	if c.isVar {
		// Indirect jump through word variable
		return []string{fmt.Sprintf("\tjmp (%s)", c.targetName)}, nil
	}

	if c.isLabel {
		// Jump to label
		return []string{fmt.Sprintf("\tjmp %s", c.targetName)}, nil
	}

	// Jump to computed address
	return []string{fmt.Sprintf("\tjmp $%04x", c.address)}, nil
}

func (c *GotoCommand) GetClass() compiler.CmdClass { return compiler.CmdLinear }
func (c *GotoCommand) GetName() string             { return "GOTO" }
