#!/usr/bin/env python3
"""
RANK Executor
Company: eXonware.com
Author: eXonware Backend Team
Email: connect@exonware.com
Version: 0.0.1.8
"""

from typing import Any, Optional
from ..base import AUniversalOperationExecutor
from ....contracts import QueryAction, ExecutionContext, ExecutionResult
from ....defs import OperationType
from ..utils import extract_items, extract_field_value


class RankExecutor(AUniversalOperationExecutor):
    """RANK - SQL rank with gaps on ties."""
    OPERATION_NAME = "RANK"
    OPERATION_TYPE = OperationType.ADVANCED
    SUPPORTED_NODE_TYPES = []

    def _do_execute(self, action: QueryAction, context: ExecutionContext) -> ExecutionResult:
        params = action.params
        result_data = self._execute_rank(context.node, params, context)
        return ExecutionResult(
            success=True,
            data=result_data,
            action_type=self.OPERATION_NAME,
            metadata={"operation": self.OPERATION_NAME},
        )

    def _partition_key(self, item: Any, partition_by: Optional[list[str]]) -> Any:
        if not partition_by:
            return "__all__"
        return tuple(extract_field_value(item, field) for field in partition_by)

    def _order(self, items: list[Any], order_by: Any) -> tuple[list[Any], Optional[str]]:
        field = None
        direction = "asc"
        if isinstance(order_by, dict):
            field = order_by.get("field")
            direction = str(order_by.get("direction", "asc")).lower()
        elif isinstance(order_by, str):
            field = order_by
        reverse = direction == "desc"
        if field:
            return sorted(items, key=lambda i: extract_field_value(i, field), reverse=reverse), field
        return list(items), None

    def _execute_rank(self, node: Any, params: dict, context: ExecutionContext) -> dict:
        items = extract_items(node)
        partition_by = params.get("partition_by")
        order_by = params.get("order_by")
        as_field = params.get("as_field", "_rank")
        if partition_by and not isinstance(partition_by, list):
            partition_by = [partition_by]

        partitions: dict[Any, list[Any]] = {}
        for item in items:
            partitions.setdefault(self._partition_key(item, partition_by), []).append(item)

        output = []
        for _, part in partitions.items():
            ordered, field = self._order(part, order_by)
            prev_value = object()
            rank_value = 0
            for index, item in enumerate(ordered, start=1):
                current_value = extract_field_value(item, field) if field else item
                if index == 1 or current_value != prev_value:
                    rank_value = index
                    prev_value = current_value
                record = dict(item) if isinstance(item, dict) else {"value": item}
                record[as_field] = rank_value
                output.append(record)

        return {"items": output, "count": len(output), "as_field": as_field}
