"""Debounced auto-sync of Telegram bot commands.

When a CommandConfig, CommandTracker, or CommandTrackerListener changes,
the affected bot(s) are marked dirty. A periodic scheduler job checks
for bots that have been dirty longer than the debounce window and syncs
their commands with the Telegram API.

The manual "Sync with Telegram" button remains available and is unaffected.
"""

from __future__ import annotations

import logging
import time

from sqlmodel import select
from sqlmodel.ext.asyncio.session import AsyncSession

from ..database.engine import get_engine
from ..database.models import CommandConfig, CommandTracker, CommandTrackerListener, TelegramBot

_LOGGER = logging.getLogger(__name__)

# bot_id -> timestamp when it was first marked dirty
_dirty_bots: dict[int, float] = {}

# Seconds to wait after the last dirty mark before syncing
DEBOUNCE_SECONDS = 30


def mark_bot_dirty(bot_id: int) -> None:
    """Mark a bot as needing a command sync with Telegram."""
    if bot_id not in _dirty_bots:
        _dirty_bots[bot_id] = time.time()
        _LOGGER.debug("Marked bot %d dirty for command sync", bot_id)
    else:
        # Reset the debounce timer on each new change
        _dirty_bots[bot_id] = time.time()


async def mark_dirty_for_config(config_id: int) -> None:
    """Find all bots linked to a CommandConfig and mark them dirty.

    Chain: CommandConfig -> CommandTracker -> CommandTrackerListener -> TelegramBot
    """
    engine = get_engine()
    async with AsyncSession(engine) as session:
        # Find trackers using this config
        result = await session.exec(
            select(CommandTracker.id).where(CommandTracker.command_config_id == config_id)
        )
        tracker_ids = list(result.all())
        if not tracker_ids:
            return

        # Find telegram bot listeners for those trackers
        result = await session.exec(
            select(CommandTrackerListener.listener_id).where(
                CommandTrackerListener.command_tracker_id.in_(tracker_ids),
                CommandTrackerListener.listener_type == "telegram_bot",
            )
        )
        for bot_id in result.all():
            mark_bot_dirty(bot_id)


async def mark_dirty_for_tracker(tracker_id: int) -> None:
    """Find all bots linked to a CommandTracker and mark them dirty."""
    engine = get_engine()
    async with AsyncSession(engine) as session:
        result = await session.exec(
            select(CommandTrackerListener.listener_id).where(
                CommandTrackerListener.command_tracker_id == tracker_id,
                CommandTrackerListener.listener_type == "telegram_bot",
            )
        )
        for bot_id in result.all():
            mark_bot_dirty(bot_id)


async def _flush_dirty_bots() -> None:
    """Check for bots that have been dirty past the debounce window and sync them."""
    if not _dirty_bots:
        return

    now = time.time()
    ready = [bid for bid, ts in _dirty_bots.items() if now - ts >= DEBOUNCE_SECONDS]
    if not ready:
        return

    from ..commands.handler import register_commands_with_telegram

    engine = get_engine()
    for bot_id in ready:
        _dirty_bots.pop(bot_id, None)
        try:
            async with AsyncSession(engine) as session:
                bot = await session.get(TelegramBot, bot_id)
                if not bot:
                    continue
                # Snapshot every attribute we touch after the session
                # exits — once detached, lazy attribute access raises
                # MissingGreenlet under SQLAlchemy async.
                bot_username = bot.bot_username
                # Expunge so the detached instance can still read snapshotted
                # attrs but won't trigger a refresh / re-query downstream.
                session.expunge(bot)
            success = await register_commands_with_telegram(bot)
            if success:
                _LOGGER.info("Auto-synced commands for bot %d (@%s)", bot_id, bot_username)
            else:
                _LOGGER.warning("Auto-sync failed for bot %d", bot_id)
        except Exception:
            _LOGGER.error("Error auto-syncing commands for bot %d", bot_id, exc_info=True)


def start_sync_scheduler() -> None:
    """Register the periodic flush job with APScheduler."""
    from .scheduler import get_scheduler

    scheduler = get_scheduler()
    job_id = "command_sync_flush"
    if scheduler.get_job(job_id):
        return
    scheduler.add_job(
        _flush_dirty_bots,
        "interval",
        seconds=10,
        id=job_id,
        replace_existing=True,
        max_instances=1,
    )
    _LOGGER.info("Command auto-sync scheduler started (debounce=%ds)", DEBOUNCE_SECONDS)
