"""
Core types for the trigger system.
"""

import logging
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from enum import IntEnum
from typing import Any, Optional

logger = logging.getLogger(__name__)


class Priority(IntEnum):
    """
    Primary execution priority for triggers.

    Lower numeric value = higher priority (dequeued first).

    Priority hierarchy (highest to lowest):
    - DATA_SOURCE: Market data, real-time feeds (most time-sensitive)
    - TIMER: Scheduled tasks, cron jobs
    - USER_AGENT: User-agent interactions (WebSocket chat)
    - USER_DATA_REQUEST: User data requests (chart loads, symbol search)
    - SYSTEM: Background tasks, cleanup
    - LOW: Retries after conflicts, non-critical tasks
    """

    DATA_SOURCE = 0  # Market data updates, real-time feeds
    TIMER = 1  # Scheduled tasks, cron jobs
    USER_AGENT = 2  # User-agent interactions (WebSocket chat)
    USER_DATA_REQUEST = 3  # User data requests (chart loads, etc.)
    SYSTEM = 4  # Background tasks, cleanup, etc.
    LOW = 5  # Retries after conflicts, non-critical tasks


# Type alias for priority tuples
# Examples:
#   (Priority.DATA_SOURCE,)                    - Simple priority
#   (Priority.DATA_SOURCE, event_time)         - Priority + event time
#   (Priority.DATA_SOURCE, event_time, queue_seq) - Full ordering
#
# Python compares tuples element-by-element, left-to-right.
# Shorter tuple wins if all shared elements are equal.
PriorityTuple = tuple[int, ...]


class ExecutionState(IntEnum):
    """State of an execution in the system"""

    QUEUED = 0  # In queue, waiting to be dequeued
    EXECUTING = 1  # Currently executing
    WAITING_COMMIT = 2  # Finished executing, waiting for sequential commit
    COMMITTED = 3  # Successfully committed
    EVICTED = 4  # Evicted due to conflict, will retry
    FAILED = 5  # Failed with error


@dataclass
class CommitIntent:
    """
    Intent to commit changes to a store.

    Created during execution, validated and applied during sequential commit phase.
    """

    store_name: str
    """Name of the store to commit to"""

    expected_seq: int
    """The seq number of the snapshot that was read (for conflict detection)"""

    new_data: Any
    """The new data to commit (format depends on store backend)"""

    def __repr__(self) -> str:
        data_preview = str(self.new_data)[:50]
        return f"CommitIntent(store={self.store_name}, expected_seq={self.expected_seq}, data={data_preview}...)"


class Trigger(ABC):
    """
    Abstract base class for all triggers.

    A trigger represents a unit of work that:
    1. Gets assigned a seq number when dequeued
    2. Executes (potentially long-running, async)
    3. Returns CommitIntents for any state changes
    4. Waits for sequential commit
    """

    def __init__(
        self,
        name: str,
        priority: Priority = Priority.SYSTEM,
        priority_tuple: Optional[PriorityTuple] = None
    ):
        """
        Initialize trigger.

        Args:
            name: Descriptive name for logging
            priority: Simple priority (used if priority_tuple not provided)
            priority_tuple: Optional tuple for compound sorting
                Examples:
                    (Priority.DATA_SOURCE, event_time)
                    (Priority.USER_AGENT, message_timestamp)
                    (Priority.TIMER, scheduled_time)
        """
        self.name = name
        self.priority = priority
        self._priority_tuple = priority_tuple

    def get_priority_tuple(self, queue_seq: int) -> PriorityTuple:
        """
        Get the priority tuple for queue ordering.

        If a priority tuple was provided at construction, append queue_seq.
        Otherwise, create tuple from simple priority.

        Args:
            queue_seq: Queue insertion order (final sort key)

        Returns:
            Priority tuple for queue ordering

        Examples:
            (Priority.DATA_SOURCE,) + (queue_seq,) = (0, queue_seq)
            (Priority.DATA_SOURCE, 1000) + (queue_seq,) = (0, 1000, queue_seq)
        """
        if self._priority_tuple is not None:
            return self._priority_tuple + (queue_seq,)
        else:
            return (self.priority.value, queue_seq)

    @abstractmethod
    async def execute(self) -> list[CommitIntent]:
        """
        Execute the trigger logic.

        Can be long-running and async. Should read from stores via
        VersionedStore.read_snapshot() and return CommitIntents for any changes.

        Returns:
            List of CommitIntents (empty if no state changes)

        Raises:
            Exception: On execution failure (will be logged, no commit)
        """
        pass

    def __repr__(self) -> str:
        return f"{self.__class__.__name__}(name='{self.name}', priority={self.priority.name})"


@dataclass
class ExecutionRecord:
    """
    Record of an execution for tracking and debugging.

    Maintained by the CommitCoordinator to track in-flight executions.
    """

    seq: int
    trigger: Trigger
    state: ExecutionState
    commit_intents: Optional[list[CommitIntent]] = None
    error: Optional[str] = None
    retry_count: int = 0

    def __repr__(self) -> str:
        return (
            f"ExecutionRecord(seq={self.seq}, trigger={self.trigger.name}, "
            f"state={self.state.name}, retry={self.retry_count})"
        )
