"""Internal routes for P2HS Cloud (Bearer CLOUD_INTERNAL_TOKEN)."""

from __future__ import annotations

from typing import Annotated
from uuid import UUID

from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy.ext.asyncio import AsyncSession

from src.core.database import get_db
from src.core.security.encryption import decrypt_value
from src.domain.cloud.dependencies import (
    get_cloud_internal_service,
    require_cloud_internal_token,
)
from src.domain.cloud.schemas import (
    AuthorizeRequest,
    AuthorizeResponse,
    CRMInboundEventsRequest,
    ConnectorCredentialsRequest,
    ConnectorCredentialsResponse,
    ConnectorCredentialsRotateRequest,
    StorageUsageAckResponse,
    StorageUsageReportRequest,
    SystemGrantRequest,
    UsageAckResponse,
    UsageFinalizeRequest,
    UsageReleaseRequest,
)
from src.domain.cloud.service import CloudInternalService
from src.domain.connectors.catalog import get_connector
from src.domain.connectors.repository import ConnectorRepository
from src.domain.integration_events.schemas import CommandAuthorizeRequest

router = APIRouter(
    prefix="/v1/cloud",
    tags=["cloud-internal"],
    dependencies=[Depends(require_cloud_internal_token)],
)


@router.post("/authorize", response_model=AuthorizeResponse)
async def cloud_authorize(
    body: AuthorizeRequest,
    svc: Annotated[CloudInternalService, Depends(get_cloud_internal_service)],
) -> AuthorizeResponse:
    return await svc.authorize(body)


@router.post("/connector-credentials", response_model=ConnectorCredentialsResponse)
async def cloud_connector_credentials(
    body: ConnectorCredentialsRequest,
    db: AsyncSession = Depends(get_db),
) -> ConnectorCredentialsResponse:
    """Release credentials for one already-authorized Cloud execution."""
    try:
        connection_id = UUID(body.connection_id)
    except ValueError as exc:
        raise HTTPException(status_code=400, detail="invalid_connector_identity") from exc

    repo = ConnectorRepository(db)
    row = await repo.get_connection(connection_id)
    if not row:
        raise HTTPException(status_code=404, detail="connector_connection_not_found")
    if row.status != "active":
        raise HTTPException(status_code=409, detail="connector_connection_unavailable")

    catalog = await repo.get_connector_by_id(row.connector_id)
    cdef = get_connector(catalog.key) if catalog else None
    if not cdef:
        raise HTTPException(status_code=404, detail="connector_not_found")
    if body.action and not any(action.slug == body.action for action in cdef.actions):
        raise HTTPException(status_code=404, detail="connector_action_not_found")
    if body.action:
        permission = await repo.get_permission(connection_id, body.action)
        if not permission or permission.mode == "deny":
            raise HTTPException(status_code=403, detail="connector_permission_denied")

    credentials = {
        key: decrypt_value(value) for key, value in (row.secrets_enc or {}).items() if value
    }
    if row.provider_connection_id and row.project_id:
        # Platform OAuth client secrets stay in the provider connection. Refresh
        # here so Cloud receives a live access token instead of the snapshot
        # taken when the connector was saved.
        from src.core.exceptions import ValidationError
        from src.domain.provider_auth.facades import execution_credentials

        try:
            fresh, _scopes = await execution_credentials(
                db,
                workspace_id=row.workspace_id,
                project_id=row.project_id,
                connection_id=row.provider_connection_id,
                connector_key=cdef.key,
            )
        except ValidationError as exc:
            raise HTTPException(
                status_code=409, detail=str(exc) or "reauthentication_required"
            ) from exc
        for key, value in fresh.items():
            if value and (
                key in cdef.secret_fields or key in {"access_token", "instance_url", "portal_id"}
            ):
                credentials[key] = value
    return ConnectorCredentialsResponse(
        connection_id=str(connection_id),
        connector_key=cdef.key,
        credentials=credentials,
    )


