"""Schedule library backend for LoggerScheduler.

This module provides a backend implementation using the 'schedule' library,
a simple, lightweight job scheduling library for Python.

This uses multipledispatch with a tag-based dispatch pattern. Functions are
dispatched based on a ScheduleTag type rather than the actual argument types,
allowing the dispatch mechanism to work with any scheduler/logger types.

Install with: pip install schedule
"""

import schedule
import time
from typing import Dict
from multipledispatch import dispatch

from atmospyre.loggers import SensorLogger
from atmospyre.scheduler.logger_scheduler import (
    SchedulerDispatchTag,
    scheduler_dispatch_namespace
)


class ScheduleTag(SchedulerDispatchTag):
    """Tag class for schedule backend type dispatch.

    This class inherits from SchedulerDispatchTag and serves as a type marker
    for multipledispatch, allowing functions to be dispatched to the schedule
    backend without requiring specific types for all arguments.
    """
    pass


@dispatch(ScheduleTag, namespace=scheduler_dispatch_namespace)
def _create_scheduler(tag: ScheduleTag):
    """Create and return a schedule scheduler instance.

    Parameters
    ----------
    tag : ScheduleTag
        Tag for dispatch (not used in implementation).

    Returns
    -------
    schedule.Scheduler
        A new scheduler instance from the schedule library.
    """
    return schedule.Scheduler()


@dispatch(ScheduleTag, object, object, int, namespace=scheduler_dispatch_namespace)
def _add_logger(
    tag: ScheduleTag,
    scheduler_instance,
    logger: SensorLogger,
    interval_seconds: int
):
    """Add a logger to the schedule-based scheduler.

    Parameters
    ----------
    tag : ScheduleTag
        Tag for dispatch (not used in implementation).
    scheduler_instance : LoggerScheduler
        The scheduler instance to add the logger to.
    logger : SensorLogger
        The sensor logger to schedule.
    interval_seconds : int
        Interval in seconds between logger executions.

    Raises
    ------
    ValueError
        If the logger is already registered.
    """
    # Check if logger already exists
    if logger in scheduler_instance.loggers:
        raise ValueError(f"Logger {logger} is already registered")

    # Add logger to the list
    scheduler_instance.loggers.append(logger)

    # Schedule the logger's log method
    job = scheduler_instance.schedule.every(interval_seconds).seconds.do(logger.log)

    # Store job reference on logger for later removal
    logger._schedule_job = job

    if hasattr(scheduler_instance, 'verbose') and scheduler_instance.verbose:
        print(f"Added logger {logger} with interval {interval_seconds}s")


@dispatch(ScheduleTag, object, object, namespace=scheduler_dispatch_namespace)
def _remove_logger(
    tag: ScheduleTag,
    scheduler_instance,
    logger: SensorLogger
) -> bool:
    """Remove a logger from the schedule-based scheduler.

    Parameters
    ----------
    tag : ScheduleTag
        Tag for dispatch (not used in implementation).
    scheduler_instance : LoggerScheduler
        The scheduler instance to remove the logger from.
    logger : SensorLogger
        The logger instance to remove.

    Returns
    -------
    bool
        True if logger was found and removed, False otherwise.
    """
    if logger not in scheduler_instance.loggers:
        return False

    # Cancel the scheduled job
    if hasattr(logger, '_schedule_job'):
        scheduler_instance.schedule.cancel_job(logger._schedule_job)
        delattr(logger, '_schedule_job')

    # Remove from logger list
    scheduler_instance.loggers.remove(logger)

    if hasattr(scheduler_instance, 'verbose') and scheduler_instance.verbose:
        print(f"Removed logger {logger}")

    return True


@dispatch(ScheduleTag, object, namespace=scheduler_dispatch_namespace)
def _clear(tag: ScheduleTag, scheduler_instance):
    """Remove all loggers and clear the schedule.

    Parameters
    ----------
    tag : ScheduleTag
        Tag for dispatch (not used in implementation).
    scheduler_instance : LoggerScheduler
        The scheduler instance to clear.
    """
    # Clear all jobs from the schedule
    scheduler_instance.schedule.clear()

    # Clear job references from loggers
    for logger in scheduler_instance.loggers:
        if hasattr(logger, '_schedule_job'):
            delattr(logger, '_schedule_job')

    # Clear logger list
    scheduler_instance.loggers.clear()

    if hasattr(scheduler_instance, 'verbose') and scheduler_instance.verbose:
        print("Cleared all loggers from scheduler")


@dispatch(ScheduleTag, object, namespace=scheduler_dispatch_namespace)
def _get_status(tag: ScheduleTag, scheduler_instance) -> Dict:
    """Get current scheduler status.

    Parameters
    ----------
    tag : ScheduleTag
        Tag for dispatch (not used in implementation).
    scheduler_instance : LoggerScheduler
        The scheduler instance to get status from.

    Returns
    -------
    dict
        Dictionary containing backend name, logger counts, and details.
    """
    jobs = scheduler_instance.schedule.get_jobs()

    logger_details = []
    for logger in scheduler_instance.loggers:
        if hasattr(logger, '_schedule_job'):
            job = logger._schedule_job
            logger_details.append({
                'logger': str(logger),
                'interval': job.interval,
                'unit': job.unit,
                'next_run': str(job.next_run) if job.next_run else None
            })

    return {
        'backend': 'schedule',
        'total_loggers': len(scheduler_instance.loggers),
        'total_jobs': len(jobs),
        'logger_details': logger_details
    }


@dispatch(ScheduleTag, object, namespace=scheduler_dispatch_namespace)
def _run_pending(tag: ScheduleTag, scheduler_instance):
    """Run all pending jobs manually.

    Parameters
    ----------
    tag : ScheduleTag
        Tag for dispatch (not used in implementation).
    scheduler_instance : LoggerScheduler
        The scheduler instance to run pending jobs on.
    """
    scheduler_instance.schedule.run_pending()


@dispatch(ScheduleTag, object, namespace=scheduler_dispatch_namespace)
def _run(tag: ScheduleTag, scheduler_instance):
    """Start the scheduler in a blocking loop.

    This method blocks indefinitely, running scheduled jobs.
    Press Ctrl+C to stop.

    Parameters
    ----------
    tag : ScheduleTag
        Tag for dispatch (not used in implementation).
    scheduler_instance : LoggerScheduler
        The scheduler instance to run.
    """
    if hasattr(scheduler_instance, 'verbose') and scheduler_instance.verbose:
        print(f"Starting scheduler with {len(scheduler_instance.loggers)} logger(s)")
        print("Press Ctrl+C to stop")

    try:
        while True:
            scheduler_instance.schedule.run_pending()
            time.sleep(scheduler_instance.check_interval)
    except KeyboardInterrupt:
        if hasattr(scheduler_instance, 'verbose') and scheduler_instance.verbose:
            print("\nScheduler stopped by user")