"""Tenant context extraction and RLS session variable setup.

Data isolation spec Sections 2.3 and 1.2.

Provides:
- TenantContext dataclass carrying verified tenant_id + node_id
- FastAPI dependency to extract context from HMAC-verified headers
- Helper to set PostgreSQL RLS session variable (app.tenant_id)
"""

from dataclasses import dataclass
from typing import Annotated, Optional
from uuid import UUID

import structlog
from fastapi import Depends, Header, HTTPException
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession

from src.db.session import get_async_session

logger = structlog.get_logger(__name__)


@dataclass(frozen=True)
class TenantContext:
    """Verified tenant + node context for request scoping."""

    tenant_id: UUID
    node_id: UUID


async def get_tenant_context(
    x_tenant_id: Annotated[Optional[str], Header()] = None,
    x_node_id: Annotated[Optional[str], Header()] = None,
) -> TenantContext:
    """Extract and validate tenant context from HMAC-verified headers.

    In production, these headers are set by the BFF layer after HMAC verification
    (see SPEC-HMAC). During the pilot phase, they may be passed directly.

    Args:
        x_tenant_id: Tenant UUID from X-Tenant-ID header.
        x_node_id: Node UUID from X-Node-ID header.

    Returns:
        TenantContext with validated UUIDs.

    Raises:
        HTTPException 400: If headers are missing or not valid UUIDs.
    """
    if not x_tenant_id:
        raise HTTPException(
            status_code=400,
            detail={"error": {"code": "MISSING_TENANT_ID", "message": "X-Tenant-ID header required"}},
        )
    if not x_node_id:
        raise HTTPException(
            status_code=400,
            detail={"error": {"code": "MISSING_NODE_ID", "message": "X-Node-ID header required"}},
        )

    try:
        tenant_uuid = UUID(x_tenant_id)
    except ValueError:
        raise HTTPException(
            status_code=400,
            detail={"error": {"code": "INVALID_TENANT_ID", "message": "X-Tenant-ID must be a valid UUID"}},
        )

    try:
        node_uuid = UUID(x_node_id)
    except ValueError:
        raise HTTPException(
            status_code=400,
            detail={"error": {"code": "INVALID_NODE_ID", "message": "X-Node-ID must be a valid UUID"}},
        )

    return TenantContext(tenant_id=tenant_uuid, node_id=node_uuid)


# Type alias for FastAPI dependency injection
TenantContextDep = Annotated[TenantContext, Depends(get_tenant_context)]


async def set_rls_tenant(session: AsyncSession, tenant_id: UUID) -> None:
    """Set PostgreSQL session variable for RLS enforcement.

    Must be called within an active transaction before any query on
    RLS-protected tables.

    Args:
        session: Active async SQLAlchemy session.
        tenant_id: Tenant UUID to scope queries to.
    """
    # Re-validate to prevent injection if called from non-dependency code path
    safe_id = str(UUID(str(tenant_id)))
    await session.execute(
        text(f"SET LOCAL app.tenant_id = '{safe_id}'"),
    )
