"""History sync background task.

Periodically copies new records from the fail2ban sqlite database into the
BanGUI application archive table to prevent gaps when fail2ban purges old rows.

Correlation IDs are propagated through the task using :mod:`app.utils.correlation`
so that task logs can be correlated across runs.
"""

from __future__ import annotations

import datetime
import uuid
from typing import TYPE_CHECKING

from app.utils.logging_compat import get_logger

from app.services import history_service
from app.tasks.db import task_db
from app.tasks.timeout_utils import run_with_timeout
from app.utils.correlation import get_correlation_id, reset_correlation_id, set_correlation_id
from app.utils.runtime_state import get_effective_settings

if TYPE_CHECKING:
    from fastapi import FastAPI

    from app.config import Settings

log = get_logger(__name__)

#: Stable APScheduler job id.
JOB_ID: str = "history_sync"

#: Interval in seconds between sync runs.
HISTORY_SYNC_INTERVAL: int = 300

#: Backfill window when archive is empty (seconds).
BACKFILL_WINDOW: int = 648000

#: Maximum seconds to allow for history sync to complete.
TASK_TIMEOUT_SECONDS: int = 60


async def _run_sync_with_settings(
    settings: Settings,
    correlation_id: str | None = None,
) -> None:
    """Run the history sync with correlation ID context."""
    if correlation_id is None:
        correlation_id = str(uuid.uuid4())

    token = set_correlation_id(correlation_id)
    try:
        await _do_sync_with_settings(settings)
    finally:
        reset_correlation_id(token)


async def _do_sync_with_settings(settings: Settings) -> None:
    """Inner sync logic that runs with correlation context set."""
    socket_path: str = settings.fail2ban_socket

    async def _do_sync() -> None:
        try:
            async with task_db(settings) as db:
                synced = await history_service.sync_from_fail2ban_db(db, socket_path)
            log.info("history_sync_complete", correlation_id=get_correlation_id(), synced=synced)
        except Exception:
            log.exception("history_sync_failed", correlation_id=get_correlation_id())

    await run_with_timeout("history_sync", _do_sync(), TASK_TIMEOUT_SECONDS)


async def _run_sync(app: FastAPI) -> None:
    await _run_sync_with_settings(get_effective_settings(app))


def register(app: FastAPI) -> None:
    """Register the history sync periodic job.

    Should be called after scheduler startup, from the lifespan handler.
    """
    settings = get_effective_settings(app)
    app.state.scheduler.add_job(
        _run_sync_with_settings,
        trigger="interval",
        seconds=HISTORY_SYNC_INTERVAL,
        kwargs={"settings": settings},
        id=JOB_ID,
        replace_existing=True,
        next_run_time=datetime.datetime.now(tz=datetime.UTC),
    )
    log.info("history_sync_scheduled", interval_seconds=HISTORY_SYNC_INTERVAL)
