"""
Patient self-service authentication and portal endpoints (phone + bill PIN).
"""

from datetime import UTC, datetime
from typing import cast
from uuid import UUID

from fastapi import APIRouter, Depends, Request, Response, status
from sqlalchemy.ext.asyncio import AsyncSession

from app.config import settings
from app.core.dependencies import get_db
from app.core.exceptions import UnauthorizedException
from app.core.patient_deps import get_current_patient
from app.core.rate_limit import limiter
from app.core.responses import SuccessResponse
from app.core.security import decode_access_token
from app.core.state_store import blacklist_access_token
from app.models.patient import Patient
from app.schemas.portal import (
    PatientLoginRequest,
    PatientPortalProfile,
    PatientTokenResponse,
    PortalOverviewResponse,
)
from app.services.patient_portal_service import PatientPortalService

router = APIRouter(prefix="/patient-auth", tags=["Patient Portal"])
_service = PatientPortalService()


@router.post(
    "/login",
    response_model=SuccessResponse[PatientTokenResponse],
    status_code=status.HTTP_200_OK,
    summary="Patient portal sign-in",
    description="Authenticate with the registered mobile number and the PIN printed on the bill.",
)
@limiter.limit("5/minute")
async def patient_login(
    request: Request,
    response: Response,
    payload: PatientLoginRequest,
    db: AsyncSession = Depends(get_db),
) -> SuccessResponse[PatientTokenResponse]:
    result = await _service.login(db, payload)
    response.set_cookie(
        key="patient_token",
        value=result.access_token,
        httponly=True,
        samesite="lax",
        secure=settings.is_production,
        max_age=86400,
        path="/",
    )
    return SuccessResponse(data=result)


@router.post(
    "/logout",
    response_model=SuccessResponse[dict[str, str]],
    status_code=status.HTTP_200_OK,
    summary="Patient portal sign-out",
)
async def patient_logout(
    request: Request,
    response: Response,
) -> SuccessResponse[dict[str, str]]:
    token = request.cookies.get("patient_token")
    authorization = request.headers.get("authorization", "")
    if authorization.lower().startswith("bearer "):
        token = authorization[7:].strip()
    if token:
        try:
            payload = decode_access_token(token)
            if str(payload.get("sub", "")).startswith("patient:"):
                ttl = max(
                    1, int(float(cast(str | int | float, payload["exp"])) - datetime.now(UTC).timestamp())
                )
                await blacklist_access_token(token, expires_in_seconds=ttl)
        except UnauthorizedException:
            pass  # Signing out an expired session remains idempotent.
    response.delete_cookie(key="patient_token", path="/")
    return SuccessResponse(data={"message": "Logged out successfully."})


@router.get(
    "/me",
    response_model=SuccessResponse[PatientPortalProfile],
    summary="Current patient profile",
)
async def patient_me(
    current_patient: Patient = Depends(get_current_patient),
) -> SuccessResponse[PatientPortalProfile]:
    return SuccessResponse(data=PatientPortalProfile.model_validate(current_patient))


@router.get(
    "/overview",
    response_model=SuccessResponse[PortalOverviewResponse],
    summary="Patient visits and appointments",
    description="Released reports per visit plus appointments, newest first.",
)
async def patient_overview(
    db: AsyncSession = Depends(get_db),
    current_patient: Patient = Depends(get_current_patient),
) -> SuccessResponse[PortalOverviewResponse]:
    result = await _service.get_overview(db, current_patient)
    return SuccessResponse(data=result)


@router.get(
    "/reports/{report_id}/pdf",
    summary="Download own released report PDF",
    responses={
        200: {
            "content": {"application/pdf": {}},
            "description": "Returns the patient's released report as binary PDF",
        }
    },
)
async def patient_report_pdf(
    report_id: UUID,
    db: AsyncSession = Depends(get_db),
    current_patient: Patient = Depends(get_current_patient),
) -> Response:
    """Stream the patient's own released report; anything else is not found."""
    pdf_bytes, filename = await _service.get_report_pdf(db, current_patient, report_id)
    return Response(
        content=pdf_bytes,
        media_type="application/pdf",
        headers={
            "Content-Disposition": f'inline; filename="{filename}"',
            "Cache-Control": "private, no-cache, no-store, must-revalidate",
        },
    )
