package analytics

import (
	"fmt"
	"strings"
)

// maxParseDepth limits recursion in the parser to prevent stack overflow on malicious input.
const maxParseDepth = 256

// Parser is a recursive-descent parser for the formula DSL.
// It supports SF-style functions (IF, CASE, ISBLANK, etc.), cross-table
// references (Account.Industry), aggregates (SUM(Opportunity.Amount)),
// and standard arithmetic/comparison/logical operators.
type Parser struct {
	tokens []Token
	pos    int
	depth  int // current recursion depth
}

// Parse tokenizes and parses a formula DSL string into an AST.
func Parse(input string) (Node, error) {
	tokens, err := Tokenize(input)
	if err != nil {
		return nil, fmt.Errorf("tokenize: %w", err)
	}
	p := &Parser{tokens: tokens, pos: 0}
	node, err := p.parseExpr()
	if err != nil {
		return nil, err
	}
	if p.peek().Type != TokEOF {
		return nil, fmt.Errorf("unexpected token %s at position %d", p.peek(), p.peek().Pos)
	}
	return node, nil
}

func (p *Parser) peek() Token {
	if p.pos >= len(p.tokens) {
		return Token{TokEOF, "", -1}
	}
	return p.tokens[p.pos]
}

func (p *Parser) advance() Token {
	tok := p.peek()
	if p.pos < len(p.tokens) {
		p.pos++
	}
	return tok
}

func (p *Parser) expect(tt TokenType) (Token, error) {
	tok := p.advance()
	if tok.Type != tt {
		return tok, fmt.Errorf("expected %s, got %s at position %d", tt, tok, tok.Pos)
	}
	return tok, nil
}

// parseExpr is the entry point: handles OR (lowest precedence).
func (p *Parser) parseExpr() (Node, error) {
	p.depth++
	if p.depth > maxParseDepth {
		return nil, fmt.Errorf("formula exceeds maximum nesting depth of %d", maxParseDepth)
	}
	defer func() { p.depth-- }()
	return p.parseOr()
}

func (p *Parser) parseOr() (Node, error) {
	left, err := p.parseAnd()
	if err != nil {
		return nil, err
	}
	for p.peek().Type == TokOr {
		p.advance()
		right, err := p.parseAnd()
		if err != nil {
			return nil, err
		}
		// Flatten nested ORs
		if lo, ok := left.(*LogicalOp); ok && lo.Op == "OR" {
			lo.Operands = append(lo.Operands, right)
			left = lo
		} else {
			left = &LogicalOp{Op: "OR", Operands: []Node{left, right}}
		}
	}
	return left, nil
}

func (p *Parser) parseAnd() (Node, error) {
	left, err := p.parseNot()
	if err != nil {
		return nil, err
	}
	for p.peek().Type == TokAnd {
		p.advance()
		right, err := p.parseNot()
		if err != nil {
			return nil, err
		}
		if lo, ok := left.(*LogicalOp); ok && lo.Op == "AND" {
			lo.Operands = append(lo.Operands, right)
			left = lo
		} else {
			left = &LogicalOp{Op: "AND", Operands: []Node{left, right}}
		}
	}
	return left, nil
}

func (p *Parser) parseNot() (Node, error) {
	if p.peek().Type == TokNot {
		p.advance()
		operand, err := p.parseNot()
		if err != nil {
			return nil, err
		}
		return &UnaryOp{Op: "NOT", Operand: operand}, nil
	}
	return p.parseComparison()
}

func (p *Parser) parseComparison() (Node, error) {
	left, err := p.parseConcat()
	if err != nil {
		return nil, err
	}
	for {
		tok := p.peek()
		switch tok.Type {
		case TokEQ, TokNE, TokLT, TokLE, TokGT, TokGE:
			op := p.advance()
			right, err := p.parseConcat()
			if err != nil {
				return nil, err
			}
			opStr := op.Value
			if opStr == "==" {
				opStr = "="
			}
			left = &BinaryOp{Op: opStr, Left: left, Right: right}
		default:
			return left, nil
		}
	}
}

// parseConcat handles & (string concat, SF-style).
func (p *Parser) parseConcat() (Node, error) {
	left, err := p.parseAddSub()
	if err != nil {
		return nil, err
	}
	for p.peek().Type == TokAmpersand {
		p.advance()
		right, err := p.parseAddSub()
		if err != nil {
			return nil, err
		}
		left = &BinaryOp{Op: "&", Left: left, Right: right}
	}
	return left, nil
}

