"""Pandas sort specification visitor for therismos sorting.

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

Requires Pandas to be installed. Install with: pip install therismos[pandas]

Example
-------
>>> from therismos.sorting import SortSpec, SortCriterion, SortOrder
>>> from therismos.sorting.visitors.pandas import PandasSortSpecVisitor
>>>
>>> spec = SortSpec([
...     SortCriterion("age", SortOrder.ASCENDING),
...     SortCriterion("name", SortOrder.DESCENDING),
... ])
>>>
>>> visitor = PandasSortSpecVisitor()
>>> sort = spec.accept(visitor)
>>> # sort.by == ("age", "name")
>>> # sort.ascending == (True, False)
>>>
>>> # Use with DataFrame
>>> # df.sort_values(by=list(sort.by), ascending=list(sort.ascending))

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

from __future__ import annotations

from dataclasses import dataclass

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

from therismos.sorting._sorting import SortOrder, SortSpec


@dataclass(frozen=True)
class PandasSortSpec:
    """Dataclass holding pandas sort parameters.

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

    Example
    -------
    >>> sort = PandasSortSpec(by=("age", "name"), ascending=(True, False))
    >>> df.sort_values(by=list(sort.by), ascending=list(sort.ascending))
    """

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


class PandasSortSpecVisitor:
    """Visitor that converts sort specifications to pandas sort parameters.

    This visitor traverses sort specifications and generates a PandasSortSpec
    dataclass that can be passed to DataFrame.sort_values().

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

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

        Criteria with SortOrder.NONE are skipped.

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

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

        return PandasSortSpec(by=tuple(by), ascending=tuple(ascending))


__all__ = [
    "PandasSortSpec",
    "PandasSortSpecVisitor",
]
