package commands

import (
	"fmt"
	"strings"

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

// DecrCommand handles DECREMENT operations
// Syntax:
//
//	DEC <target>           # old syntax
//	DECREMENT <target>     # old syntax
//	<var>--                # new syntax (literal, no space - variables only)
//
// <target> can be a variable or absolute address (old syntax only)
type DecrCommand struct {
	varName    string
	varKind    compiler.VarKind
	isAbsolute bool
	absAddr    uint16
}

func (c *DecrCommand) WillHandle(line preproc.Line) bool {
	params, err := utils.ParseParams(line.Text)
	if err != nil || len(params) == 0 {
		return false
	}

	// Old syntax: DEC/DECREMENT
	keyword := strings.ToUpper(params[0])
	if (keyword == "DEC" || keyword == "DECREMENT") && len(params) == 2 {
		return true
	}

	// New syntax: <var>-- (literal, no space)
	if len(params) == 1 && strings.HasSuffix(params[0], "--") {
		return true
	}

	return false
}

func (c *DecrCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext) error {
	// Clear state
	c.varName = ""
	c.isAbsolute = false
	c.absAddr = 0

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

	scope := ctx.CurrentScope()
	keyword := strings.ToUpper(params[0])

	var targetParam string
	var isNewSyntax bool

	if keyword == "DEC" || keyword == "DECREMENT" {
		// Old syntax: DEC/DECREMENT <target>
		if len(params) != 2 {
			return fmt.Errorf("DEC: wrong number of parameters")
		}
		targetParam = params[1]
		isNewSyntax = false
	} else if strings.HasSuffix(params[0], "--") {
		// New syntax: <var>-- (literal)
		if len(params) != 1 {
			return fmt.Errorf("DEC: wrong number of parameters")
		}
		targetParam = strings.TrimSuffix(params[0], "--")
		isNewSyntax = true
	} else {
		return fmt.Errorf("DEC: unrecognized syntax")
	}

	// Try variable lookup
	sym := ctx.SymbolTable.Lookup(targetParam, scope)
	if sym != nil {
		if sym.IsConst() {
			return fmt.Errorf("DEC: cannot decrement constant %q", targetParam)
		}
		c.varName = sym.FullName()
		c.varKind = sym.GetVarKind()
		c.isAbsolute = false
		return nil
	}

	// For new syntax (--), must be a variable
	if isNewSyntax {
		return fmt.Errorf("DEC: unknown variable %q", targetParam)
	}

	// Old syntax allows absolute addresses
	constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)

	val, evalErr := utils.EvaluateExpression(targetParam, constLookup)
	if evalErr != nil {
		return fmt.Errorf("DEC: expected variable or absolute address, got %q: %w", targetParam, evalErr)
	}

	if val < 0 || val > 65535 {
		return fmt.Errorf("DEC: address %d out of range (0-65535)", val)
	}

	c.isAbsolute = true
	c.absAddr = uint16(val)

	return nil
}

func (c *DecrCommand) Generate(ctx *compiler.CompilerContext) ([]string, error) {
	var asm []string

	if c.isAbsolute {
		// Absolute address
		asm = append(asm, fmt.Sprintf("\tdec $%04x", c.absAddr))
		return asm, nil
	}

	// Variable
	if c.varKind == compiler.KindByte {
		asm = append(asm, fmt.Sprintf("\tdec %s", c.varName))
		return asm, nil
	}

	// Word variable - handle borrow from high byte
	label := ctx.GeneralStack.Push()
	asm = append(asm, fmt.Sprintf("\tlda %s", c.varName))
	asm = append(asm, fmt.Sprintf("\tbne %s", label))
	asm = append(asm, fmt.Sprintf("\tdec %s+1", c.varName))
	asm = append(asm, label)
	asm = append(asm, fmt.Sprintf("\tdec %s", c.varName))

	return asm, nil
}

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