"""Core sorting module for therismos library.

This module provides classes for modeling sorting criteria as object structures.
"""

from __future__ import annotations

from dataclasses import dataclass
from enum import IntEnum
from typing import Generic, Protocol, TypeVar

T = TypeVar("T", covariant=True)


class SortOrder(IntEnum):
    """Enumeration of sort orders.

    :ivar NONE: No sorting (0).
    :ivar ASCENDING: Ascending order (1).
    :ivar DESCENDING: Descending order (-1).
    """

    NONE = 0
    ASCENDING = 1
    DESCENDING = -1


class SortCriterionVisitor(Protocol, Generic[T]):
    """Visitor interface for traversing sort criteria.

    Implementations should provide a visit method for sort criteria.
    The generic type parameter T specifies the return type of visit methods.
    """

    def visit_sort_criterion(self, criterion: SortCriterion) -> T:
        """Visit a sort criterion.

        :param criterion: The sort criterion to visit.
        :type criterion: SortCriterion
        :returns: The result of visiting the criterion.
        :rtype: T
        """
        ...


@dataclass(frozen=True)
class SortCriterion:
    """Represents a single sorting criterion.

    A sort criterion specifies a field to sort by and the order (ascending or descending).

    :ivar field: The field name to sort by.
    :vartype field: str
    :ivar order: The sort order.
    :vartype order: SortOrder
    """

    field: str
    order: SortOrder

    def __str__(self) -> str:
        """Return a string representation of the sort criterion.

        :returns: String representation in format "field ORDER".
        :rtype: str
        """
        if self.order == SortOrder.ASCENDING:
            order_str = "ASC"
        elif self.order == SortOrder.DESCENDING:
            order_str = "DESC"
        else:  # SortOrder.NONE
            order_str = "NONE"
        return f"{self.field} {order_str}"

    def accept(self, visitor: SortCriterionVisitor[T]) -> T:
        """Accept a visitor and dispatch to the appropriate visit method.

        :param visitor: The visitor to accept.
        :type visitor: SortCriterionVisitor[T]
        :returns: The result of the visitor's visit method.
        :rtype: T
        """
        return visitor.visit_sort_criterion(self)

    def __eq__(self, other: object) -> bool:
        """Check equality with another SortCriterion.

        :param other: The other object to compare with.
        :type other: object
        :returns: True if equal, False otherwise.
        :rtype: bool
        """
        if not isinstance(other, SortCriterion):
            return NotImplemented
        return self.field == other.field and self.order == other.order

    def __hash__(self) -> int:
        """Return hash of this SortCriterion.

        Custom hash is needed to match custom __eq__.

        :returns: Hash value.
        :rtype: int
        """
        return hash((self.field, self.order))


class SortSpec(list[SortCriterion]):
    """A list-like collection of sorting criteria with visitor support.

    This class extends the built-in list to provide additional functionality:
    - Visitor pattern support for converting to different formats
    - Integration with the sorting optimizer

    Example:
        >>> from therismos.sorting import SortSpec, SortCriterion, SortOrder
        >>> spec = SortSpec([
        ...     SortCriterion("age", SortOrder.ASCENDING),
        ...     SortCriterion("name", SortOrder.DESCENDING),
        ... ])
    """

    def __str__(self) -> str:
        """Return a string representation of the sort specification.

        :returns: String representation in format "field1 ORDER1, field2 ORDER2, ...".
        :rtype: str
        """
        if not self:
            return ""
        parts = [str(criterion) for criterion in self]
        return ", ".join(parts)

    def accept(self, visitor: SortSpecVisitor[T]) -> T:
        """Accept a visitor and dispatch to the appropriate visit method.

        :param visitor: The visitor to accept.
        :type visitor: SortSpecVisitor[T]
        :returns: The result of the visitor's visit method.
        :rtype: T
        """
        return visitor.visit_sort_spec(self)


class SortSpecVisitor(Protocol, Generic[T]):
    """Visitor interface for traversing sort specifications.

    Implementations should provide a visit method for sort specifications.
    The generic type parameter T specifies the return type of visit methods.
    """

    def visit_sort_spec(self, spec: SortSpec) -> T:
        """Visit a sort specification.

        :param spec: The sort specification to visit.
        :type spec: SortSpec
        :returns: The result of visiting the specification.
        :rtype: T
        """
        ...


__all__ = [
    "SortCriterion",
    "SortCriterionVisitor",
    "SortOrder",
    "SortSpec",
    "SortSpecVisitor",
]
