"""``/admin/accounts/{org_id}/cloud/{cloud_kind}/*`` — guided cloud onboarding.

Five-step federated-first wizard surface (plan §"Shape of the wizard"):

1. ``POST .../bootstrap`` — emit trust-policy / federated-credential /
   WIF pool / scoped-token template + 1-click IaC link.
2. ``POST .../validate-identity`` — caller-identity probe.
3. ``POST .../validate-permissions`` — dry-run permission preview.
4. ``POST .../enumerate`` — list accounts / subscriptions / projects /
   zones / regions for downstream dropdowns.
5. ``POST .../connect`` — persist the resolved record.

Plus ``POST .../health`` (re-run identity probe + update row) and
``DELETE ...`` (drop local record) for parity with the existing
HF / Docker Hub integration routes.

The same routes serve the admin-tenant flow via the synthetic
``org_id='__platform__'`` value (see
:data:`alphaswarm_admin.providers.base.PLATFORM_ORG_ID`).

Audit + step-up posture (AGENTS rules 52, 26):

- Read-only steps (``bootstrap``, ``validate-identity``,
  ``validate-permissions``, ``enumerate``) gate on the ``admin:cluster``
  scope and require step-up MFA but the audit row is light (no
  resource mutation).
- Mutating steps (``connect`` / ``disconnect`` / ``health``) write the
  ``pending`` row BEFORE the provider call and ``succeeded|failed``
  AFTER. Token / federated-credential JSON / scoped-API-token bytes
  NEVER appear in the audit payload — the providers only emit
  metadata.
"""
from __future__ import annotations

import logging
from typing import Any, Literal

from fastapi import APIRouter, Depends, Header, HTTPException, status
from pydantic import BaseModel, Field

from alphaswarm_admin.deps.audit import AuditContext, audit_context_dep
from alphaswarm_admin.deps.identity import AdminUser
from alphaswarm_admin.deps.stepup import require_admin_step_up
from alphaswarm_admin.providers.base import (
    IntegrationKind,
    IntegrationProviderError,
    IntegrationRecord,
)
from alphaswarm_admin.services.account_integration_service import (
    get_account_integration_service,
)

logger = logging.getLogger(__name__)


router = APIRouter(
    prefix="/admin/accounts/{org_id}/cloud",
    tags=["cloud-onboarding"],
)


# ---------------------------------------------------------------------------
# Wire types — kept narrow because the cloud-specific shape lives in
# the provider; the route just forwards a body dict and surfaces the
# structured response.
# ---------------------------------------------------------------------------


CloudKind = Literal["cloud_aws", "cloud_azure", "cloud_gcp", "cloud_cloudflare"]


class CloudOnboardingBody(BaseModel):
    """Free-form per-cloud body. Validated by the provider."""

    model_config = {"extra": "allow"}


def _bearer_from_header(header_value: str | None) -> str | None:
    if not header_value or not header_value.lower().startswith("bearer "):
        return None
    return header_value.split(None, 1)[1].strip()


def _resolve_kind(cloud_kind: str) -> IntegrationKind:
    try:
        kind = IntegrationKind(cloud_kind)
    except ValueError as exc:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail={
                "error": "unknown_cloud_kind",
                "error_description": (
                    f"unknown cloud kind {cloud_kind!r}; "
                    "valid values: cloud_aws, cloud_azure, cloud_gcp, cloud_cloudflare"
                ),
            },
        ) from exc
    if kind not in {
        IntegrationKind.CLOUD_AWS,
        IntegrationKind.CLOUD_AZURE,
        IntegrationKind.CLOUD_GCP,
        IntegrationKind.CLOUD_CLOUDFLARE,
    }:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail={
                "error": "not_a_cloud_kind",
                "error_description": (
                    f"{cloud_kind!r} is an integration kind but not a cloud kind"
                ),
            },
        )
    return kind


def _provider_error_to_http(exc: IntegrationProviderError) -> HTTPException:
    return HTTPException(
        status_code=exc.status_code or status.HTTP_502_BAD_GATEWAY,
        detail={"error": exc.code, "error_description": str(exc)},
    )


def _record_payload(record: IntegrationRecord) -> dict[str, Any]:
    return record.to_dict()


# ---------------------------------------------------------------------------
# Step 1 — Bootstrap artifacts (read-only, no AlphaSwarm mutation)
# ---------------------------------------------------------------------------


