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

Transfer session WebSocket push: fan-out to transfer_session subscribers (no job notifier dedup).
"""

from __future__ import annotations

import asyncio
from typing import Any

from fastapi import FastAPI

from mcp_proxy_adapter.core.job_push.events import (
    SUBSCRIPTION_TYPE_TRANSFER_SESSION,
    TRANSFER_WS_TERMINAL_EVENTS,
)
from mcp_proxy_adapter.core.job_push.transfer_correlation import (
    merge_transfer_correlation_envelope,
)
from mcp_proxy_adapter.core.job_push.subscriptions import (
    Subscriber,
    SubscriptionCategory,
)
from mcp_proxy_adapter.core.logging import get_global_logger

_transfer_push_app: FastAPI | None = None


def build_transfer_expired_ws_payload(
    *,
    transfer_id: str,
    direction: str,
    message: str | None = None,
) -> dict[str, Any]:
    """
    Shared payload for ``transfer_expired`` events (batch TTL and request-path).

    Matches ``_notify_transfer_expired`` / control-plane spec: error_type, message,
    progress_percent 0, direction from session.
    """
    from mcp_proxy_adapter.transfer.errors import TransferExpiredError

    tid = str(transfer_id).strip()
    msg = message
    if msg is None:
        msg = str(
            TransferExpiredError(
                message="Transfer session expired",
                transfer_id=tid or None,
            )
        )
    return {
        "direction": str(direction).strip(),
        "error_type": "TransferExpiredError",
        "message": msg,
        "progress_percent": 0,
    }


_terminal_transfer_ids: set[str] = set()
_terminal_lock = asyncio.Lock()


def coerce_ws_push_enabled(value: object) -> bool:
    """
    Coerce config values to bool for transfer.ws_push_enabled.

    Avoids ``bool("false") == True`` by handling common string/int forms.
    """
    if isinstance(value, bool):
        return value
    if isinstance(value, int) and not isinstance(value, bool):
        return value != 0
    if isinstance(value, str):
        s = value.strip().lower()
        if s in ("0", "false", "no", "off", ""):
            return False
        if s in ("1", "true", "yes", "on"):
            return True
    return bool(value)


def set_transfer_push_app(app: FastAPI | None) -> None:
    """Store FastAPI app for transfer WS fan-out; None clears."""
    global _transfer_push_app
    _transfer_push_app = app
    if app is None:
        _terminal_transfer_ids.clear()


def get_transfer_push_app() -> FastAPI | None:
    """Return app set by set_transfer_push_app."""
    return _transfer_push_app


async def notify_transfer_state_changed(
    transfer_id: str,
    event: str,
    payload: dict[str, Any] | None = None,
) -> None:
    """Push one transfer_session event to all registry subscribers for transfer_id."""
    tid = str(transfer_id).strip()
    if tid == "":
        raise ValueError("notify_transfer_state_changed requires non-empty transfer_id")
    from mcp_proxy_adapter.api.handlers import _merge_transfer_config

    cfg = _merge_transfer_config()
    if not coerce_ws_push_enabled(cfg.get("ws_push_enabled", True)):
        return
    app = get_transfer_push_app()
    if app is None:
        return
    registry = getattr(app.state, "job_push_registry", None)
    if registry is None:
        return
    if event in TRANSFER_WS_TERMINAL_EVENTS:
        async with _terminal_lock:
            if tid in _terminal_transfer_ids:
                return
            _terminal_transfer_ids.add(tid)
    extra = merge_transfer_correlation_envelope(tid, dict(payload) if payload else None)
    message: dict[str, Any] = dict(extra)
    message["event"] = str(event)
    message["subscription_type"] = SUBSCRIPTION_TYPE_TRANSFER_SESSION
    message["transfer_id"] = tid
    subs: list[Subscriber] = await registry.get_subscribers(
        tid, SubscriptionCategory.TRANSFER_SESSION
    )
    logger = get_global_logger()
    for sub in subs:
        try:
            await sub.send(message)
        except Exception as e:
            logger.warning("Transfer push send failed for transfer_id=%s: %s", tid, e)
