"""
Lifespan wiring for job push: create registry, register notifier listener, run poll loop.

Called from api/core/lifespan_manager.py when queue_manager is enabled and
job_push is enabled. Creates app.state.job_push_registry and starts a task
that polls subscribed job_ids and calls notify_job_state_changed on terminal state.

Author: Vasiliy Zdanovskiy
email: vasilyvz@gmail.com
"""

from __future__ import annotations

import asyncio
from typing import Any, Callable, Dict, Optional

from fastapi import FastAPI

from mcp_proxy_adapter.core.logging import get_global_logger
from mcp_proxy_adapter.core.job_push.events import build_push_message
from mcp_proxy_adapter.core.job_push.subscriptions import SubscriberRegistry
from mcp_proxy_adapter.core.job_push.notifier import register_notifier_listener
from mcp_proxy_adapter.core.job_push.hooks import run_job_push_handlers

JOB_PUSH_POLL_INTERVAL = 1.0
"""Seconds between poll loop iterations for subscribed job_ids."""


def _make_listener(app: FastAPI) -> Callable[..., Any]:
    """Build the notifier listener that sends to registry subscribers."""

    async def listener(
        job_id: str,
        status: str,
        result: Optional[Dict[str, Any]],
        error: Optional[str],
        progress: int,
        description: str,
    ) -> None:
        registry = getattr(app.state, "job_push_registry", None)
        if registry is None:
            return
        payload = build_push_message(
            job_id=job_id,
            status=status,
            result=result,
            error=error,
            progress=progress,
            description=description,
        )
        final = await run_job_push_handlers(job_id, payload)
        if final is None:
            return
        subscribers = await registry.get_subscribers(job_id)
        logger = get_global_logger()
        for sub in subscribers:
            try:
                await sub.send(final)
            except Exception as e:
                logger.warning("Job push send failed for job_id=%s: %s", job_id, e)

    return listener


async def _job_push_poll_loop(app: FastAPI) -> None:
    """Poll subscribed job_ids; on terminal state call notify_job_state_changed."""
    from mcp_proxy_adapter.integrations.queuemgr_integration import (
        get_global_queue_manager,
        QueueJobStatus,
    )

    logger = get_global_logger()
    last_status: Dict[str, str] = {}
    while True:
        await asyncio.sleep(JOB_PUSH_POLL_INTERVAL)
        registry = getattr(app.state, "job_push_registry", None)
        if registry is None:
            break
        try:
            queue_manager = await get_global_queue_manager()
        except Exception:
            continue
        job_ids = await registry.get_subscribed_job_ids()
        if not job_ids:
            continue
        for job_id in job_ids:
            try:
                job_result = await queue_manager.get_job_status(job_id)
            except Exception:
                continue
            status = job_result.status
            if status in (
                QueueJobStatus.COMPLETED,
                QueueJobStatus.FAILED,
                QueueJobStatus.STOPPED,
            ):
                prev = last_status.get(job_id)
                if prev == status:
                    continue
                last_status[job_id] = status
                from mcp_proxy_adapter.core.job_push.notifier import (
                    notify_job_state_changed,
                )

                await notify_job_state_changed(
                    job_id=job_id,
                    status=status,
                    result=job_result.result,
                    error=job_result.error,
                    progress=job_result.progress,
                    description=job_result.description or "",
                )
            else:
                last_status[job_id] = status
        await asyncio.sleep(0)

    logger.debug("Job push poll loop stopped")


async def startup_job_push(
    app: FastAPI, current_config: Optional[Dict[str, Any]] = None
) -> None:
    """
    Create SubscriberRegistry, set app.state.job_push_registry, register listener, start poll loop.

    Call from lifespan startup after queue_manager is initialized.
    Skips if job_push is disabled in config (job_push.enabled = false).
    """
    logger = get_global_logger()
    job_push_cfg = (current_config or {}).get("job_push") or {}
    if job_push_cfg.get("enabled") is False:
        logger.info("Job push is disabled in config, skipping")
        return
    registry = SubscriberRegistry()
    app.state.job_push_registry = registry
    listener = _make_listener(app)
    register_notifier_listener(listener)
    task = asyncio.create_task(_job_push_poll_loop(app))
    app.state._job_push_poll_task = task
    logger.info("Job push: registry and poll loop started")


async def shutdown_job_push(app: FastAPI) -> None:
    """Cancel poll loop task. Call from lifespan shutdown."""
    task = getattr(app.state, "_job_push_poll_task", None)
    if task is not None and not task.done():
        task.cancel()
        try:
            await task
        except asyncio.CancelledError:
            pass
        get_global_logger().info("Job push poll loop stopped")
