"""
Public client link endpoints for report downloads and appointment views.
No staff authentication required; access is validated via time-limited (72h) PostgreSQL tokens.
"""

import asyncio
from uuid import UUID

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

from app.core.dependencies import get_db
from app.core.exceptions import ForbiddenException, NotFoundException, ValidationException
from app.core.rate_limit import limiter
from app.core.responses import SuccessResponse
from app.models.order import OrderStatus, SampleStatus
from app.models.report import Report, ReportStatus
from app.repositories.appointment_repository import AppointmentRepository
from app.repositories.order_repository import OrderRepository
from app.repositories.report_repository import ReportRepository
from app.repositories.sample_repository import SampleRepository
from app.schemas.notification import (
    ClientAppointmentDetailResponse,
    ClientLabSummary,
    ClientOrderTrackResponse,
    ClientPatientSummary,
    ClientReportDetailResponse,
    OrderTrackStage,
    ReportTrackStatus,
)
from app.services.client_link_service import ClientLinkService
from app.services.pdf_service import PDFReportService

router = APIRouter(prefix="/client", tags=["Client Portal Links"])


@router.get("/reports/{token}", response_model=SuccessResponse[ClientReportDetailResponse])
async def view_client_report(
    token: str,
    db: AsyncSession = Depends(get_db),
    link_service: ClientLinkService = Depends(),
    report_repo: ReportRepository = Depends(),
) -> SuccessResponse[ClientReportDetailResponse]:
    """
    Public endpoint for patients to view verified and released diagnostic reports.
    Accepts the 72-hour emailed token, or the durable printed-QR token
    (stored on the report at release). No login required either way.
    """
    report = None
    try:
        payload = await link_service.verify_token(token, expected_type="report")
        report_id = UUID(payload["target_id"])
        report = await report_repo.get_by_id(db, report_id)
    except (NotFoundException, ValidationException, KeyError, ValueError, AttributeError):
        report = None
    if report is None:
        # Fallback: durable public token printed on the official report.
        stmt = select(Report).where(Report.public_token == token)
        report = (await db.execute(stmt)).scalars().first()
    if not report:
        raise NotFoundException(message="Report associated with this link was not found.")

    if report.status != ReportStatus.RELEASED:
        raise ForbiddenException(
            message=(
                f"This diagnostic report is currently '{report.status.value}' "
                "and cannot be viewed by the patient."
            )
        )

    order = report.order
    patient = order.patient if order else None
    lab = order.lab if order else None

    # Calculate patient age
    patient_age = None
    if patient:
        patient_age = patient.age

    client_patient = ClientPatientSummary(
        name=patient.name if patient else "Patient",
        patient_code=patient.patient_code if patient else "UNKNOWN",
        gender=patient.gender.value if patient else "other",
        age=patient_age,
    )

    client_lab = ClientLabSummary(
        name=lab.name if lab else "Munal Medical Diagnostic Laboratory",
        address=lab.address if lab else None,
        logo_url=lab.logo_url if lab else None,
    )

    data = ClientReportDetailResponse(
        report_id=report.id,
        order_number=order.order_number if order else "UNKNOWN",
        version=report.version,
        status=report.status.value,
        verified_at=report.verified_at,
        released_at=report.released_at,
        verifier_name=report.verifier.name if report.verifier else None,
        patient=client_patient,
        lab=client_lab,
        result_data=report.result_data,
    )
    return SuccessResponse(data=data, message="Report retrieved successfully.")


@router.get("/reports/{token}/pdf")
async def download_client_report_pdf(
    token: str,
    db: AsyncSession = Depends(get_db),
    link_service: ClientLinkService = Depends(),
    report_repo: ReportRepository = Depends(),
    pdf_service: PDFReportService = Depends(),
) -> Response:
    """
    Public endpoint for patients to stream the official ReportLab PDF report.
    Accepts the 72-hour emailed token or the durable printed-QR token.
    """
    report = None
    try:
        payload = await link_service.verify_token(token, expected_type="report")
        report_id = UUID(payload["target_id"])
        report = await report_repo.get_by_id(db, report_id)
    except (NotFoundException, ValidationException, KeyError, ValueError, AttributeError):
        report = None
    if report is None:
        stmt = select(Report).where(Report.public_token == token)
        report = (await db.execute(stmt)).scalars().first()
    if not report:
        raise NotFoundException(message="Report associated with this link was not found.")

    if report.status != ReportStatus.RELEASED:
        raise ForbiddenException(
            message=(
                f"This diagnostic report is currently '{report.status.value}' "
                "and cannot be downloaded by the patient."
            )
        )

    order = report.order
    order_num = order.order_number.replace("/", "_") if order else str(report.id)
    filename = f"report_{order_num}_v{report.version}.pdf"

    pdf_bytes = await asyncio.to_thread(pdf_service.render_pdf, report)
    return Response(
        content=pdf_bytes,
        media_type="application/pdf",
        headers={"Content-Disposition": f'inline; filename="{filename}"'},
    )


