from __future__ import annotations
import os
import logging
from datetime import datetime
from typing import Callable, Dict, Optional

from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.interval import IntervalTrigger
from apscheduler.triggers.cron import CronTrigger

log = logging.getLogger("govbot.scheduler")

_scheduler: Optional[BackgroundScheduler] = None


def _get_scheduler() -> BackgroundScheduler:
    global _scheduler
    if _scheduler is None:
        _scheduler = BackgroundScheduler(timezone=os.getenv("TZ") or "Asia/Seoul")
    return _scheduler


def start_scheduler():
    s = _get_scheduler()
    if not s.running:
        s.start()
        log.info("Scheduler started")


def shutdown_scheduler():
    global _scheduler
    if _scheduler and _scheduler.running:
        _scheduler.shutdown(wait=False)
        log.info("Scheduler stopped")


def upsert_interval_job(job_id: str, func: Callable, seconds: int, *, enabled: bool = True):
    s = _get_scheduler()
    # remove if exists
    try:
        s.remove_job(job_id)
    except Exception:
        pass
    if not enabled:
        log.info("Job %s disabled", job_id)
        return
    s.add_job(func, IntervalTrigger(seconds=max(10, int(seconds))), id=job_id, replace_existing=True)
    log.info("Job %s scheduled every %ss", job_id, seconds)


def upsert_daily_cron(job_id: str, func: Callable, hour: int, minute: int = 0, *, enabled: bool = True):
    s = _get_scheduler()
    try:
        s.remove_job(job_id)
    except Exception:
        pass
    if not enabled:
        log.info("Job %s disabled", job_id)
        return
    s.add_job(func, CronTrigger(hour=hour, minute=minute), id=job_id, replace_existing=True)
    log.info("Job %s scheduled daily at %02d:%02d", job_id, hour, minute)


def list_jobs() -> list[dict]:
    s = _get_scheduler()
    out = []
    for j in s.get_jobs():
        next_run = j.next_run_time.isoformat() if j.next_run_time else None
        out.append({"id": j.id, "next_run": next_run, "trigger": str(j.trigger)})
    return out

