"""Connectors router — governed connector catalogue and invocation."""
from __future__ import annotations

from typing import Any

from fastapi import APIRouter, HTTPException
from pydantic import BaseModel

from gsap_broker.connectors.base import ConnectorContext
from gsap_broker.connectors.registry import ConnectorRegistry
from gsap_broker.connectors.runtime import ConnectorRuntime
from gsap_broker.connectors.examples.echo_connector import EchoConnector
from gsap_broker.settings import settings

router = APIRouter()

# Module-level registry and runtime
_registry = ConnectorRegistry()
_runtime = ConnectorRuntime(registry=_registry)

# Register built-in connectors
_registry.register(EchoConnector())

# ── Credential resolver (shared by session connectors) ──────────
from gsap_broker.credentials.resolver import CredentialResolver

_credential_resolver = CredentialResolver()

if settings.credential_backend == "stub" or (
    settings.credential_backend == "auto" and not settings.entra_client_secret
):
    # Fix H-2: stub backend requires explicit opt-in
    if not settings.allow_stub_credentials:
        import logging as _logging
        _logging.getLogger(__name__).warning(
            "StubCredentialBackend would activate but ALLOW_STUB_CREDENTIALS is not set. "
            "Session connectors will have no credential backend."
        )
    else:
        from gsap_broker.credentials.stub_backend import StubCredentialBackend
        _credential_resolver.register(StubCredentialBackend())
elif settings.entra_client_secret:
    from gsap_broker.credentials.entra_backend import EntraCredentialBackend
    _credential_resolver.register(EntraCredentialBackend(
        tenant_id=settings.entra_tenant_id,
        client_id=settings.entra_client_id,
        client_secret=settings.entra_client_secret,
    ))

# ── Conditionally register connectors ───────────────────────────

# Intune (API-mediated — uses GraphClient, not CredentialResolver)
if settings.intune_enabled and settings.entra_client_secret:
    from gsap_broker.intune.graph_client import GraphClient
    from gsap_broker.intune.device_cache import DeviceComplianceCache
    from gsap_broker.connectors.intune import IntuneConnector

    _intune_graph = GraphClient(
        tenant_id=settings.entra_tenant_id,
        client_id=settings.entra_client_id,
        client_secret=settings.entra_client_secret,
    )
    _intune_cache = DeviceComplianceCache(ttl_seconds=settings.intune_compliance_cache_ttl)
    _intune_connector = IntuneConnector(graph_client=_intune_graph, cache=_intune_cache)
    _registry.register(_intune_connector)

# Bascule (session-based — uses CredentialResolver)
if settings.bascule_enabled:
    from gsap_broker.connectors.bascule import BasculeConnector
    _registry.register(BasculeConnector(credential_resolver=_credential_resolver))

# PowerShell (session-based — uses CredentialResolver)
if settings.powershell_enabled:
    from gsap_broker.connectors.powershell import PowerShellConnector
    _registry.register(PowerShellConnector(credential_resolver=_credential_resolver))

# Ansible (orchestrator — uses CredentialResolver)
if settings.ansible_enabled:
    from gsap_broker.connectors.ansible import AnsibleConnector
    _registry.register(AnsibleConnector(credential_resolver=_credential_resolver))


class InvokeRequest(BaseModel):
    operation: str
    parameters: dict[str, Any] = {}
    chronicle_session_id: str = ""
    gsap_context_id: str = ""
    pipeline_run_id: str = ""
    dag_id: str = ""
    # Fix C-6: caller must declare the AC's capability_mask
    capability_mask: int = 0
    principal_did: str = ""


class InvokeResponse(BaseModel):
    success: bool
    data: Any = None
    error: str | None = None
    lineage_cid: str = ""
    connector_id: str = ""
    operation: str = ""


@router.get("/")
async def catalogue() -> list[dict]:
    return _registry.catalogue()


@router.get("/{connector_id}/")
async def get_descriptor(connector_id: str) -> dict:
    connector = _registry.get(connector_id)
    if connector is None:
        raise HTTPException(status_code=404, detail=f"Connector not found: {connector_id}")
    return connector.descriptor()


@router.post("/{connector_id}/invoke/")
async def invoke_connector(connector_id: str, body: InvokeRequest) -> InvokeResponse:
    connector = _registry.get(connector_id)
    if connector is None:
        raise HTTPException(status_code=404, detail=f"Connector not found: {connector_id}")

    ctx = ConnectorContext(
        chronicle_session_id=body.chronicle_session_id,
        gsap_context_id=body.gsap_context_id,
        pipeline_run_id=body.pipeline_run_id,
        dag_id=body.dag_id,
        capability_mask=body.capability_mask,
        principal_did=body.principal_did,
    )
    result = await _runtime.invoke(connector_id, body.operation, body.parameters, ctx)
    return InvokeResponse(
        success=result.success,
        data=result.data,
        error=result.error,
        lineage_cid=result.lineage_cid,
        connector_id=connector_id,
        operation=body.operation,
    )


@router.get("/{connector_id}/health/")
async def health_check(connector_id: str) -> dict:
    connector = _registry.get(connector_id)
    if connector is None:
        raise HTTPException(status_code=404, detail=f"Connector not found: {connector_id}")
    healthy = connector.health_check()
    return {"connector_id": connector_id, "healthy": healthy}