@router.get("/appointments/{token}", response_model=SuccessResponse[ClientAppointmentDetailResponse])
async def view_client_appointment(
    token: str,
    db: AsyncSession = Depends(get_db),
    link_service: ClientLinkService = Depends(),
    appointment_repo: AppointmentRepository = Depends(),
) -> SuccessResponse[ClientAppointmentDetailResponse]:
    """
    Public endpoint for patients to view their confirmed appointment schedule.
    Validated via 72-hour URL-safe token.
    """
    payload = await link_service.verify_token(token, expected_type="appointment")
    appointment_id = UUID(payload["target_id"])

    appointment = await appointment_repo.get_by_id(db, appointment_id)
    if not appointment:
        raise NotFoundException(message="Appointment associated with this link was not found.")

    patient = appointment.patient
    lab = appointment.lab
    slot = appointment.slot

    client_patient = ClientPatientSummary(
        name=patient.name if patient else (appointment.contact_name or "Patient"),
        patient_code=patient.patient_code if patient else "UNKNOWN",
        gender=patient.gender.value if patient else "other",
        age=patient.age if patient else None,
    )

    client_lab = ClientLabSummary(
        name=lab.name if lab else "Munal Medical Diagnostic Laboratory",
        address=lab.address if lab else None,
        logo_url=lab.logo_url if lab else None,
    )

    data = ClientAppointmentDetailResponse(
        appointment_id=appointment.id,
        status=appointment.status.value,
        booked_via=appointment.booked_via.value,
        start_at=slot.start_at if slot else None,
        end_at=slot.end_at if slot else None,
        patient=client_patient,
        lab=client_lab,
        notes=appointment.notes,
    )
    return SuccessResponse(data=data, message="Appointment details retrieved successfully.")


def _digits(value: str | None) -> str:
    digits = "".join(c for c in (value or "") if c in "0123456789")
    # Normalize Nepal's explicit country prefix; never accept suffix-only matches.
    if digits.startswith("00977") and len(digits) >= 14:
        return digits[5:]
    if digits.startswith("977") and len(digits) >= 12:
        return digits[3:]
    return digits


@router.get("/orders/track", response_model=SuccessResponse[ClientOrderTrackResponse])
@limiter.limit("10/minute")
async def track_order(
    request: Request,
    order_number: str = Query(
        ..., min_length=3, max_length=80, description="Visit order number or specimen barcode"
    ),
    phone: str = Query(..., min_length=7, max_length=32, description="Registered patient phone number"),
    db: AsyncSession = Depends(get_db),
    order_repo: OrderRepository = Depends(),
    sample_repo: SampleRepository = Depends(),
    report_repo: ReportRepository = Depends(),
) -> SuccessResponse[ClientOrderTrackResponse]:
    """
    Public order-status lookup guarded by the registered phone number.
    Returns only the pipeline stages — no amounts, no clinical values.
    """
    query = order_number.strip()
    given_digits = _digits(phone)
    if len(given_digits) < 7:
        raise NotFoundException(message="No visit found for these details.")
    order = await order_repo.get_by_number(db, query, phone_digits=given_digits)
    if order is None:
        sample = await sample_repo.get_by_barcode(db, query)
        order = sample.order if sample else None
    if order is None or order.patient is None:
        raise NotFoundException(message="No visit found for these details.")

    registered_digits = _digits(order.patient.phone)
    given_digits = _digits(phone)
    if len(given_digits) < 7 or registered_digits != given_digits:
        raise NotFoundException(message="No visit found for these details.")

    samples = await sample_repo.get_by_order_id(db, order.id)
    side_reports = [
        await report_repo.get_latest_by_order_id(db, order.id, modality)
        for modality in ("pathology", "imaging")
    ]
    reports = [r for r in side_reports if r is not None]
    # Legacy orders without side reports: fall back to the latest overall report.
    if not reports:
        latest = await report_repo.get_latest_by_order_id(db, order.id)
        reports = [latest] if latest is not None else []
    documents = [
        ReportTrackStatus(
            modality=r.modality.value if hasattr(r.modality, "value") else str(r.modality),
            status=r.status.value,
            version=r.version,
            ready=r.status == ReportStatus.RELEASED,
        )
        for r in reports
    ]

    collected = any(
        s.status in (SampleStatus.COLLECTED, SampleStatus.PROCESSING, SampleStatus.COMPLETED) for s in samples
    ) or order.status in (
        OrderStatus.SAMPLE_COLLECTED,
        OrderStatus.PROCESSING,
        OrderStatus.COMPLETED,
    )
    analysis = order.status in (OrderStatus.PROCESSING, OrderStatus.COMPLETED) or any(
        r.status != ReportStatus.DRAFT for r in reports
    )
    verified = any(r.status in (ReportStatus.VERIFIED, ReportStatus.RELEASED) for r in reports)
    ready = len(documents) > 0 and all(d.ready for d in documents)

    data = ClientOrderTrackResponse(
        order_number=order.order_number,
        patient_first_name=order.patient.name.split()[0] if order.patient.name else "Patient",
        stages=[
            OrderTrackStage(key="registered", label="Order Registered", done=True),
            OrderTrackStage(key="collected", label="Sample Collected", done=collected),
            OrderTrackStage(key="analysis", label="Laboratory Analysis", done=analysis),
            OrderTrackStage(key="verified", label="Pathologist Verified", done=verified),
            OrderTrackStage(key="ready", label="Ready for Download", done=ready),
        ],
        report_ready=ready,
        documents=documents,
    )
    return SuccessResponse(data=data, message="Order status retrieved successfully.")
