"""
Background Task Scheduler for POS Service

Sets up periodic background jobs for:
- Syncing POS transactions to sales service
- Other maintenance tasks as needed

Uses Redis-based leader election to ensure only one pod runs scheduled tasks
when running with multiple replicas.

Usage in main.py:
```python
from app.scheduler import POSScheduler

# On startup
scheduler = POSScheduler(redis_url=settings.REDIS_URL)
await scheduler.start()

# On shutdown
await scheduler.stop()
```
"""

import structlog
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.interval import IntervalTrigger
from datetime import datetime
from typing import Optional

logger = structlog.get_logger()


class POSScheduler:
    """
    POS Scheduler service that manages background sync jobs.

    Uses Redis-based leader election to ensure only one pod runs
    scheduled jobs in a multi-replica deployment.
    """

    def __init__(self, redis_url: str = None, sync_interval_minutes: int = 5):
        """
        Initialize POS scheduler.

        Args:
            redis_url: Redis connection URL for leader election
            sync_interval_minutes: Interval for POS-to-sales sync job
        """
        self.scheduler = None
        self.sync_interval_minutes = sync_interval_minutes

        # Leader election
        self._redis_url = redis_url
        self._leader_election = None
        self._redis_manager = None
        self._redis_client = None
        self._scheduler_started = False

    async def start(self):
        """Start the POS scheduler with leader election"""
        if self._redis_url:
            await self._start_with_leader_election()
        else:
            # Fallback to standalone mode (for local development or single-pod deployments)
            logger.warning("Redis URL not provided, starting POS scheduler in standalone mode")
            await self._start_standalone()

    async def _start_with_leader_election(self):
        """Start with Redis-based leader election for horizontal scaling"""
        from shared.leader_election 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,
                service_name="pos-scheduler"
            )

            # 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("POS scheduler started with leader election",
                       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",
                        error=str(e))
            await self._start_standalone()

    async def _on_become_leader(self):
        """Called when this instance becomes the leader"""
        logger.info("POS scheduler became leader, starting scheduled jobs")
        await self._start_scheduler()

    async def _on_lose_leader(self):
        """Called when this instance loses leadership"""
        logger.warning("POS scheduler lost leadership, stopping scheduled jobs")
        await self._stop_scheduler()

    async def _start_scheduler(self):
        """Start the APScheduler with POS jobs"""
        if self._scheduler_started:
            logger.warning("POS scheduler already started")
            return

        try:
            # Import sync job
            from app.jobs.sync_pos_to_sales import run_pos_to_sales_sync

            # Job 1: Sync POS transactions to sales service
            self.scheduler.add_job(
                run_pos_to_sales_sync,
                trigger=IntervalTrigger(minutes=self.sync_interval_minutes),
                id='pos_to_sales_sync',
                name='Sync POS Transactions to Sales',
                replace_existing=True,
                max_instances=1,  # Prevent concurrent runs
                coalesce=True,  # Combine multiple missed runs into one
                misfire_grace_time=60  # Allow 60 seconds grace for missed runs
            )

            # Start scheduler
            if not self.scheduler.running:
                self.scheduler.start()
                self._scheduler_started = True
                logger.info("POS scheduler jobs started",
                           sync_interval_minutes=self.sync_interval_minutes,
                           job_count=len(self.scheduler.get_jobs()),
                           next_run=self.scheduler.get_jobs()[0].next_run_time if self.scheduler.get_jobs() else None)

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

    async def _stop_scheduler(self):
        """Stop the APScheduler"""
        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("POS scheduler jobs stopped")

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

    async def _start_standalone(self):
        """Start scheduler without leader election (fallback mode)"""
        logger.warning("Starting POS scheduler in standalone mode (no leader election)")

        self.scheduler = AsyncIOScheduler()

        try:
            # Import sync job
            from app.jobs.sync_pos_to_sales import run_pos_to_sales_sync

            self.scheduler.add_job(
                run_pos_to_sales_sync,
                trigger=IntervalTrigger(minutes=self.sync_interval_minutes),
                id='pos_to_sales_sync',
                name='Sync POS Transactions to Sales',
                replace_existing=True,
                max_instances=1,
                coalesce=True,
                misfire_grace_time=60
            )

            if not self.scheduler.running:
                self.scheduler.start()
                self._scheduler_started = True
                logger.info("POS scheduler started (standalone mode)",
                           sync_interval_minutes=self.sync_interval_minutes,
                           next_run=self.scheduler.get_jobs()[0].next_run_time if self.scheduler.get_jobs() else None)

        except Exception as e:
            logger.error("Failed to start POS scheduler in standalone mode", error=str(e))

    async def stop(self):
        """Stop the POS 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("POS scheduler stopped")

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

    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"}

    def get_scheduler_status(self) -> dict:
        """
        Get current scheduler status

        Returns:
            Dict with scheduler info and job statuses
        """
        if self.scheduler is None or not self._scheduler_started:
            return {
                "running": False,
                "is_leader": self.is_leader,
                "jobs": []
            }

        jobs = []
        for job in self.scheduler.get_jobs():
            jobs.append({
                "id": job.id,
                "name": job.name,
                "next_run": job.next_run_time.isoformat() if job.next_run_time else None,
                "trigger": str(job.trigger)
            })

        return {
            "running": True,
            "is_leader": self.is_leader,
            "jobs": jobs,
            "state": self.scheduler.state
        }

    def trigger_job_now(self, job_id: str) -> bool:
        """
        Manually trigger a scheduled job immediately

        Args:
            job_id: Job identifier (e.g., 'pos_to_sales_sync')

        Returns:
            True if job was triggered, False otherwise
        """
        if self.scheduler is None or not self._scheduler_started:
            logger.error("Cannot trigger job, scheduler not running")
            return False

        if not self.is_leader:
            logger.warning("Cannot trigger job, this instance is not the leader")
            return False

        try:
            job = self.scheduler.get_job(job_id)
            if job:
                self.scheduler.modify_job(job_id, next_run_time=datetime.now())
                logger.info("Job triggered manually", job_id=job_id)
                return True
            else:
                logger.warning("Job not found", job_id=job_id)
                return False

        except Exception as e:
            logger.error("Failed to trigger job", job_id=job_id, error=str(e))
            return False


# ================================================================
# Legacy compatibility functions (deprecated - use POSScheduler class)
# ================================================================

# Global scheduler instance for backward compatibility
_scheduler_instance: Optional[POSScheduler] = None


def start_scheduler():
    """
    DEPRECATED: Use POSScheduler class directly for better leader election support.

    Initialize and start the background scheduler (legacy function).
    """
    global _scheduler_instance

    if _scheduler_instance is not None:
        logger.warning("Scheduler already running")
        return

    logger.warning("Using deprecated start_scheduler function. "
                  "Consider migrating to POSScheduler class for leader election support.")

    try:
        _scheduler_instance = POSScheduler()
        # Note: This is synchronous fallback, no leader election
        import asyncio
        asyncio.create_task(_scheduler_instance._start_standalone())

    except Exception as e:
        logger.error("Failed to start scheduler", error=str(e), exc_info=True)
        _scheduler_instance = None


def shutdown_scheduler():
    """
    DEPRECATED: Use POSScheduler class directly.

    Gracefully shutdown the scheduler (legacy function).
    """
    global _scheduler_instance

    if _scheduler_instance is None:
        logger.warning("Scheduler not running")
        return

    try:
        import asyncio
        asyncio.create_task(_scheduler_instance.stop())
        _scheduler_instance = None

    except Exception as e:
        logger.error("Failed to shutdown scheduler", error=str(e), exc_info=True)


def get_scheduler_status():
    """
    DEPRECATED: Use POSScheduler class directly.

    Get current scheduler status (legacy function).
    """
    if _scheduler_instance is None:
        return {
            "running": False,
            "jobs": []
        }
    return _scheduler_instance.get_scheduler_status()


def trigger_job_now(job_id: str):
    """
    DEPRECATED: Use POSScheduler class directly.

    Manually trigger a scheduled job immediately (legacy function).
    """
    if _scheduler_instance is None:
        logger.error("Cannot trigger job, scheduler not running")
        return False
    return _scheduler_instance.trigger_job_now(job_id)