@router.post("/connector-credentials/rotate")
async def cloud_connector_credentials_rotate(
    body: ConnectorCredentialsRotateRequest,
    db: AsyncSession = Depends(get_db),
) -> dict[str, bool]:
    """Persist provider-refreshed credentials back into Core custody."""
    try:
        connection_id = UUID(body.connection_id)
    except ValueError as exc:
        raise HTTPException(status_code=400, detail="invalid_connector_identity") from exc
    repo = ConnectorRepository(db)
    row = await repo.get_connection(connection_id)
    if not row:
        raise HTTPException(status_code=404, detail="connector_connection_not_found")
    if row.status != "active":
        raise HTTPException(status_code=409, detail="connector_connection_unavailable")
    catalog = await repo.get_connector_by_id(row.connector_id)
    cdef = get_connector(catalog.key) if catalog else None
    if not cdef:
        raise HTTPException(status_code=404, detail="connector_not_found")
    unknown = set(body.credentials) - set(cdef.secret_fields)
    if unknown:
        raise HTTPException(status_code=400, detail="unknown_connector_secret")
    from src.core.security.encryption import encrypt_value

    row.secrets_enc = {
        **(row.secrets_enc or {}),
        **{key: encrypt_value(value) for key, value in body.credentials.items() if value},
    }
    await db.flush()
    return {"ok": True}


@router.post("/system-grant", response_model=AuthorizeResponse)
async def cloud_system_grant(
    body: SystemGrantRequest,
    svc: Annotated[CloudInternalService, Depends(get_cloud_internal_service)],
) -> AuthorizeResponse:
    """Grant for project function-token callers (generated-app staff BFF, edge functions,
    verified webhooks). No browser handshake. Core still decides allow-list, billing,
    email delivery, and connector execution."""
    return await svc.authorize_system_job(body)


@router.post("/authorize-command", response_model=AuthorizeResponse)
async def cloud_authorize_command(
    body: CommandAuthorizeRequest,
    svc: Annotated[CloudInternalService, Depends(get_cloud_internal_service)],
) -> AuthorizeResponse:
    return await svc.authorize_command(body)


class IntegrationCommandBody(BaseModel):
    grant_jwt: str
    command: str
    project_id: str
    command_id: str
    input: dict = {}


@router.post("/integration-commands")
async def cloud_execute_integration_command(
    body: IntegrationCommandBody,
    svc: Annotated[CloudInternalService, Depends(get_cloud_internal_service)],
) -> dict:
    return await svc.execute_granted_command(
        grant_jwt=body.grant_jwt,
        command=body.command,
        project_id=body.project_id,
        input_data=body.input,
        command_id=body.command_id,
    )


@router.post("/usage/finalize", response_model=UsageAckResponse)
async def cloud_usage_finalize(
    body: UsageFinalizeRequest,
    svc: Annotated[CloudInternalService, Depends(get_cloud_internal_service)],
) -> UsageAckResponse:
    out = await svc.usage_finalize(body)
    if out is None:
        raise HTTPException(status_code=401, detail="Invalid or missing authorization_id")
    return out


@router.post("/usage/release", response_model=UsageAckResponse)
async def cloud_usage_release(
    body: UsageReleaseRequest,
    svc: Annotated[CloudInternalService, Depends(get_cloud_internal_service)],
) -> UsageAckResponse:
    out = await svc.usage_release(body)
    if out is None:
        raise HTTPException(status_code=401, detail="Invalid or missing authorization_id")
    return out


@router.post("/usage/storage", response_model=StorageUsageAckResponse)
async def cloud_usage_storage(
    body: StorageUsageReportRequest,
    svc: Annotated[CloudInternalService, Depends(get_cloud_internal_service)],
) -> StorageUsageAckResponse:
    """Called by Cloud after every confirmed file upload or delete.

    Updates Core's running storage counter for quota enforcement and billing.
    """
    return await svc.usage_storage(body)


@router.post("/crm/inbound-events")
async def cloud_crm_inbound_events(
    body: CRMInboundEventsRequest,
    svc: Annotated[CloudInternalService, Depends(get_cloud_internal_service)],
) -> dict:
    """pc-webhooks → Core after provider verification. Not a public capability BFF."""
    from src.domain.connectors.crm_events import CRMEventError

    try:
        return await svc.ingest_crm_inbound(body)
    except CRMEventError as exc:
        status = {
            "crm_inbound_unverified": 401,
            "crm_connection_unmapped": 404,
            "crm_attribution_ambiguous": 409,
            "crm_event_identity_missing": 400,
        }.get(exc.code, 400)
        raise HTTPException(status_code=status, detail=exc.code) from exc