@router.post(
    "/{cloud_kind}/bootstrap",
    summary="Generate trust policy / federated credential / WIF / scoped-token templates.",
)
async def bootstrap(
    org_id: str,
    cloud_kind: str,
    body: CloudOnboardingBody,
    user: AdminUser = Depends(require_admin_step_up("admin:cluster")),
) -> dict[str, Any]:
    kind = _resolve_kind(cloud_kind)
    service = get_account_integration_service()
    try:
        artifacts = await service.bootstrap_artifacts(kind, org_id, body.model_dump())
    except IntegrationProviderError as exc:
        raise _provider_error_to_http(exc) from exc
    return {"org_id": org_id, **artifacts.to_dict()}


# ---------------------------------------------------------------------------
# Step 2 — Identity probe (read-only against cloud; audit row written)
# ---------------------------------------------------------------------------


@router.post(
    "/{cloud_kind}/validate-identity",
    summary="Caller-identity probe (read-only).",
)
async def validate_identity(
    org_id: str,
    cloud_kind: str,
    body: CloudOnboardingBody,
    user: AdminUser = Depends(require_admin_step_up("admin:cluster")),
    audit: AuditContext = Depends(
        audit_context_dep("admin.cloud_onboarding.validate_identity")
    ),
) -> dict[str, Any]:
    kind = _resolve_kind(cloud_kind)
    audit.target = f"{org_id}/{kind.value}"
    audit.start(
        payload={
            "org_id": org_id,
            "cloud_kind": kind.value,
            "step_name": "validate_identity",
        }
    )
    service = get_account_integration_service()
    try:
        probe = await service.validate_identity(kind, org_id, body.model_dump())
    except IntegrationProviderError as exc:
        audit.fail(f"{exc.code}: {exc}")
        raise _provider_error_to_http(exc) from exc
    audit.succeed(
        {
            "ok": probe.ok,
            "caller_arn": probe.identity.get("caller_arn"),
            "subscription_id": probe.identity.get("subscription_id"),
            "project_id": probe.identity.get("project_id"),
            "token_id": probe.identity.get("token_id"),
        }
    )
    return {"org_id": org_id, **probe.to_dict(), "audit_run_id": audit.run_id}


# ---------------------------------------------------------------------------
# Step 3 — Permission preview
# ---------------------------------------------------------------------------


@router.post(
    "/{cloud_kind}/validate-permissions",
    summary="Dry-run permission preview against the candidate principal.",
)
async def validate_permissions(
    org_id: str,
    cloud_kind: str,
    body: CloudOnboardingBody,
    user: AdminUser = Depends(require_admin_step_up("admin:cluster")),
    audit: AuditContext = Depends(
        audit_context_dep("admin.cloud_onboarding.validate_permissions")
    ),
) -> dict[str, Any]:
    kind = _resolve_kind(cloud_kind)
    audit.target = f"{org_id}/{kind.value}"
    audit.start(
        payload={
            "org_id": org_id,
            "cloud_kind": kind.value,
            "step_name": "validate_permissions",
        }
    )
    service = get_account_integration_service()
    try:
        preview = await service.validate_permissions(kind, org_id, body.model_dump())
    except IntegrationProviderError as exc:
        audit.fail(f"{exc.code}: {exc}")
        raise _provider_error_to_http(exc) from exc
    audit.succeed(
        {
            "ok": preview.ok,
            "allowed_count": len(preview.allowed),
            "denied_count": len(preview.denied),
            "missing_required_count": len(preview.missing_required),
        }
    )
    return {"org_id": org_id, **preview.to_dict(), "audit_run_id": audit.run_id}


# ---------------------------------------------------------------------------
# Step 4 — Enumerate resources (read-only)
# ---------------------------------------------------------------------------


@router.post(
    "/{cloud_kind}/enumerate",
    summary="List accounts / subscriptions / projects / zones / regions.",
)
async def enumerate_resources(
    org_id: str,
    cloud_kind: str,
    body: CloudOnboardingBody,
    user: AdminUser = Depends(require_admin_step_up("admin:cluster")),
    audit: AuditContext = Depends(
        audit_context_dep("admin.cloud_onboarding.enumerate")
    ),
) -> dict[str, Any]:
    kind = _resolve_kind(cloud_kind)
    audit.target = f"{org_id}/{kind.value}"
    audit.start(
        payload={
            "org_id": org_id,
            "cloud_kind": kind.value,
            "step_name": "enumerate",
        }
    )
    service = get_account_integration_service()
    try:
        result = await service.enumerate_resources(kind, org_id, body.model_dump())
    except IntegrationProviderError as exc:
        audit.fail(f"{exc.code}: {exc}")
        raise _provider_error_to_http(exc) from exc
    audit.succeed(
        {
            "ok": result.error is None,
            "resource_counts": {k: len(v) for k, v in result.resources.items()},
        }
    )
    return {"org_id": org_id, **result.to_dict(), "audit_run_id": audit.run_id}


