"""Polars sort specification visitor for therismos sorting.

This module provides a visitor that converts therismos sort specifications into
Polars-compatible sort parameters via the PolarsSortSpec dataclass.

Requires Polars to be installed. Install with: pip install therismos[polars]

Example
-------
>>> from therismos.sorting import SortSpec, SortCriterion, SortOrder
>>> from therismos.sorting.visitors.polars import PolarsSortSpecVisitor
>>>
>>> spec = SortSpec([
...     SortCriterion("age", SortOrder.ASCENDING),
...     SortCriterion("name", SortOrder.DESCENDING),
... ])
>>>
>>> visitor = PolarsSortSpecVisitor()
>>> sort = spec.accept(visitor)
>>> # sort.by == ("age", "name")
>>> # sort.descending == (False, True)
>>>
>>> # Use with eager DataFrame
>>> # df.sort(by=list(sort.by), descending=list(sort.descending))
>>>
>>> # Use with lazy LazyFrame
>>> # lf.sort(by=list(sort.by), descending=list(sort.descending))

Notes
-----
- ASCENDING maps to descending=False, DESCENDING maps to descending=True
- SortOrder.NONE criteria are skipped and not included in the result
"""

from __future__ import annotations

from dataclasses import dataclass

try:
    import polars as pl  # noqa: F401
except ImportError as exc:
    msg = "polars is required. Install with: pip install therismos[polars]"
    raise ImportError(msg) from exc

from therismos.sorting._sorting import SortOrder, SortSpec


@dataclass(frozen=True)
class PolarsSortSpec:
    """Dataclass holding Polars sort parameters.

    :ivar by: Tuple of field names to sort by.
    :vartype by: tuple[str, ...]
    :ivar descending: Tuple of boolean flags indicating descending order per field.
    :vartype descending: tuple[bool, ...]

    Example
    -------
    >>> sort = PolarsSortSpec(by=("age", "name"), descending=(False, True))
    >>> df.sort(by=list(sort.by), descending=list(sort.descending))
    """

    by: tuple[str, ...]
    descending: tuple[bool, ...]


class PolarsSortSpecVisitor:
    """Visitor that converts sort specifications to Polars sort parameters.

    This visitor traverses sort specifications and generates a PolarsSortSpec
    dataclass that can be passed to DataFrame.sort() or LazyFrame.sort().

    Example
    -------
    >>> import polars as pl
    >>> from therismos.sorting import SortSpec, SortCriterion, SortOrder
    >>> from therismos.sorting.visitors.polars import PolarsSortSpecVisitor
    >>>
    >>> spec = SortSpec([
    ...     SortCriterion("age", SortOrder.DESCENDING),
    ...     SortCriterion("name", SortOrder.ASCENDING),
    ... ])
    >>>
    >>> visitor = PolarsSortSpecVisitor()
    >>> sort = spec.accept(visitor)
    >>> df = pl.DataFrame({"age": [30, 21], "name": ["Bob", "Alice"]})
    >>> df.sort(by=list(sort.by), descending=list(sort.descending))
    """

    def visit_sort_spec(self, spec: SortSpec) -> PolarsSortSpec:
        """Convert a sort specification to Polars sort parameters.

        Criteria with SortOrder.NONE are skipped.

        :param spec: The sort specification to visit.
        :type spec: SortSpec
        :returns: PolarsSortSpec with by and descending tuples.
        :rtype: PolarsSortSpec
        """
        by = []
        descending = []

        for criterion in spec:
            if criterion.order == SortOrder.NONE:
                continue
            by.append(criterion.field)
            descending.append(criterion.order == SortOrder.DESCENDING)

        return PolarsSortSpec(by=tuple(by), descending=tuple(descending))


__all__ = [
    "PolarsSortSpec",
    "PolarsSortSpecVisitor",
]
