"""
Server-side match timer component for synchronized countdown across all clients
"""

import time
import logging
import threading
from typing import Dict, Any, Optional
from datetime import datetime, timedelta

from .thread_manager import ThreadedComponent
from .message_bus import MessageBus, Message, MessageType, MessageBuilder
from ..database.manager import DatabaseManager
from ..config.manager import ConfigManager

logger = logging.getLogger(__name__)


class MatchTimerComponent(ThreadedComponent):
    """Server-side match timer that synchronizes countdown across all clients"""

    def __init__(self, message_bus: MessageBus, db_manager: DatabaseManager,
                 config_manager: ConfigManager):
        super().__init__("match_timer", message_bus)
        self.db_manager = db_manager
        self.config_manager = config_manager

        # Timer state
        self.timer_running = False
        self.timer_start_time: Optional[float] = None
        self.timer_duration_seconds = 0
        self.current_fixture_id: Optional[str] = None
        self.current_match_id: Optional[int] = None

        # Synchronization
        self._timer_lock = threading.RLock()
        self._last_update = time.time()

        # Register component with message bus
        self.message_bus.register_component(self.name)

        # Register message handlers
        self.message_bus.subscribe(self.name, MessageType.START_GAME, self._handle_start_game)
        self.message_bus.subscribe(self.name, MessageType.SCHEDULE_GAMES, self._handle_schedule_games)
        self.message_bus.subscribe(self.name, MessageType.CUSTOM, self._handle_custom_message)

        logger.info("MatchTimer component initialized")

    def initialize(self) -> bool:
        """Initialize the match timer component"""
        try:
            logger.info("MatchTimer component initialized successfully")
            return True
        except Exception as e:
            logger.error(f"Failed to initialize MatchTimer: {e}")
            return False

    def run(self):
        """Main timer loop"""
        logger.info("MatchTimer component started")

        while self.running:
            try:
                # Process any pending messages first
                message = self.message_bus.get_message(self.name, timeout=0.1)
                if message:
                    self._process_message(message)

                current_time = time.time()

                # Check if timer needs to be updated
                if self.timer_running and self.timer_start_time:
                    elapsed = current_time - self.timer_start_time
                    remaining = self.timer_duration_seconds - elapsed

                    if remaining <= 0:
                        # Timer reached zero, start next match
                        self._on_timer_expired()
                    else:
                        # Send periodic timer updates (every 1 second)
                        if current_time - self._last_update >= 1.0:
                            self._send_timer_update()
                            self._last_update = current_time

                # Check if we should stop the timer (no more matches)
                if self.timer_running and self._should_stop_timer():
                    self._stop_timer()
                    logger.info("Timer stopped - no more matches to process")

                # Update heartbeat for health monitoring
                self.heartbeat()

                # Sleep for a short interval
                time.sleep(0.1)

            except Exception as e:
                logger.error(f"MatchTimer run loop error: {e}")
                time.sleep(1.0)

        logger.info("MatchTimer component stopped")

    def _process_message(self, message):
        """Process incoming messages directly"""
        try:
            logger.debug(f"MatchTimer processing message: {message}")

            # Handle messages directly since some messages don't trigger subscription handlers
            if message.type == MessageType.START_GAME:
                self._handle_start_game(message)
            elif message.type == MessageType.SCHEDULE_GAMES:
                self._handle_schedule_games(message)
            elif message.type == MessageType.CUSTOM:
                self._handle_custom_message(message)

        except Exception as e:
            logger.error(f"Failed to process message: {e}")

    def shutdown(self):
        """Shutdown the match timer"""
        with self._timer_lock:
            self.timer_running = False
            self.timer_start_time = None
            self.current_fixture_id = None
            self.current_match_id = None

        # Unregister from message bus
        self.message_bus.unregister_component(self.name)

        logger.info("MatchTimer component shutdown")

    def get_timer_state(self) -> Dict[str, Any]:
        """Get current timer state for API responses"""
        with self._timer_lock:
            if not self.timer_running or not self.timer_start_time:
                return {
                    "running": False,
                    "remaining_seconds": 0,
                    "total_seconds": 0,
                    "fixture_id": None,
                    "match_id": None,
                    "start_time": None
                }

            elapsed = time.time() - self.timer_start_time
            remaining = max(0, self.timer_duration_seconds - elapsed)

            return {
                "running": True,
                "remaining_seconds": int(remaining),
                "total_seconds": self.timer_duration_seconds,
                "fixture_id": self.current_fixture_id,
                "match_id": self.current_match_id,
                "start_time": self.timer_start_time,
                "elapsed_seconds": int(elapsed)
            }

    def _handle_start_game(self, message: Message):
        """Handle START_GAME message"""
        try:
            fixture_id = message.data.get("fixture_id")

            logger.info(f"Received START_GAME message for fixture: {fixture_id}")

            # Get match interval from configuration
            match_interval = self._get_match_interval()

            # Start the timer
            self._start_timer(match_interval * 60, fixture_id)

        except Exception as e:
            logger.error(f"Failed to handle START_GAME message: {e}")

    def _handle_schedule_games(self, message: Message):
        """Handle SCHEDULE_GAMES message"""
        try:
            logger.info("Received SCHEDULE_GAMES message")

            # Get match interval from configuration
            match_interval = self._get_match_interval()

            # Start the timer without specific fixture (will find first available)
            self._start_timer(match_interval * 60, None)

        except Exception as e:
            logger.error(f"Failed to handle SCHEDULE_GAMES message: {e}")

    def _handle_custom_message(self, message: Message):
        """Handle custom messages (like timer state requests)"""
        try:
            request = message.data.get("request")

            if request == "get_timer_state":
                # Send timer state response
                timer_state = self.get_timer_state()

                response = Message(
                    type=MessageType.CUSTOM,
                    sender=self.name,
                    recipient=message.sender,
                    data={
                        "response": "timer_state",
                        "timer_state": timer_state,
                        "timestamp": time.time()
                    },
                    correlation_id=message.correlation_id
                )
                self.message_bus.publish(response)

            elif request == "stop":
                # Stop the timer
                self._stop_timer()

                response = Message(
                    type=MessageType.CUSTOM,
                    sender=self.name,
                    recipient=message.sender,
                    data={
                        "response": "timer_stopped",
                        "timestamp": time.time()
                    },
                    correlation_id=message.correlation_id
                )
                self.message_bus.publish(response)

        except Exception as e:
            logger.error(f"Failed to handle custom message: {e}")

    def _start_timer(self, duration_seconds: int, fixture_id: Optional[str]):
        """Start the countdown timer"""
        with self._timer_lock:
            self.timer_duration_seconds = duration_seconds
            self.timer_start_time = time.time()
            self.timer_running = True
            self.current_fixture_id = fixture_id

            logger.info(f"Match timer started: {duration_seconds}s for fixture {fixture_id}")

            # Send timer started notification
            self._send_timer_update()

    def _stop_timer(self):
        """Stop the countdown timer"""
        with self._timer_lock:
            self.timer_running = False
            self.timer_start_time = None
            self.current_fixture_id = None
            self.current_match_id = None

            logger.info("Match timer stopped")

            # Send timer stopped notification
            self._send_timer_update()

    def _on_timer_expired(self):
        """Handle timer expiration - start next match"""
        try:
            logger.info("Match timer expired, starting next match...")

            # Find and start the next match
            match_info = self._find_and_start_next_match()

            if match_info:
                logger.info(f"Started match {match_info['match_id']} in fixture {match_info['fixture_id']}")

                # Reset timer for next interval
                match_interval = self._get_match_interval()
                self._start_timer(match_interval * 60, match_info['fixture_id'])
            else:
                logger.info("No more matches to start, stopping timer")
                self._stop_timer()

        except Exception as e:
            logger.error(f"Failed to handle timer expiration: {e}")
            # Reset timer on error
            match_interval = self._get_match_interval()
            self._start_timer(match_interval * 60, self.current_fixture_id)

    def _find_and_start_next_match(self) -> Optional[Dict[str, Any]]:
        """Find and start the next available match"""
        try:
            session = self.db_manager.get_session()
            try:
                from ..database.models import MatchModel

                # Priority 1: Use current fixture if specified
                target_fixture_id = None
                target_match = None

                if self.current_fixture_id:
                    # Find next match in current fixture
                    matches = session.query(MatchModel).filter_by(
                        fixture_id=self.current_fixture_id
                    ).order_by(MatchModel.match_number.asc()).all()

                    target_match = self._find_next_match_in_list(matches)

                if not target_match:
                    # Priority 2: Find matches in today's fixtures
                    today = datetime.now().date()
                    today_matches = session.query(MatchModel).filter(
                        MatchModel.start_time >= datetime.combine(today, datetime.min.time()),
                        MatchModel.start_time < datetime.combine(today, datetime.max.time())
                    ).order_by(MatchModel.created_at.asc()).all()

                    if today_matches:
                        # Group by fixture and find first available match
                        fixtures = {}
                        for match in today_matches:
                            if match.fixture_id not in fixtures:
                                fixtures[match.fixture_id] = []
                            fixtures[match.fixture_id].append(match)

                        # Try each fixture
                        for fixture_id, matches in fixtures.items():
                            target_match = self._find_next_match_in_list(matches)
                            if target_match:
                                target_fixture_id = fixture_id
                                break

                if not target_match:
                    # Priority 3: Find any pending matches
                    all_matches = session.query(MatchModel).filter_by(
                        status='pending'
                    ).order_by(MatchModel.created_at.asc()).all()

                    if all_matches:
                        target_match = all_matches[0]
                        target_fixture_id = target_match.fixture_id

                if target_match:
                    # Send MATCH_START message
                    match_start_message = MessageBuilder.match_start(
                        sender=self.name,
                        fixture_id=target_fixture_id or target_match.fixture_id,
                        match_id=target_match.id
                    )

                    self.message_bus.publish(match_start_message)

                    return {
                        "fixture_id": target_fixture_id or target_match.fixture_id,
                        "match_id": target_match.id,
                        "match_number": target_match.match_number
                    }

                return None

            finally:
                session.close()

        except Exception as e:
            logger.error(f"Failed to find and start next match: {e}")
            return None

    def _find_next_match_in_list(self, matches: list) -> Optional[Any]:
        """Find the next match to start from a list of matches"""
        # Priority order: bet -> scheduled -> pending
        for status in ['bet', 'scheduled', 'pending']:
            for match in matches:
                if match.status == status:
                    return match
        return None

    def _should_stop_timer(self) -> bool:
        """Check if timer should be stopped (no more matches)"""
        try:
            session = self.db_manager.get_session()
            try:
                from ..database.models import MatchModel

                # Count matches that can be started
                active_matches = session.query(MatchModel).filter(
                    MatchModel.status.in_(['bet', 'scheduled', 'pending'])
                ).count()

                return active_matches == 0

            finally:
                session.close()

        except Exception as e:
            logger.error(f"Failed to check if timer should stop: {e}")
            return False

    def _get_match_interval(self) -> int:
        """Get match interval from configuration"""
        try:
            if self.config_manager:
                general_config = self.config_manager.get_section_config("general") or {}
                return general_config.get('match_interval', 20)
            else:
                return 20  # Default fallback
        except Exception as e:
            logger.error(f"Failed to get match interval: {e}")
            return 20

    def _send_timer_update(self):
        """Send timer update message to all clients"""
        try:
            timer_state = self.get_timer_state()

            update_message = Message(
                type=MessageType.CUSTOM,  # Use custom type for timer updates
                sender=self.name,
                data={
                    "timer_update": timer_state,
                    "timestamp": time.time()
                }
            )

            # Send to web dashboard for broadcasting to clients
            update_message.recipient = "web_dashboard"
            self.message_bus.publish(update_message)

        except Exception as e:
            logger.error(f"Failed to send timer update: {e}")