"""
Event registry with condition-based indexing.

Replaces O(n) event scanning with O(1) filtered lookups.
"""

from typing import Callable, Optional, Tuple, List, Dict, Any
import logging

logger = logging.getLogger(__name__)


class EventRegistry:
    """
    Registry for game events with efficient filtering.

    Events can be registered with conditions:
    - age_range: (min_age, max_age)
    - requires_relationship: bool
    - requires_job: bool
    - day_of_year: int or range

    Usage:
        registry = EventRegistry()
        registry.register("birthday", birthday_event, age_range=(1, 120))

        applicable = registry.get_applicable_events(player)
        for event_info in applicable:
            result = event_info['func'](player, 'check')
    """

    def __init__(self):
        self._events: Dict[str, dict] = {}  # event_id -> {func, conditions}

        # Indexes for fast filtering
        self._by_age: Dict[Tuple[int, int], List[str]] = {}  # (min, max) -> [event_ids]
        self._no_conditions: List[str] = []  # Events with no conditions (always check)

    def register(
        self,
        event_id: str,
        func: Callable,
        age_range: Optional[Tuple[int, int]] = None,
        requires_relationship: bool = False,
        requires_job: bool = False,
        day_of_year: Optional[int] = None
    ) -> None:
        """
        Register an event with optional conditions.

        Args:
            event_id: Unique event identifier
            func: Event function(player, mode) -> event object
            age_range: (min_age, max_age) tuple
            requires_relationship: Event needs relationships
            requires_job: Event needs player to have a job
            day_of_year: Specific day of year (1-365)
        """
        if event_id in self._events:
            logger.debug(f"Event {event_id} already registered, skipping duplicate registration")
            return

        # Store event
        self._events[event_id] = {
            'id': event_id,
            'func': func,
            'age_range': age_range,
            'requires_relationship': requires_relationship,
            'requires_job': requires_job,
            'day_of_year': day_of_year,
        }

        # Index by conditions
        if age_range:
            if age_range not in self._by_age:
                self._by_age[age_range] = []
            self._by_age[age_range].append(event_id)

        # If no conditions, always check
        if not any([age_range, requires_relationship, requires_job, day_of_year]):
            self._no_conditions.append(event_id)

        logger.debug(f"Registered event: {event_id}")

    def get_applicable_events(self, player: Any) -> List[dict]:
        """
        Get events applicable to player's current state.

        Args:
            player: playerClass instance

        Returns:
            List of event info dicts: [{'id', 'func', ...}, ...]
        """
        applicable = []
        player_age = player.c.ageYears
        player_day = getattr(player, 'dayOfYear', None)
        has_relationships = len(getattr(player, 'r', [])) > 0
        has_job = bool(getattr(player.c, 'occupation', None))

        # Check age-based events
        for age_range, event_ids in self._by_age.items():
            min_age, max_age = age_range
            if min_age <= player_age <= max_age:
                for event_id in event_ids:
                    if event_id not in player.events:  # Skip already triggered
                        event_info = self._events[event_id]

                        # Check other conditions
                        if event_info['requires_relationship'] and not has_relationships:
                            continue
                        if event_info['requires_job'] and not has_job:
                            continue
                        if event_info['day_of_year'] and event_info['day_of_year'] != player_day:
                            continue

                        applicable.append(event_info)

        # Check unconditional events
        for event_id in self._no_conditions:
            if event_id not in player.events:
                applicable.append(self._events[event_id])

        return applicable

    def count(self) -> int:
        """Get number of registered events"""
        return len(self._events)

    def list_events(self) -> List[str]:
        """List all registered event IDs"""
        return list(self._events.keys())


# Global registry
_registry = EventRegistry()


def register_event(
    event_id: str,
    func: Callable,
    **conditions
) -> None:
    """Register an event in the global registry"""
    _registry.register(event_id, func, **conditions)


def get_applicable_events(player: Any) -> List[dict]:
    """Get applicable events from global registry"""
    return _registry.get_applicable_events(player)


def event_count() -> int:
    """Get total registered event count"""
    return _registry.count()