# ---------------------------------------------------------------------------
# Step 5 — Connect (mutates AlphaSwarm state, audit-first)
# ---------------------------------------------------------------------------


@router.post(
    "/{cloud_kind}/connect",
    summary="Persist a cloud account record after validation succeeded.",
)
async def connect(
    org_id: str,
    cloud_kind: str,
    body: CloudOnboardingBody,
    user: AdminUser = Depends(require_admin_step_up("admin:cluster")),
    audit: AuditContext = Depends(
        audit_context_dep("admin.cloud_onboarding.connect")
    ),
    authorization: str | None = Header(default=None, alias="Authorization"),
) -> dict[str, Any]:
    kind = _resolve_kind(cloud_kind)
    audit.target = f"{org_id}/{kind.value}"
    audit.start(
        payload={
            "org_id": org_id,
            "cloud_kind": kind.value,
            "step_name": "connect",
        }
    )
    service = get_account_integration_service()
    try:
        record = await service.connect(
            kind,
            org_id,
            body.model_dump(),
            bearer=_bearer_from_header(authorization),
        )
    except IntegrationProviderError as exc:
        audit.fail(f"{exc.code}: {exc}")
        raise _provider_error_to_http(exc) from exc
    audit.succeed(
        {
            "namespace": record.namespace,
            "credential_key": record.credential_key,
            "auth_method": str(record.metadata.get("auth_method") or ""),
        }
    )
    return {"integration": _record_payload(record), "audit_run_id": audit.run_id}


# ---------------------------------------------------------------------------
# Health re-check (parity with existing integrations router)
# ---------------------------------------------------------------------------


@router.post(
    "/{cloud_kind}/health",
    summary="Re-run the identity probe against the persisted cloud record.",
)
async def cloud_health(
    org_id: str,
    cloud_kind: str,
    user: AdminUser = Depends(require_admin_step_up("admin:cluster")),
    audit: AuditContext = Depends(
        audit_context_dep("admin.cloud_onboarding.health")
    ),
) -> dict[str, Any]:
    kind = _resolve_kind(cloud_kind)
    audit.target = f"{org_id}/{kind.value}"
    audit.start(payload={"org_id": org_id, "cloud_kind": kind.value})
    service = get_account_integration_service()
    try:
        health = await service.health(kind, org_id)
    except IntegrationProviderError as exc:
        audit.fail(f"{exc.code}: {exc}")
        raise _provider_error_to_http(exc) from exc
    if health is None:
        audit.succeed({"status": "not_connected"})
        return {"status": "not_connected", "audit_run_id": audit.run_id}
    audit.succeed({"status": health.status.value})
    return {
        "status": health.status.value,
        "checked_at": health.checked_at.isoformat(),
        "error": health.error,
        "metadata": dict(health.metadata),
        "audit_run_id": audit.run_id,
    }


# ---------------------------------------------------------------------------
# Disconnect
# ---------------------------------------------------------------------------


@router.delete(
    "/{cloud_kind}",
    summary="Disconnect a cloud account (local record only).",
    description=(
        "Drops the local record. Vendor-side cleanup (AWS role, Entra "
        "federated credential, GCP WIF pool, Cloudflare scoped token) "
        "is the operator's responsibility per the runbook."
    ),
)
async def disconnect(
    org_id: str,
    cloud_kind: str,
    user: AdminUser = Depends(require_admin_step_up("admin:cluster")),
    audit: AuditContext = Depends(
        audit_context_dep("admin.cloud_onboarding.disconnect")
    ),
    authorization: str | None = Header(default=None, alias="Authorization"),
) -> dict[str, Any]:
    kind = _resolve_kind(cloud_kind)
    audit.target = f"{org_id}/{kind.value}"
    audit.start(payload={"org_id": org_id, "cloud_kind": kind.value})
    service = get_account_integration_service()
    try:
        removed = await service.disconnect(
            kind,
            org_id,
            bearer=_bearer_from_header(authorization),
        )
    except IntegrationProviderError as exc:
        audit.fail(f"{exc.code}: {exc}")
        raise _provider_error_to_http(exc) from exc
    audit.succeed({"removed": removed})
    return {"removed": removed, "audit_run_id": audit.run_id}


__all__ = ["router"]
