"""
Patient portal authentication dependency.

Patient JWTs carry `sub = "patient:<uuid>"` and never pass staff guards —
this dependency validates them and injects the Patient record.
"""

from uuid import UUID

from fastapi import Depends, Request
from fastapi.security import HTTPAuthorizationCredentials
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.auth_deps import http_bearer
from app.core.dependencies import get_db
from app.core.exceptions import UnauthorizedException
from app.core.security import decode_access_token
from app.core.state_store import is_access_token_blacklisted
from app.models.patient import Patient
from app.repositories.patient_repository import PatientRepository

_patient_repo = PatientRepository()


async def get_current_patient(
    request: Request,
    credentials: HTTPAuthorizationCredentials | None = Depends(http_bearer),
    db: AsyncSession = Depends(get_db),
) -> Patient:
    """Validate a patient Bearer token or HttpOnly cookie and return the Patient."""
    token: str | None = None
    if credentials and credentials.credentials:
        token = credentials.credentials
    elif "patient_token" in request.cookies:
        token = request.cookies.get("patient_token")

    if not token:
        raise UnauthorizedException(
            message="Authentication required. Please provide a Bearer token or cookie."
        )

    if await is_access_token_blacklisted(token):
        raise UnauthorizedException(message="Token has been revoked. Please log in again.")

    payload = decode_access_token(token)
    sub = str(payload.get("sub", ""))
    if not sub.startswith("patient:"):
        raise UnauthorizedException(message="A patient portal token is required for this endpoint.")

    try:
        patient_uuid = UUID(sub.split(":", 1)[1])
    except ValueError as exc:
        raise UnauthorizedException(message="Invalid token claims: subject is not a valid UUID.") from exc

    patient = await _patient_repo.get_by_id(db, patient_uuid)
    if not patient:
        raise UnauthorizedException(message="Patient not found.")

    return patient
