"""Drop a subscription provider's 9Router lane and report only what actually happened.

Deleting is the easy half; honesty is the hard half. 9Router answers a bad id with a 404 and a JSON
error body, so a caller that ignores status codes reports "disconnected" for a lane that is still
live, and the user reconnects on top of a stale row. So success here is verified, never inferred:
`ok` means the provider has no connection row left, confirmed by re-reading 9Router.
"""

import logging
from typing import Dict, List, Optional

import httpx
from pydantic import BaseModel, ConfigDict
from typeguard import typechecked

from backend.apps.nine_router.process import NINE_ROUTER_API, cli_auth_headers, is_running
from backend.apps.nine_router.subscription_health import invalidate_health_cache

logger = logging.getLogger(__name__)

# gemini-cli and antigravity are two Google OAuth lanes; the registry prefers AG, so dropping gemini-cli must drop the stale AG row too or it 400s. One-directional: AG operations MUST NOT cascade back.
PROVIDER_CASCADE_REMOVES: Dict[str, List[str]] = {
    "gemini-cli": ["antigravity"],
}

P_TIMEOUT_S = 10.0


class SubscriptionDisconnectResult(BaseModel):
    """What the user may be told: `ok` only once the lane is verifiably empty."""

    model_config = ConfigDict(validate_assignment=True)
    ok: bool
    removed: int = 0
    error: str = ""


@typechecked
def sync_settings_state() -> None:
    """Push the settings snapshot to the cloud state sync, exactly as connecting does. Imported late: service.client reaches back into this package."""
    from backend.apps.service.client import sync
    from backend.apps.settings.settings import load_settings
    sync(load_settings().model_dump())


@typechecked
async def p_list_connections() -> Optional[List[Dict]]:
    """Every 9Router connection row, or None when the router can't be read.

    Deliberately not `nine_router.get_providers`, which folds an unreachable router into an empty
    list: here "I couldn't look" and "nothing is connected" must not be the same answer.
    """
    try:
        async with httpx.AsyncClient(timeout=P_TIMEOUT_S, headers=cli_auth_headers()) as client:
            r = await client.get(f"{NINE_ROUTER_API}/providers")
            if r.status_code != 200:
                return None
            data = r.json()
            if not isinstance(data, dict):
                return None
            return data.get("connections") or []
    except Exception as e:
        logger.debug(f"9Router provider list failed: {e}")
        return None


@typechecked
async def delete_provider_connections(providers: List[str]) -> int:
    """Delete every 9Router connection belonging to `providers`; returns how many really went."""
    connections = await p_list_connections()
    if connections is None:
        return 0
    removed = 0
    async with httpx.AsyncClient(timeout=P_TIMEOUT_S, headers=cli_auth_headers()) as client:
        for c in connections:
            if c.get("provider") not in providers or not c.get("id"):
                continue
            try:
                r = await client.delete(f"{NINE_ROUTER_API}/providers/{c['id']}")
                if r.status_code < 400:
                    removed += 1
                else:
                    logger.warning(f"9Router refused to drop the {c.get('provider')} connection: HTTP {r.status_code}")
            except Exception as e:
                logger.warning(f"9Router delete failed for {c.get('provider')}: {e}")
    return removed


@typechecked
async def disconnect_subscription(provider: str) -> SubscriptionDisconnectResult:
    """Clear `provider` (and its cascade lanes) from 9Router, confirming the lane is gone."""
    if not is_running():
        return SubscriptionDisconnectResult(ok=False, error="The subscription service isn't running.")

    targets = [provider, *PROVIDER_CASCADE_REMOVES.get(provider, [])]
    removed = await delete_provider_connections(targets)

    remaining = await p_list_connections()
    if remaining is None:
        return SubscriptionDisconnectResult(
            ok=False, removed=removed, error="Couldn't confirm the disconnect. Please try again.",
        )
    if any(c.get("provider") in targets for c in remaining):
        return SubscriptionDisconnectResult(
            ok=False, removed=removed, error="The subscription service kept the connection. Please try again.",
        )

    sync_settings_state()
    # A deliberate disconnect makes the cached boot verdict a lie: it would nag "reconnect" about the lane the user just dropped.
    invalidate_health_cache()
    return SubscriptionDisconnectResult(ok=True, removed=removed)
