# src/svy/core/terms.py
from __future__ import annotations

from dataclasses import dataclass
from typing import Union


# A "Feature" can be a simple string (column name) or a Term object
Feature = Union[str, "Term"]


class Term:
    """Base class for all model terms."""

    pass


@dataclass(frozen=True)
class Cat(Term):
    """
    Explicitly treat a variable as categorical.

    Args:
        name: The column name in the dataframe.
        ref: (Optional) A specific level to drop (reference level).
             If None, all levels are usually kept (typical for calibration/raking).
    """

    name: str
    ref: str | int | float | None = None

    def __repr__(self) -> str:
        ref_str = f", ref={self.ref!r}" if self.ref is not None else ""
        return f"Cat('{self.name}'{ref_str})"


@dataclass(frozen=True)
class Cross(Term):
    """
    Create an interaction (crossing) between two variables.

    Args:
        left: The first variable (string name or Term object).
        right: The second variable (string name or Term object).
    """

    left: Feature
    right: Feature

    def __repr__(self) -> str:
        return f"Cross({self.left!r}, {self.right!r})"


@dataclass(frozen=True)
class RE(Term):
    """
    Random Effect term.
    Used to define the nesting structure (Area) for SAE.
    """

    name: str

    def __repr__(self) -> str:
        return f"RE('{self.name}')"
