"""Built-in formula functions for the bitable formula engine.

v1 implements: SUM, AVG, COUNT, MIN, MAX, CONCAT, ABS, ROUND, IF, LEN.

Aggregate functions (SUM/AVG/COUNT/MIN/MAX) accept a list of values
(the entire column) and return a scalar. Non-aggregate functions
(ABS/ROUND/IF/LEN/CONCAT) operate on scalars.

The engine determines whether to pass a column (list) or scalar (row value)
based on the calling context — see :mod:`agentkit.bitable.formula.engine`.
"""

from __future__ import annotations

from typing import Any, Callable

# ── Aggregate functions (operate on lists) ────────────────


def _sum(values: list[Any]) -> float | int:
    """Sum of numeric values, ignoring None/empty."""
    total = 0
    for v in values:
        if v is None or v == "":
            continue
        total += v
    return total


def _avg(values: list[Any]) -> float:
    """Average of numeric values, ignoring None/empty."""
    nums = [v for v in values if v is not None and v != ""]
    if not nums:
        return 0.0
    return sum(nums) / len(nums)


def _count(values: list[Any]) -> int:
    """Count of non-empty values."""
    return sum(1 for v in values if v is not None and v != "")


def _min(values: list[Any]) -> Any:
    """Minimum of numeric values, ignoring None/empty."""
    nums = [v for v in values if v is not None and v != ""]
    if not nums:
        return 0
    return min(nums)


def _max(values: list[Any]) -> Any:
    """Maximum of numeric values, ignoring None/empty."""
    nums = [v for v in values if v is not None and v != ""]
    if not nums:
        return 0
    return max(nums)


# ── Scalar functions ──────────────────────────────────────


def _abs(value: Any) -> Any:
    return abs(value)


def _round(value: Any, digits: int = 0) -> float:
    return round(value, digits)


def _if(condition: Any, true_val: Any, false_val: Any = None) -> Any:
    return true_val if condition else false_val


def _len(value: Any) -> int:
    if value is None:
        return 0
    return len(str(value))


def _concat(*args: Any) -> str:
    """Concatenate all arguments as strings."""
    return "".join(str(a) for a in args if a is not None)


# ── Registry ──────────────────────────────────────────────

# Functions that aggregate a column (receive a list of all column values)
AGGREGATE_FUNCTIONS: frozenset[str] = frozenset({"SUM", "AVG", "COUNT", "MIN", "MAX"})

FUNCTION_REGISTRY: dict[str, Callable[..., Any]] = {
    "SUM": _sum,
    "AVG": _avg,
    "COUNT": _count,
    "MIN": _min,
    "MAX": _max,
    "ABS": _abs,
    "ROUND": _round,
    "IF": _if,
    "LEN": _len,
    "CONCAT": _concat,
}
