"""MongoDB sort specification visitor for therismos sorting.

This module provides a visitor that converts therismos sort specifications into
MongoDB sort documents compatible with PyMongo and Motor.

The MongoDB visitor does not require PyMongo or Motor to be installed for the
conversion itself - it simply returns dictionaries in MongoDB sort format.
However, you'll need one of these libraries to actually execute the queries:

- PyMongo: Synchronous MongoDB driver
- Motor: Asynchronous MongoDB driver for asyncio

Example
-------
>>> from therismos.sorting import SortSpec, SortCriterion, SortOrder
>>> from therismos.sorting.visitors.mongo import MongoVisitor
>>>
>>> spec = SortSpec([
...     SortCriterion("age", SortOrder.ASCENDING),
...     SortCriterion("name", SortOrder.DESCENDING),
... ])
>>>
>>> visitor = MongoVisitor()
>>> mongo_sort = spec.accept(visitor)
>>> # Result: {"age": 1, "name": -1}
>>>
>>> # Use with PyMongo
>>> # collection.find().sort(list(mongo_sort.items()))
>>> # or
>>> # collection.find(sort=list(mongo_sort.items()))
>>>
>>> # Use with Motor
>>> # await collection.find().sort(list(mongo_sort.items())).to_list(length=100)

Notes
-----
- ASCENDING (1) converts to MongoDB value 1
- DESCENDING (-1) converts to MongoDB value -1
- NONE (0) is skipped (not included in the result)
- The result is an ordered dictionary to preserve sort order
- Field type casting is not applied for sorting (unlike filter expressions)
"""

from __future__ import annotations

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


class MongoVisitor:
    """Visitor that converts sort specifications to MongoDB sort documents.

    This visitor traverses sort specifications and generates MongoDB sort
    dictionaries that can be used with PyMongo's sort() or Motor's sort()
    methods.

    Example
    -------
    >>> from therismos.sorting import SortSpec, SortCriterion, SortOrder
    >>> from therismos.sorting.visitors.mongo import MongoVisitor
    >>>
    >>> spec = SortSpec([
    ...     SortCriterion("age", SortOrder.DESCENDING),
    ...     SortCriterion("created_at", SortOrder.ASCENDING),
    ... ])
    >>>
    >>> visitor = MongoVisitor()
    >>> mongo_sort = spec.accept(visitor)
    >>> print(mongo_sort)
    {'age': -1, 'created_at': 1}
    >>>
    >>> # Use with PyMongo or Motor
    >>> # cursor = collection.find().sort(list(mongo_sort.items()))
    """

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

        :param criterion: The sort criterion to visit.
        :type criterion: SortCriterion
        :returns: Dictionary with field name and sort direction (1 or -1).
        :rtype: dict[str, int]
        """
        # Convert SortOrder to MongoDB sort value
        if criterion.order == SortOrder.NONE:
            # Skip NONE orders
            return {}

        mongo_value = int(criterion.order)  # ASCENDING=1, DESCENDING=-1
        return {criterion.field: mongo_value}

    def visit_sort_spec(self, spec: SortSpec) -> dict[str, int]:
        """Convert a sort specification to MongoDB format.

        :param spec: The sort specification to visit.
        :type spec: SortSpec
        :returns: Dictionary mapping field names to sort directions.
        :rtype: dict[str, int]
        """
        result: dict[str, int] = {}

        for criterion in spec:
            criterion_dict = criterion.accept(self)
            # Merge the criterion into the result
            # Later criteria override earlier ones (same as MongoDB behavior)
            result.update(criterion_dict)

        return result


__all__ = [
    "MongoVisitor",
]
