"""Scheduler jobs for background maintenance tasks.

- Reminder notifications: checks every 5 minutes for due event reminders.
- CalDAV pull sync: runs every hour for all users with CalDAV configured.
- Chat retention cleanup: runs daily, deleting old conversations per user setting.

Uses the same BackgroundScheduler pattern as briefing_scheduler.py.
"""
from __future__ import annotations

import asyncio
import logging
from datetime import datetime, timedelta, timezone

from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.interval import IntervalTrigger
from sqlalchemy import select

from fabledassistant.models import async_session
from fabledassistant.models.event import Event

logger = logging.getLogger(__name__)

_scheduler: BackgroundScheduler | None = None
_loop: asyncio.AbstractEventLoop | None = None


# ---------------------------------------------------------------------------
# Reminder job
# ---------------------------------------------------------------------------

async def _fire_reminders() -> None:
    """Find events with reminders due in the next 5 minutes and fire push notifications."""
    now = datetime.now(timezone.utc)
    window_end = now + timedelta(minutes=5)

    async with async_session() as session:
        result = await session.execute(
            select(Event).where(
                Event.reminder_minutes.isnot(None),
                Event.reminder_sent_at.is_(None),
                Event.start_dt > now,  # event hasn't started yet
                # reminder fires when now >= start_dt - reminder_minutes
                # i.e. start_dt <= now + reminder_minutes (approximated by window_end check)
            )
        )
        candidates = list(result.scalars().all())

    to_notify: list[Event] = []
    for event in candidates:
        reminder_dt = event.start_dt - timedelta(minutes=event.reminder_minutes)
        if reminder_dt <= window_end:
            to_notify.append(event)

    if not to_notify:
        return

    async with async_session() as session:
        for event in to_notify:
            try:
                from fabledassistant.services.push import send_push_notification  # noqa: PLC0415
                start_local = event.start_dt.strftime("%H:%M")
                await send_push_notification(
                    user_id=event.user_id,
                    title=f"Reminder: {event.title}",
                    body=f"Starting at {start_local} UTC",
                )
            except Exception:
                logger.warning("Failed to send reminder push for event %d", event.id, exc_info=True)

            # Mark as sent regardless of push success to avoid re-firing
            result = await session.execute(
                select(Event).where(Event.id == event.id)
            )
            ev = result.scalar_one_or_none()
            if ev:
                ev.reminder_sent_at = datetime.now(timezone.utc)
        await session.commit()


def _run_reminders(loop: asyncio.AbstractEventLoop) -> None:
    asyncio.run_coroutine_threadsafe(_fire_reminders(), loop)


# ---------------------------------------------------------------------------
# CalDAV pull sync job
# ---------------------------------------------------------------------------

async def _run_caldav_sync() -> None:
    from fabledassistant.services.caldav_sync import sync_all_users  # noqa: PLC0415
    try:
        await sync_all_users()
    except Exception:
        logger.warning("CalDAV pull sync job failed", exc_info=True)


def _run_caldav_sync_threadsafe(loop: asyncio.AbstractEventLoop) -> None:
    asyncio.run_coroutine_threadsafe(_run_caldav_sync(), loop)


# ---------------------------------------------------------------------------
# Chat retention cleanup job
# ---------------------------------------------------------------------------

async def _run_chat_retention_cleanup() -> None:
    """Delete old conversations for all users according to their retention setting."""
    from sqlalchemy import select as sa_select  # noqa: PLC0415

    from fabledassistant.models.user import User  # noqa: PLC0415
    from fabledassistant.services.chat import cleanup_old_conversations  # noqa: PLC0415
    from fabledassistant.services.settings import get_setting  # noqa: PLC0415

    async with async_session() as session:
        result = await session.execute(sa_select(User.id))
        user_ids = [row[0] for row in result.all()]

    total_deleted = 0
    for user_id in user_ids:
        try:
            retention_str = await get_setting(user_id, "chat_retention_days", "90")
            try:
                retention_days = int(retention_str)
            except (ValueError, TypeError):
                retention_days = 90
            if retention_days > 0:
                deleted = await cleanup_old_conversations(user_id, retention_days)
                total_deleted += deleted
        except Exception:
            logger.warning("Chat retention cleanup failed for user %d", user_id, exc_info=True)

    if total_deleted:
        logger.info("Chat retention cleanup: deleted %d conversation(s)", total_deleted)


def _run_chat_retention_threadsafe(loop: asyncio.AbstractEventLoop) -> None:
    asyncio.run_coroutine_threadsafe(_run_chat_retention_cleanup(), loop)


# ---------------------------------------------------------------------------
# Lifecycle
# ---------------------------------------------------------------------------

def start_event_scheduler(loop: asyncio.AbstractEventLoop) -> None:
    global _scheduler, _loop
    if _scheduler is not None:
        return
    _loop = loop
    _scheduler = BackgroundScheduler()

    # Check reminders every 5 minutes
    _scheduler.add_job(
        _run_reminders,
        trigger=IntervalTrigger(minutes=5),
        args=[loop],
        id="event_reminders",
        replace_existing=True,
    )

    # CalDAV pull sync every hour
    _scheduler.add_job(
        _run_caldav_sync_threadsafe,
        trigger=IntervalTrigger(hours=1),
        args=[loop],
        id="caldav_pull_sync",
        replace_existing=True,
    )

    # Chat retention cleanup once per day
    _scheduler.add_job(
        _run_chat_retention_threadsafe,
        trigger=IntervalTrigger(hours=24),
        args=[loop],
        id="chat_retention_cleanup",
        replace_existing=True,
    )

    _scheduler.start()
    logger.info("Event scheduler started (reminders every 5m, CalDAV sync every 1h, chat cleanup every 24h)")


def stop_event_scheduler() -> None:
    global _scheduler
    if _scheduler is not None:
        _scheduler.shutdown(wait=False)
        _scheduler = None
        logger.info("Event scheduler stopped")
