"""Expressionator namespace — defines allowed names, builtins, and AST nodes.

Provides a safe evaluation environment by whitelisting math functions,
common builtins, and permissible AST node types.
"""

import ast
import math


# Math functions (sin, cos, sqrt, pi, etc.)
ALLOWED_NAMES = {k: v for k, v in math.__dict__.items() if not k.startswith("__")}

# Safe builtin functions
ALLOWED_BUILTINS = {
    "_getattr_": getattr,
    "_getiter_": iter,
    "max": max,
    "min": min,
    "abs": abs,
    "len": len,
    "sum": sum,
    "any": any,
    "all": all,
    "sorted": sorted,
    "round": round,
    "int": int,
    "float": float,
    "str": str,
    "bool": bool,
    "list": list,
    "tuple": tuple,
    "dict": dict,
    "range": range,
    "enumerate": enumerate,
    "zip": zip,
    "map": map,
    "filter": filter,
}

# Allowed AST node types for safe expression evaluation
ALLOWED_AST_NODES = {
    ast.Expression,
    ast.Module,
    # Literals
    ast.Constant,
    ast.Num,        # Deprecated but needed for older Python compat
    ast.Str,        # Deprecated but needed for older Python compat
    ast.FormattedValue,
    ast.JoinedStr,
    ast.List,
    ast.Tuple,
    ast.Set,
    ast.Dict,
    # Variables
    ast.Name,
    ast.Load,
    ast.Store,
    # Expressions
    ast.Expr,
    ast.UnaryOp,
    ast.UAdd,
    ast.USub,
    ast.Not,
    ast.BinOp,
    ast.Add,
    ast.Sub,
    ast.Mult,
    ast.Div,
    ast.FloorDiv,
    ast.Mod,
    ast.Pow,
    ast.BoolOp,
    ast.And,
    ast.Or,
    # Comparisons
    ast.Compare,
    ast.Eq,
    ast.NotEq,
    ast.Lt,
    ast.LtE,
    ast.Gt,
    ast.GtE,
    ast.Is,
    ast.IsNot,
    ast.In,
    ast.NotIn,
    # Function calls
    ast.Call,
    ast.keyword,
    # Attribute access
    ast.Attribute,
    # Subscripting
    ast.Subscript,
    ast.Index,
    ast.Slice,
    # Comprehensions
    ast.ListComp,
    ast.SetComp,
    ast.DictComp,
    ast.GeneratorExp,
    ast.comprehension,
    # Conditionals
    ast.IfExp,
    # Starred (for unpacking in function calls)
    ast.Starred,
}


class ExpressionatorException(Exception):
    """Raised when expression evaluation fails due to invalid syntax or disallowed constructs."""


class ExpressionatorNamespace:
    """Base class for defining custom safe namespaces.

    Subclass this and define methods prefixed with ``_expr_`` to make
    them available in expressions.

    Example::

        class MyNamespace(ExpressionatorNamespace):
            def _expr_clamp(self, value, low, high):
                return max(low, min(high, value))

        # Now ``clamp(x, 0, 100)`` is available in expressions.
    """

    name = "_expr_"

    @property
    def safe_expr_namespace(self):
        """Build a dict of custom functions from methods prefixed with ``_expr_``.

        Returns:
            Dict mapping function names (without prefix) to method references.
        """
        return {
            func_name.replace(self.name, ""): getattr(self, func_name)
            for func_name in dir(self)
            if func_name.startswith(self.name)
        }