func (p *Parser) parseAddSub() (Node, error) {
	left, err := p.parseMulDiv()
	if err != nil {
		return nil, err
	}
	for {
		tok := p.peek()
		if tok.Type == TokPlus || tok.Type == TokMinus {
			op := p.advance()
			right, err := p.parseMulDiv()
			if err != nil {
				return nil, err
			}
			left = &BinaryOp{Op: op.Value, Left: left, Right: right}
		} else {
			return left, nil
		}
	}
}

func (p *Parser) parseMulDiv() (Node, error) {
	left, err := p.parseExponent()
	if err != nil {
		return nil, err
	}
	for {
		tok := p.peek()
		if tok.Type == TokStar || tok.Type == TokSlash {
			op := p.advance()
			right, err := p.parseExponent()
			if err != nil {
				return nil, err
			}
			left = &BinaryOp{Op: op.Value, Left: left, Right: right}
		} else {
			return left, nil
		}
	}
}

func (p *Parser) parseExponent() (Node, error) {
	left, err := p.parseUnary()
	if err != nil {
		return nil, err
	}
	if p.peek().Type == TokCaret {
		p.advance()
		right, err := p.parseExponent() // right-associative
		if err != nil {
			return nil, err
		}
		left = &BinaryOp{Op: "^", Left: left, Right: right}
	}
	return left, nil
}

func (p *Parser) parseUnary() (Node, error) {
	if p.peek().Type == TokMinus {
		p.advance()
		operand, err := p.parseUnary()
		if err != nil {
			return nil, err
		}
		return &UnaryOp{Op: "-", Operand: operand}, nil
	}
	return p.parsePrimary()
}

// parsePrimary handles literals, identifiers, function calls, and parenthesized expressions.
func (p *Parser) parsePrimary() (Node, error) {
	tok := p.peek()

	switch tok.Type {
	case TokNumber:
		p.advance()
		return &NumberLit{Value: tok.Value}, nil

	case TokString:
		p.advance()
		return &StringLit{Value: tok.Value}, nil

	case TokBoolean:
		p.advance()
		return &BoolLit{Value: strings.ToUpper(tok.Value) == "TRUE"}, nil

	case TokLParen:
		p.advance()
		expr, err := p.parseExpr()
		if err != nil {
			return nil, err
		}
		if _, err := p.expect(TokRParen); err != nil {
			return nil, err
		}
		return expr, nil

	case TokIdentifier:
		return p.parseIdentOrCall()

	case TokAnd, TokOr:
		// AND/OR as function call: AND(a, b, c) or OR(a, b, c)
		name := tok.Value
		p.advance()
		if p.peek().Type == TokLParen {
			return p.parseFuncCall(name)
		}
		return nil, fmt.Errorf("unexpected token %s at position %d", tok, tok.Pos)

	default:
		return nil, fmt.Errorf("unexpected token %s at position %d", tok, tok.Pos)
	}
}

// parseIdentOrCall handles identifiers, dotted references, and function calls.
func (p *Parser) parseIdentOrCall() (Node, error) {
	name := p.advance() // consume identifier

	// Check for NULL keyword
	if strings.ToUpper(name.Value) == "NULL" {
		return &NullLit{}, nil
	}

	// Handle SF polymorphic relationship syntax: "Owner:User" → relationship name "Owner"
	// The tokenizer captures "Owner:User" as a single identifier.
	nameVal := name.Value
	if idx := strings.Index(nameVal, ":"); idx > 0 {
		nameVal = nameVal[:idx] // Strip ":Type" suffix, keep relationship name
	}
	name.Value = nameVal

	// Check for function call: NAME(
	if p.peek().Type == TokLParen {
		return p.parseFuncCall(name.Value)
	}

	// Check for dotted reference: Object.Field or Object.Object.Field (multi-level)
	if p.peek().Type == TokDot {
		p.advance() // consume first dot
		field, err := p.expect(TokIdentifier)
		if err != nil {
			return nil, fmt.Errorf("expected field name after '%s.': %w", name.Value, err)
		}

		// Check for additional dots (3+ level refs like Opportunity.Account.BillingCountryCode)
		if p.peek().Type == TokDot {
			parts := []string{name.Value, field.Value}
			for p.peek().Type == TokDot {
				p.advance() // consume dot
				next, err := p.expect(TokIdentifier)
				if err != nil {
					return nil, fmt.Errorf("expected field name after '%s.': %w", strings.Join(parts, "."), err)
				}
				parts = append(parts, next.Value)
			}
			return &DeepCrossRef{Parts: parts}, nil
		}

		return &CrossRef{Object: name.Value, Field: field.Value}, nil
	}

	// Simple field reference
	return &FieldRef{Name: name.Value}, nil
}

