"""Visitor implementations for sort specifications.

This module provides example visitors for converting sort specifications to
various formats and for analyzing them.
"""

from __future__ import annotations

from typing import Any

from therismos.sorting._sorting import SortCriterion, SortOrder, SortSpec


class StringVisitor:
    """Visitor that converts sort specifications to string representation.

    Example:
        >>> from therismos.sorting import SortSpec, SortCriterion, SortOrder
        >>> spec = SortSpec([
        ...     SortCriterion("age", SortOrder.ASCENDING),
        ...     SortCriterion("name", SortOrder.DESCENDING),
        ... ])
        >>> visitor = StringVisitor()
        >>> spec.accept(visitor)
        'age ASC, name DESC'
    """

    def visit_sort_criterion(self, criterion: SortCriterion) -> str:
        """Convert a single sort criterion to string.

        :param criterion: The sort criterion to visit.
        :type criterion: SortCriterion
        :returns: String representation of the criterion.
        :rtype: str
        """
        if criterion.order == SortOrder.ASCENDING:
            order_str = "ASC"
        elif criterion.order == SortOrder.DESCENDING:
            order_str = "DESC"
        else:  # SortOrder.NONE
            order_str = "NONE"

        return f"{criterion.field} {order_str}"

    def visit_sort_spec(self, spec: SortSpec) -> str:
        """Convert a sort specification to string.

        :param spec: The sort specification to visit.
        :type spec: SortSpec
        :returns: String representation of the specification.
        :rtype: str
        """
        if not spec:
            return ""

        parts = [criterion.accept(self) for criterion in spec]
        return ", ".join(parts)


class DictVisitor:
    """Visitor that converts sort specifications to dictionary representation.

    Example:
        >>> from therismos.sorting import SortSpec, SortCriterion, SortOrder
        >>> spec = SortSpec([
        ...     SortCriterion("age", SortOrder.ASCENDING),
        ...     SortCriterion("name", SortOrder.DESCENDING),
        ... ])
        >>> visitor = DictVisitor()
        >>> spec.accept(visitor)
        [{'field': 'age', 'order': 'ASC'}, {'field': 'name', 'order': 'DESC'}]
    """

    def visit_sort_criterion(self, criterion: SortCriterion) -> dict[str, Any]:
        """Convert a single sort criterion to dictionary.

        :param criterion: The sort criterion to visit.
        :type criterion: SortCriterion
        :returns: Dictionary representation of the criterion.
        :rtype: dict[str, Any]
        """
        if criterion.order == SortOrder.ASCENDING:
            order_str = "ASC"
        elif criterion.order == SortOrder.DESCENDING:
            order_str = "DESC"
        else:  # SortOrder.NONE
            order_str = "NONE"

        return {"field": criterion.field, "order": order_str}

    def visit_sort_spec(self, spec: SortSpec) -> list[dict[str, Any]]:
        """Convert a sort specification to list of dictionaries.

        :param spec: The sort specification to visit.
        :type spec: SortSpec
        :returns: List of dictionary representations for each criterion.
        :rtype: list[dict[str, Any]]
        """
        return [criterion.accept(self) for criterion in spec]


class FieldGathererVisitor:
    """Visitor that collects the names of all fields used in a sort specification.

    Example:
        >>> from therismos.sorting import SortSpec, SortCriterion, SortOrder
        >>> spec = SortSpec([
        ...     SortCriterion("age", SortOrder.ASCENDING),
        ...     SortCriterion("name", SortOrder.DESCENDING),
        ...     SortCriterion("age", SortOrder.DESCENDING),
        ... ])
        >>> visitor = FieldGathererVisitor()
        >>> spec.accept(visitor)
        >>> sorted(visitor.field_names)
        ['age', 'name']
    """

    def __init__(self) -> None:
        """Initialize the visitor with an empty set of field names."""
        self.field_names: set[str] = set()

    def visit_sort_criterion(self, criterion: SortCriterion) -> None:
        """Collect the field name from a sort criterion.

        :param criterion: The sort criterion to visit.
        :type criterion: SortCriterion
        """
        self.field_names.add(criterion.field)

    def visit_sort_spec(self, spec: SortSpec) -> None:
        """Collect field names from a sort specification.

        :param spec: The sort specification to visit.
        :type spec: SortSpec
        """
        for criterion in spec:
            criterion.accept(self)


__all__ = [
    "StringVisitor",
    "DictVisitor",
    "FieldGathererVisitor",
]
