#!/usr/bin/env python
"""
Offline Event Queue System
Manages event queuing and replay for offline players.
"""

import json
import time
from typing import List, Dict, Any, Optional
from datetime import datetime, timedelta
import logging

logger = logging.getLogger(__name__)


class OfflineEventQueue:
    """
    Manages queuing of events that occur while a player is offline.
    Events are stored and replayed when the player reconnects.
    """

    def __init__(self, max_queue_size=1000, max_offline_hours=168):
        """
        Initialize the offline event queue.

        Args:
            max_queue_size: Maximum events to queue per player
            max_offline_hours: Maximum hours to keep offline events (default: 7 days)
        """
        self.max_queue_size = max_queue_size
        self.max_offline_hours = max_offline_hours
        self._queues = {}  # player_id -> list of events
        self._offline_since = {}  # player_id -> timestamp

    def mark_offline(self, player_id: str):
        """
        Mark a player as offline and start queuing events.

        Args:
            player_id: Player's unique identifier
        """
        if player_id not in self._offline_since:
            self._offline_since[player_id] = time.time()
            if player_id not in self._queues:
                self._queues[player_id] = []
            logger.info(f"Player {player_id} marked offline")

    def mark_online(self, player_id: str) -> List[Dict[str, Any]]:
        """
        Mark a player as online and return queued events.

        Args:
            player_id: Player's unique identifier

        Returns:
            List of queued events to replay
        """
        if player_id in self._offline_since:
            offline_duration = time.time() - self._offline_since[player_id]
            del self._offline_since[player_id]

            events = self._queues.get(player_id, [])
            if player_id in self._queues:
                del self._queues[player_id]

            logger.info(f"Player {player_id} back online after {offline_duration/3600:.1f} hours, replaying {len(events)} events")
            return events
        return []

    def queue_event(self, player_id: str, event: Dict[str, Any]) -> bool:
        """
        Queue an event for an offline player.

        Args:
            player_id: Player's unique identifier
            event: Event data to queue

        Returns:
            True if queued, False if player is online or queue full
        """
        # Only queue if player is marked offline
        if player_id not in self._offline_since:
            return False

        # Check if we've exceeded max offline time
        offline_duration = time.time() - self._offline_since[player_id]
        if offline_duration > self.max_offline_hours * 3600:
            logger.warning(f"Player {player_id} offline too long ({offline_duration/3600:.1f}h), discarding events")
            self.clear_queue(player_id)
            return False

        # Initialize queue if needed
        if player_id not in self._queues:
            self._queues[player_id] = []

        # Check queue size limit
        if len(self._queues[player_id]) >= self.max_queue_size:
            logger.warning(f"Queue full for player {player_id}, discarding oldest event")
            self._queues[player_id].pop(0)

        # Add event with timestamp
        event['queued_at'] = time.time()
        self._queues[player_id].append(event)
        logger.debug(f"Queued event for offline player {player_id}")
        return True

    def get_offline_duration(self, player_id: str) -> Optional[float]:
        """
        Get how long a player has been offline in seconds.

        Args:
            player_id: Player's unique identifier

        Returns:
            Offline duration in seconds, or None if online
        """
        if player_id in self._offline_since:
            return time.time() - self._offline_since[player_id]
        return None

    def clear_queue(self, player_id: str):
        """
        Clear all queued events for a player.

        Args:
            player_id: Player's unique identifier
        """
        if player_id in self._queues:
            del self._queues[player_id]
        logger.info(f"Cleared event queue for player {player_id}")

    def get_queue_size(self, player_id: str) -> int:
        """
        Get number of queued events for a player.

        Args:
            player_id: Player's unique identifier

        Returns:
            Number of queued events
        """
        return len(self._queues.get(player_id, []))

    def get_stats(self) -> Dict[str, Any]:
        """
        Get queue statistics.

        Returns:
            Dictionary with queue stats
        """
        return {
            'offline_players': len(self._offline_since),
            'total_queued_events': sum(len(q) for q in self._queues.values()),
            'max_queue_size': self.max_queue_size,
            'max_offline_hours': self.max_offline_hours
        }

    def cleanup_expired(self):
        """Remove queues for players offline longer than max_offline_hours."""
        current_time = time.time()
        max_offline_seconds = self.max_offline_hours * 3600

        expired = [
            player_id for player_id, offline_since in self._offline_since.items()
            if current_time - offline_since > max_offline_seconds
        ]

        for player_id in expired:
            logger.info(f"Removing expired queue for player {player_id}")
            del self._offline_since[player_id]
            if player_id in self._queues:
                del self._queues[player_id]

        return len(expired)


# Global queue instance
offline_queue = OfflineEventQueue(max_queue_size=1000, max_offline_hours=168)


def handle_player_disconnect(player_id: str):
    """
    Handle player disconnection - start queuing events.

    Args:
        player_id: Player's unique identifier
    """
    offline_queue.mark_offline(player_id)


def handle_player_reconnect(player_id: str) -> Dict[str, Any]:
    """
    Handle player reconnection - return queued events and stats.

    Args:
        player_id: Player's unique identifier

    Returns:
        Dictionary with offline duration and queued events
    """
    offline_duration = offline_queue.get_offline_duration(player_id)
    events = offline_queue.mark_online(player_id)

    return {
        'offline_duration_seconds': offline_duration,
        'offline_duration_hours': offline_duration / 3600 if offline_duration else 0,
        'queued_events': events,
        'event_count': len(events)
    }
