"""
Scheduler Leader Mixin

Provides a mixin class for services that use APScheduler and need
leader election for horizontal scaling.

Usage:
    class MySchedulerService(SchedulerLeaderMixin):
        def __init__(self, redis_url: str, service_name: str):
            super().__init__(redis_url, service_name)
            # Your initialization here

        async def _create_scheduler_jobs(self):
            '''Override to define your scheduled jobs'''
            self.scheduler.add_job(
                self.my_job,
                trigger=CronTrigger(hour=0),
                id='my_job'
            )

        async def my_job(self):
            # Your job logic here
            pass
"""

import asyncio
from typing import Optional
from abc import abstractmethod
import structlog

logger = structlog.get_logger()


class SchedulerLeaderMixin:
    """
    Mixin for services that use APScheduler with leader election.

    Provides automatic leader election and scheduler management.
    Only the leader pod will run scheduled jobs.
    """

    def __init__(self, redis_url: str, service_name: str, **kwargs):
        """
        Initialize the scheduler with leader election.

        Args:
            redis_url: Redis connection URL for leader election
            service_name: Unique service name for leader election lock
            **kwargs: Additional arguments passed to parent class
        """
        super().__init__(**kwargs)

        self._redis_url = redis_url
        self._service_name = service_name
        self._leader_election = None
        self._redis_manager = None
        self._redis_client = None
        self.scheduler = None
        self._scheduler_started = False

    async def start_with_leader_election(self):
        """
        Start the service with leader election.

        Only the leader will start the scheduler.
        """
        from apscheduler.schedulers.asyncio import AsyncIOScheduler
        from shared.leader_election.service import LeaderElectionService
        from shared.redis_utils import RedisConnectionManager

        try:
            # Create Redis connection using shared manager (handles SSL, pooling, health checks)
            self._redis_manager = await RedisConnectionManager.create(
                self._redis_url, decode_responses=False
            )
            self._redis_client = self._redis_manager.get_client()

            # Create scheduler (but don't start it yet)
            self.scheduler = AsyncIOScheduler()

            # Create leader election
            self._leader_election = LeaderElectionService(
                self._redis_client,
                self._service_name
            )

            # Start leader election with callbacks
            await self._leader_election.start(
                on_become_leader=self._on_become_leader,
                on_lose_leader=self._on_lose_leader
            )

            logger.info("Scheduler service started with leader election",
                       service=self._service_name,
                       is_leader=self._leader_election.is_leader,
                       instance_id=self._leader_election.instance_id)

        except Exception as e:
            logger.error("Failed to start with leader election, falling back to standalone",
                        service=self._service_name,
                        error=str(e))
            # Fallback: start scheduler anyway (for single-pod deployments)
            await self._start_scheduler_standalone()

    async def _on_become_leader(self):
        """Called when this instance becomes the leader"""
        logger.info("Became leader, starting scheduler",
                   service=self._service_name)
        await self._start_scheduler()

    async def _on_lose_leader(self):
        """Called when this instance loses leadership"""
        logger.warning("Lost leadership, stopping scheduler",
                      service=self._service_name)
        await self._stop_scheduler()

    async def _start_scheduler(self):
        """Start the scheduler with defined jobs"""
        if self._scheduler_started:
            logger.warning("Scheduler already started",
                         service=self._service_name)
            return

        try:
            # Let subclass define jobs
            await self._create_scheduler_jobs()

            # Start scheduler
            if not self.scheduler.running:
                self.scheduler.start()
                self._scheduler_started = True
                logger.info("Scheduler started",
                           service=self._service_name,
                           job_count=len(self.scheduler.get_jobs()))

        except Exception as e:
            logger.error("Failed to start scheduler",
                        service=self._service_name,
                        error=str(e))

    async def _stop_scheduler(self):
        """Stop the scheduler"""
        if not self._scheduler_started:
            return

        try:
            if self.scheduler and self.scheduler.running:
                self.scheduler.shutdown(wait=False)
                self._scheduler_started = False
                logger.info("Scheduler stopped",
                           service=self._service_name)

        except Exception as e:
            logger.error("Failed to stop scheduler",
                        service=self._service_name,
                        error=str(e))

    async def _start_scheduler_standalone(self):
        """Start scheduler without leader election (fallback mode)"""
        from apscheduler.schedulers.asyncio import AsyncIOScheduler

        logger.warning("Starting scheduler in standalone mode (no leader election)",
                      service=self._service_name)

        self.scheduler = AsyncIOScheduler()
        await self._create_scheduler_jobs()

        if not self.scheduler.running:
            self.scheduler.start()
            self._scheduler_started = True

    @abstractmethod
    async def _create_scheduler_jobs(self):
        """
        Override to define scheduled jobs.

        Example:
            self.scheduler.add_job(
                self.my_task,
                trigger=CronTrigger(hour=0, minute=30),
                id='my_task',
                max_instances=1
            )
        """
        pass

    async def stop(self):
        """Stop the scheduler and leader election"""
        # Stop leader election
        if self._leader_election:
            await self._leader_election.stop()

        # Stop scheduler
        await self._stop_scheduler()

        # Close Redis manager (handles client and pool cleanup)
        if self._redis_manager:
            await self._redis_manager.close()

        logger.info("Scheduler service stopped",
                   service=self._service_name)

    @property
    def is_leader(self) -> bool:
        """Check if this instance is the leader"""
        return self._leader_election.is_leader if self._leader_election else False

    def get_leader_status(self) -> dict:
        """Get leader election status"""
        if self._leader_election:
            return self._leader_election.get_status()
        return {"is_leader": True, "mode": "standalone"}