// parseFuncCall handles all function calls including special forms.
func (p *Parser) parseFuncCall(funcName string) (Node, error) {
	upper := strings.ToUpper(funcName)
	p.advance() // consume '('

	switch upper {
	case "IF":
		return p.parseIfFunc()
	case "CASE":
		return p.parseCaseFunc()
	case "AND":
		return p.parseLogicalFunc("AND")
	case "OR":
		return p.parseLogicalFunc("OR")
	case "SUM", "COUNT", "MIN", "MAX", "AVG":
		return p.parseAggregateOrFunc(upper)
	default:
		return p.parseGenericFunc(upper)
	}
}

func (p *Parser) parseIfFunc() (Node, error) {
	cond, err := p.parseExpr()
	if err != nil {
		return nil, fmt.Errorf("IF condition: %w", err)
	}
	if _, err := p.expect(TokComma); err != nil {
		return nil, fmt.Errorf("IF: %w", err)
	}
	thenVal, err := p.parseExpr()
	if err != nil {
		return nil, fmt.Errorf("IF then: %w", err)
	}
	if _, err := p.expect(TokComma); err != nil {
		return nil, fmt.Errorf("IF: %w", err)
	}
	elseVal, err := p.parseExpr()
	if err != nil {
		return nil, fmt.Errorf("IF else: %w", err)
	}
	if _, err := p.expect(TokRParen); err != nil {
		return nil, err
	}
	return &IfExpr{Condition: cond, Then: thenVal, Else: elseVal}, nil
}

// parseCaseFunc parses CASE(expr, val1, result1, val2, result2, ..., elseResult)
func (p *Parser) parseCaseFunc() (Node, error) {
	expr, err := p.parseExpr()
	if err != nil {
		return nil, fmt.Errorf("CASE expression: %w", err)
	}

	var branches []CaseBranch
	var def Node

	for {
		if _, err := p.expect(TokComma); err != nil {
			return nil, fmt.Errorf("CASE: %w", err)
		}
		val, err := p.parseExpr()
		if err != nil {
			return nil, fmt.Errorf("CASE value: %w", err)
		}

		// Check if this is the last arg (else clause) — next token is ')'
		if p.peek().Type == TokRParen {
			def = val
			p.advance()
			break
		}

		if _, err := p.expect(TokComma); err != nil {
			return nil, fmt.Errorf("CASE: %w", err)
		}
		result, err := p.parseExpr()
		if err != nil {
			return nil, fmt.Errorf("CASE result: %w", err)
		}
		branches = append(branches, CaseBranch{Value: val, Result: result})

		if p.peek().Type == TokRParen {
			p.advance()
			break
		}
	}

	return &CaseExpr{Expr: expr, Branches: branches, Default: def}, nil
}

// parseLogicalFunc parses AND(a, b, c) or OR(a, b, c) as function-call form.
func (p *Parser) parseLogicalFunc(op string) (Node, error) {
	var operands []Node
	for {
		arg, err := p.parseExpr()
		if err != nil {
			return nil, err
		}
		operands = append(operands, arg)
		if p.peek().Type == TokComma {
			p.advance()
		} else {
			break
		}
	}
	if _, err := p.expect(TokRParen); err != nil {
		return nil, err
	}
	return &LogicalOp{Op: op, Operands: operands}, nil
}

// parseAggregateOrFunc distinguishes SUM(Object.Field) from SUM(expr).
// If the argument is a CrossRef, it becomes an AggregateExpr.
// Otherwise it's a regular function call.
func (p *Parser) parseAggregateOrFunc(funcName string) (Node, error) {
	arg, err := p.parseExpr()
	if err != nil {
		return nil, err
	}
	if _, err := p.expect(TokRParen); err != nil {
		return nil, err
	}

	if cr, ok := arg.(*CrossRef); ok {
		return &AggregateExpr{
			Func:        funcName,
			ChildObject: cr.Object,
			ChildField:  cr.Field,
		}, nil
	}

	// Regular function (e.g. SUM of a local expression)
	return &FuncCall{Name: funcName, Args: []Node{arg}}, nil
}

// parseGenericFunc parses FUNC(arg1, arg2, ...).
func (p *Parser) parseGenericFunc(funcName string) (Node, error) {
	var args []Node
	if p.peek().Type != TokRParen {
		for {
			arg, err := p.parseExpr()
			if err != nil {
				return nil, err
			}
			args = append(args, arg)
			if p.peek().Type == TokComma {
				p.advance()
			} else {
				break
			}
		}
	}
	if _, err := p.expect(TokRParen); err != nil {
		return nil, err
	}
	return &FuncCall{Name: funcName, Args: args}, nil
}
