from __future__ import annotations

from typing import Any

from lx_dtypes.models.contracts import DocumentType as DocumentTypeContract
from lx_dtypes.models.contracts import ReportContext

from endoreg_db.models import (
    AnonymExaminationReport,
    DocumentType as DocumentTypeModel,
    RawPdfFile,
)

DOCUMENT_TYPE_VALUES = [
    DocumentTypeContract.report_draft.value,
    DocumentTypeContract.report_final.value,
    DocumentTypeContract.histology_draft.value,
    DocumentTypeContract.histology_final.value,
]


def document_type_description(name: str) -> str:
    return f"Autogenerated document type for {name.replace('_', ' ')}"


def ensure_document_types() -> dict[str, DocumentTypeModel]:
    document_types: dict[str, DocumentTypeModel] = {}
    for name in DOCUMENT_TYPE_VALUES:
        document_type, _ = DocumentTypeModel.objects.get_or_create(
            name=name,
            defaults={"description": document_type_description(name)},
        )
        document_types[name] = document_type
    return document_types


def resolve_report_text(pdf: RawPdfFile, payload: dict[str, Any] | None = None) -> str:
    payload_obj = payload or {}

    if isinstance(pdf.anonymized_text, str) and pdf.anonymized_text.strip():
        return pdf.anonymized_text

    payload_text = payload_obj.get("anonymized_text")
    if isinstance(payload_text, str) and payload_text.strip():
        return payload_text

    sensitive_meta = pdf.sensitive_meta
    if sensitive_meta and isinstance(sensitive_meta.anonymized_text, str):
        if sensitive_meta.anonymized_text.strip():
            return sensitive_meta.anonymized_text

    if isinstance(pdf.text, str):
        return pdf.text
    return ""


def resolve_pdf_document_type_name(
    pdf: RawPdfFile, document_type_name: str | DocumentTypeContract | None = None
) -> str:
    if document_type_name:
        if isinstance(document_type_name, DocumentTypeContract):
            return document_type_name.value
        return document_type_name

    raw_meta = pdf.raw_meta if isinstance(pdf.raw_meta, dict) else {}
    candidate = raw_meta.get("document_type")
    if isinstance(candidate, str) and candidate in DOCUMENT_TYPE_VALUES:
        return candidate

    raise ValueError(
        "document_type is required to materialize a report; "
        "provide it explicitly or persist it in pdf.raw_meta during validation"
    )


def build_report_context_from_pdf(
    *,
    pdf: RawPdfFile,
    payload: dict[str, Any] | None = None,
    document_type_name: str | DocumentTypeContract | None = None,
) -> ReportContext:
    if pdf.examination is None:
        raise ValueError(
            "pdf.examination must be explicitly resolved before report materialization"
        )
    patient = pdf.examination.patient
    if patient is None:
        raise ValueError(
            "pdf.examination.patient must be resolved before report materialization"
        )
    if pdf.examination_id is None:
        raise ValueError(
            "pdf.examination_id must be resolved before report materialization"
        )

    sensitive_meta = pdf.sensitive_meta
    document_type = DocumentTypeContract(
        resolve_pdf_document_type_name(pdf, document_type_name=document_type_name)
    )
    return ReportContext(
        patient_examination_id=pdf.examination_id,
        patient_id=patient.pk,
        document_type=document_type,
        anonymized_text=resolve_report_text(pdf, payload),
        patient_hash=getattr(sensitive_meta, "patient_hash", None),
        examination_hash=getattr(sensitive_meta, "examination_hash", None),
        source_pdf_id=pdf.pk,
    )


def build_report_context_from_validation(
    *,
    pdf: RawPdfFile,
    payload: dict[str, Any] | None = None,
    document_type_name: str | DocumentTypeContract,
) -> ReportContext:
    sensitive_meta = pdf.sensitive_meta
    if sensitive_meta is None:
        raise ValueError("pdf.sensitive_meta is required for validation context")
    if sensitive_meta.pseudo_examination_id is None:
        raise ValueError(
            "sensitive_meta.pseudo_examination_id is required for validation context"
        )
    if sensitive_meta.pseudo_patient_id is None:
        raise ValueError(
            "sensitive_meta.pseudo_patient_id is required for validation context"
        )

    document_type = DocumentTypeContract(
        resolve_pdf_document_type_name(pdf, document_type_name=document_type_name)
    )
    return ReportContext(
        patient_examination_id=sensitive_meta.pseudo_examination_id,
        patient_id=sensitive_meta.pseudo_patient_id,
        document_type=document_type,
        anonymized_text=resolve_report_text(pdf, payload),
        patient_hash=sensitive_meta.patient_hash,
        examination_hash=sensitive_meta.examination_hash,
        source_pdf_id=pdf.pk,
    )


def upsert_anonym_examination_report_from_pdf(
    *,
    pdf: RawPdfFile,
    payload: dict[str, Any] | None = None,
    document_type_name: str | None = None,
    validated_at_iso: str | None = None,
    source: str = "case_resolution",
) -> tuple[AnonymExaminationReport, bool]:
    report_context = build_report_context_from_pdf(
        pdf=pdf,
        payload=payload,
        document_type_name=document_type_name,
    )
    document_type = ensure_document_types()[report_context.document_type.value]
    sensitive_meta = pdf.sensitive_meta

    patient_examination = pdf.examination
    assert patient_examination is not None
    patient = patient_examination.patient

    center = pdf.center
    if center is None and sensitive_meta is not None:
        center = sensitive_meta.center

    report_obj = pdf.anonym_examination_report
    created = False
    if report_obj is None:
        report_obj = AnonymExaminationReport()
        created = True

    resolved_text = report_context.anonymized_text

    report_obj.patient = patient
    report_obj.patient_examination = patient_examination
    report_obj.center = center
    report_obj.sensitive_meta = sensitive_meta
    report_obj.type = document_type
    report_obj.text = resolved_text

    meta_obj: dict[str, Any]
    if isinstance(report_obj.meta, dict):
        meta_obj = dict(report_obj.meta)
    else:
        meta_obj = {}
    meta_obj.update(
        {
            "source": source,
            "raw_pdf_file_id": pdf.pk,
            "document_type": report_context.document_type.value,
        }
    )
    if validated_at_iso is not None:
        meta_obj["validated_at"] = validated_at_iso
    report_obj.meta = meta_obj
    report_obj.save()

    update_fields: list[str] = []
    if pdf.anonym_examination_report_id != report_obj.id:
        pdf.anonym_examination_report = report_obj
        update_fields.append("anonym_examination_report")
    if getattr(pdf, "anonymized_text", None) != resolved_text:
        pdf.anonymized_text = resolved_text
        update_fields.append("anonymized_text")
    if pdf.center_id is None and center is not None:
        pdf.center = center
        update_fields.append("center")
    if update_fields:
        pdf.save(update_fields=update_fields)

    return report_obj, created


__all__ = [
    "DOCUMENT_TYPE_VALUES",
    "ensure_document_types",
    "build_report_context_from_pdf",
    "build_report_context_from_validation",
    "resolve_report_text",
    "resolve_pdf_document_type_name",
    "upsert_anonym_examination_report_from_pdf",
]
