package commands

import (
	"fmt"
	"strings"

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

// ByteCommand handles BYTE variable declarations
// Syntax:
//
//	BYTE varname                    # byte with init = 0
//	BYTE varname = value            # byte with init value
//	BYTE varname @ address          # byte at absolute address
//	BYTE CONST varname = value      # constant byte
//	BYTE REGISTER varname           # register-hinted byte (function-local only)
//	BYTE REGISTER varname = value   # register-hinted byte with init value
type ByteCommand struct {
	varName string
	value   uint16
	isConst bool
	isAbs   bool
}

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

func (c *ByteCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext) error {
	c.varName = ""
	c.value = 0
	c.isConst = false
	c.isAbs = false

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

	paramCount := len(params)

	// Check for REGISTER keyword
	register := false
	if paramCount >= 2 && strings.ToUpper(params[1]) == "REGISTER" {
		register = true
		params = append(params[:1], params[2:]...)
		paramCount = len(params)
	}

	if register {
		if paramCount != 2 && paramCount != 4 {
			return fmt.Errorf("BYTE REGISTER: expected 'name' or 'name = value', got %d parameters after REGISTER", paramCount)
		}
	} else {
		if paramCount != 2 && paramCount != 4 && paramCount != 5 {
			return fmt.Errorf("BYTE: wrong number of parameters (%d)", paramCount)
		}
	}

	var varName string
	var value int64
	scope := ctx.FunctionHandler.CurrentFunction()

	constLookup := ctx.SymbolTable.ConstantLookupFunc(ctx.CurrentScope())

	switch paramCount {
	case 2:
		varName = params[1]
		value = 0

		if !utils.ValidateIdentifier(varName) {
			return fmt.Errorf("BYTE: invalid identifier %q", varName)
		}

		if register {
			if scope == "" {
				return fmt.Errorf("BYTE REGISTER %q is only valid inside a FUNC block (remove REGISTER or move inside a function)", varName)
			}
			err = ctx.SymbolTable.AddRegisterVar(varName, scope, uint16(value), line)
		} else {
			err = ctx.SymbolTable.AddVar(varName, scope, compiler.KindByte, uint16(value), line)
		}

	case 4:
		varName = params[1]
		operator := params[2]
		valueStr := params[3]

		if !utils.ValidateIdentifier(varName) {
			return fmt.Errorf("BYTE: invalid identifier %q", varName)
		}

		value, err = utils.EvaluateExpression(valueStr, constLookup)
		if err != nil {
			return fmt.Errorf("BYTE: invalid value %q: %w", valueStr, err)
		}

		if operator == "=" {
			if value < 0 || value > 255 {
				return fmt.Errorf("BYTE: init value %d out of range (0-255)", value)
			}
			if register {
				if scope == "" {
					return fmt.Errorf("BYTE REGISTER: variable %q must be declared in function scope", varName)
				}
				err = ctx.SymbolTable.AddRegisterVar(varName, scope, uint16(value), line)
			} else {
				err = ctx.SymbolTable.AddVar(varName, scope, compiler.KindByte, uint16(value), line)
			}

		} else if operator == "@" {
			if register {
				return fmt.Errorf("BYTE REGISTER: @-mapped address is not valid for register variables")
			}
			if value < 0 || value > 0xFFFF {
				return fmt.Errorf("BYTE: absolute address $%X out of range", value)
			}
			c.isAbs = true
			err = ctx.SymbolTable.AddAbsolute(varName, scope, compiler.KindByte, uint16(value), line)

		} else {
			return fmt.Errorf("BYTE: expected '=' or '@', got %q", operator)
		}

	case 5:
		if register {
			return fmt.Errorf("BYTE REGISTER: unexpected additional parameters")
		}
		constKeyword := strings.ToUpper(params[1])
		varName = params[2]
		operator := params[3]
		valueStr := params[4]

		if constKeyword != "CONST" {
			return fmt.Errorf("BYTE: expected CONST keyword, got %q", params[1])
		}

		if operator != "=" {
			return fmt.Errorf("BYTE: expected '=', got %q", operator)
		}

		if !utils.ValidateIdentifier(varName) {
			return fmt.Errorf("BYTE: invalid identifier %q", varName)
		}

		value, err = utils.EvaluateExpression(valueStr, constLookup)
		if err != nil {
			return fmt.Errorf("BYTE: invalid value %q: %w", valueStr, err)
		}

		if value < 0 || value > 255 {
			return fmt.Errorf("BYTE: const value %d out of range (0-255)", value)
		}

		c.isConst = true
		err = ctx.SymbolTable.AddConst(varName, scope, compiler.KindByte, uint16(value), line)
	}

	if err != nil {
		return fmt.Errorf("BYTE: %w", err)
	}

	c.varName = varName
	c.value = uint16(value)

	return nil
}

func (c *ByteCommand) Generate(_ *compiler.CompilerContext) ([]string, error) {
	// Variables are rendered by assembleOutput, not by individual commands
	return nil, nil
}

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