"""
FastAPI RBAC guard dependencies for portal API endpoints.

Usage pattern:
    @router.get("/tenants/{tenant_id}/agents")
    async def list_agents(
        tenant_id: UUID,
        caller: PortalCaller = Depends(get_portal_caller),
        session: AsyncSession = Depends(get_session),
    ) -> ...:
        await require_tenant_member(tenant_id, caller, session)
        ...

Headers consumed (set by portal frontend / gateway middleware):
    X-Portal-User-Id   — UUID of the authenticated portal user
    X-Portal-User-Role — Role string (platform_admin | customer_admin | customer_operator)
    X-Portal-Tenant-Id — UUID of the caller's currently-selected tenant (optional)

These headers are populated by the Auth.js session forwarded through the portal.
"""

from __future__ import annotations

import uuid
from dataclasses import dataclass

from fastapi import Depends, Header, HTTPException, status
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from shared.db import get_session
from shared.models.auth import UserTenantRole


@dataclass
class PortalCaller:
    """Resolved caller identity from portal request headers."""

    user_id: uuid.UUID
    role: str
    tenant_id: uuid.UUID | None = None


async def get_portal_caller(
    x_portal_user_id: str = Header(..., alias="X-Portal-User-Id"),
    x_portal_user_role: str = Header(..., alias="X-Portal-User-Role"),
    x_portal_tenant_id: str | None = Header(default=None, alias="X-Portal-Tenant-Id"),
) -> PortalCaller:
    """
    FastAPI dependency: parse and validate portal identity headers.

    Returns PortalCaller with typed fields.
    Raises 401 if X-Portal-User-Id is not a valid UUID.
    """
    try:
        user_id = uuid.UUID(x_portal_user_id)
    except (ValueError, AttributeError) as exc:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid X-Portal-User-Id header",
        ) from exc

    tenant_id: uuid.UUID | None = None
    if x_portal_tenant_id:
        try:
            tenant_id = uuid.UUID(x_portal_tenant_id)
        except (ValueError, AttributeError) as exc:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid X-Portal-Tenant-Id header",
            ) from exc

    return PortalCaller(
        user_id=user_id,
        role=x_portal_user_role,
        tenant_id=tenant_id,
    )


def require_platform_admin(
    caller: PortalCaller = Depends(get_portal_caller),
) -> PortalCaller:
    """
    FastAPI dependency: ensure the caller is a platform admin.

    Returns the caller if role == 'platform_admin'.
    Raises 403 for any other role.
    """
    if caller.role != "platform_admin":
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Platform admin access required",
        )
    return caller


async def require_tenant_admin(
    tenant_id: uuid.UUID,
    caller: PortalCaller = Depends(get_portal_caller),
    session: AsyncSession = Depends(get_session),
) -> PortalCaller:
    """
    FastAPI dependency: ensure the caller is an admin for the given tenant.

    - platform_admin: always passes (bypasses membership check)
    - customer_admin: must have a UserTenantRole row for the tenant
    - customer_operator: always rejected (403)
    - unknown roles: always rejected (403)

    Returns the caller on success.
    """
    if caller.role == "platform_admin":
        return caller

    if caller.role != "customer_admin":
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Tenant admin access required",
        )

    # customer_admin: verify membership in this specific tenant
    result = await session.execute(
        select(UserTenantRole).where(
            UserTenantRole.user_id == caller.user_id,
            UserTenantRole.tenant_id == tenant_id,
            UserTenantRole.role == "customer_admin",
        )
    )
    membership = result.scalar_one_or_none()
    if membership is None:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="You do not have admin access to this tenant",
        )
    return caller


async def require_tenant_member(
    tenant_id: uuid.UUID,
    caller: PortalCaller = Depends(get_portal_caller),
    session: AsyncSession = Depends(get_session),
) -> PortalCaller:
    """
    FastAPI dependency: ensure the caller is a member of the given tenant.

    - platform_admin: always passes (bypasses membership check)
    - customer_admin or customer_operator: must have a UserTenantRole row for the tenant
    - unknown roles: always rejected (403)

    Returns the caller on success.
    """
    if caller.role == "platform_admin":
        return caller

    if caller.role not in ("customer_admin", "customer_operator"):
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Tenant member access required",
        )

    result = await session.execute(
        select(UserTenantRole).where(
            UserTenantRole.user_id == caller.user_id,
            UserTenantRole.tenant_id == tenant_id,
        )
    )
    membership = result.scalar_one_or_none()
    if membership is None:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="You are not a member of this tenant",
        )
    return caller
