package analytics

import (
	"fmt"
	"strings"
)

// Node is the interface for all AST nodes in the formula DSL.
type Node interface {
	nodeType() string
}

// --- Literal nodes ---

// NumberLit represents a numeric literal (e.g. 3.14).
type NumberLit struct {
	Value string
}

func (n *NumberLit) nodeType() string { return "NumberLit" }

// StringLit represents a string literal (e.g. 'hello').
type StringLit struct {
	Value string
}

func (n *StringLit) nodeType() string { return "StringLit" }

// BoolLit represents TRUE or FALSE.
type BoolLit struct {
	Value bool
}

func (n *BoolLit) nodeType() string { return "BoolLit" }

// NullLit represents NULL.
type NullLit struct{}

func (n *NullLit) nodeType() string { return "NullLit" }

// --- Reference nodes ---

// FieldRef is a simple field reference (e.g. Amount, Name).
type FieldRef struct {
	Name string
}

func (n *FieldRef) nodeType() string { return "FieldRef" }

// CrossRef is a cross-table field reference (e.g. Account.Industry, Owner.Name).
type CrossRef struct {
	Object string // "Account", "Owner"
	Field  string // "Industry", "Name"
}

func (n *CrossRef) nodeType() string { return "CrossRef" }

// DeepCrossRef is a multi-level cross-table reference (e.g. Opportunity.Account.BillingCountryCode).
// Parts contains all segments: ["Opportunity", "Account", "BillingCountryCode"].
type DeepCrossRef struct {
	Parts []string // e.g. ["Opportunity", "Account", "BillingCountryCode"]
}

func (n *DeepCrossRef) nodeType() string { return "DeepCrossRef" }

// --- Expression nodes ---

// BinaryOp represents a binary operation (e.g. a + b, a = b).
type BinaryOp struct {
	Op    string // "+", "-", "*", "/", "^", "=", "!=", "<", "<=", ">", ">=", "&"
	Left  Node
	Right Node
}

func (n *BinaryOp) nodeType() string { return "BinaryOp" }

// UnaryOp represents a unary operation (e.g. -x, NOT x).
type UnaryOp struct {
	Op      string // "-", "NOT"
	Operand Node
}

func (n *UnaryOp) nodeType() string { return "UnaryOp" }

// LogicalOp represents AND / OR with multiple operands.
type LogicalOp struct {
	Op       string // "AND", "OR"
	Operands []Node
}

func (n *LogicalOp) nodeType() string { return "LogicalOp" }

// --- Function call nodes ---

// FuncCall represents a function call: FUNC(arg1, arg2, ...).
type FuncCall struct {
	Name string
	Args []Node
}

func (n *FuncCall) nodeType() string { return "FuncCall" }

// --- Conditional nodes ---

// IfExpr represents IF(condition, thenVal, elseVal).
type IfExpr struct {
	Condition Node
	Then      Node
	Else      Node
}

func (n *IfExpr) nodeType() string { return "IfExpr" }

// CaseExpr represents CASE(expr, val1, result1, val2, result2, ..., elseResult).
// This is the SF-style CASE, not SQL CASE WHEN.
type CaseExpr struct {
	Expr     Node           // The expression being compared
	Branches []CaseBranch   // val → result pairs
	Default  Node           // else result
}

type CaseBranch struct {
	Value  Node
	Result Node
}

func (n *CaseExpr) nodeType() string { return "CaseExpr" }

// --- Aggregate node (for rollup formulas) ---

// AggregateExpr represents SUM(Object.Field), COUNT(Object.Field), etc.
type AggregateExpr struct {
	Func        string // SUM, COUNT, MIN, MAX, AVG
	ChildObject string // e.g. "Opportunity"
	ChildField  string // e.g. "Amount" (empty for COUNT)
	ForeignKey  string // e.g. "AccountId" — FK on child pointing to parent
}

func (n *AggregateExpr) nodeType() string { return "AggregateExpr" }

// nodeString returns a debug representation of an AST node.
func nodeString(n Node) string {
	switch v := n.(type) {
	case *NumberLit:
		return v.Value
	case *StringLit:
		return fmt.Sprintf("'%s'", v.Value)
	case *BoolLit:
		if v.Value {
			return "TRUE"
		}
		return "FALSE"
	case *NullLit:
		return "NULL"
	case *FieldRef:
		return v.Name
	case *CrossRef:
		return fmt.Sprintf("%s.%s", v.Object, v.Field)
	case *DeepCrossRef:
		return strings.Join(v.Parts, ".")
	case *BinaryOp:
		return fmt.Sprintf("(%s %s %s)", nodeString(v.Left), v.Op, nodeString(v.Right))
	case *UnaryOp:
		return fmt.Sprintf("(%s %s)", v.Op, nodeString(v.Operand))
	case *LogicalOp:
		parts := make([]string, len(v.Operands))
		for i, o := range v.Operands {
			parts[i] = nodeString(o)
		}
		return fmt.Sprintf("(%s)", strings.Join(parts, " "+v.Op+" "))
	case *FuncCall:
		args := make([]string, len(v.Args))
		for i, a := range v.Args {
			args[i] = nodeString(a)
		}
		return fmt.Sprintf("%s(%s)", v.Name, strings.Join(args, ", "))
	case *IfExpr:
		return fmt.Sprintf("IF(%s, %s, %s)", nodeString(v.Condition), nodeString(v.Then), nodeString(v.Else))
	case *CaseExpr:
		s := fmt.Sprintf("CASE(%s", nodeString(v.Expr))
		for _, b := range v.Branches {
			s += fmt.Sprintf(", %s, %s", nodeString(b.Value), nodeString(b.Result))
		}
		if v.Default != nil {
			s += fmt.Sprintf(", %s", nodeString(v.Default))
		}
		return s + ")"
	case *AggregateExpr:
		if v.ChildField == "" {
			return fmt.Sprintf("%s(%s)", v.Func, v.ChildObject)
		}
		return fmt.Sprintf("%s(%s.%s)", v.Func, v.ChildObject, v.ChildField)
	default:
		return fmt.Sprintf("?<%T>", n)
	}
}
