from __future__ import annotations

from importlib import import_module
from typing import Any, Final

from bson import ObjectId
from fastapi import APIRouter, HTTPException, Request
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
from starlette import status

from app.batch_processing.infrastructure.mongo_repositories import MongoBatchCaseRepository
from app.services.clinical_processing import extraer_nombre_paciente

from .common import (
    AppServices,
    CurrentUser,
    ServicesDep,
    _apply_legacy_epicrisis_deprecation_headers,
    _build_epicrisis_blocking_detail,
    _build_epicrisis_document_bundle,
    _build_epicrisis_regen_url,
    _build_epicrisis_rule_diagnostic,
    _build_full_epicrisis_context,
    _cache_legacy_epicrisis_context,
    _canonical_case_epicrisis_url,
    _find_legacy_cached_epicrisis,
    _prepare_epicrisis_template_context,
    _render_epicrisis_status_view,
    _render_legacy_cached_epicrisis,
    _sanitize_epicrisis_error_detail,
    logger,
)


router = APIRouter()

EPICRISIS_TEMPLATE: Final[str] = "epicrisis.html"


def _case_repo() -> MongoBatchCaseRepository:
    return MongoBatchCaseRepository()


def _find_latest_case_document_impl(mongo, username: str, case_key: str) -> dict | None:
    return mongo.collection.find_one(
        {
            "usuario": username,
            "case_key": case_key,
            "tipo_documento": {"$nin": ["epicrisis", "epicrisis_case_cache"]},
        },
        sort=[("fecha_analisis", -1)],
    )


def _find_latest_case_document(mongo, username: str, case_key: str) -> dict | None:
    pages_package = import_module("app.routes.pages")
    finder = getattr(pages_package, "_find_latest_case_document", _find_latest_case_document_impl)
    return finder(mongo, username, case_key)


def _epicrisis_queue_response(
    *,
    case_key: str,
    job_id: str,
    status_value: str,
    reused: bool,
    epicrisis_url: str | None = None,
) -> dict[str, object]:
    return {
        "job_id": job_id,
        "status": status_value,
        "reused": reused,
        "epicrisis_url": epicrisis_url or _canonical_case_epicrisis_url(case_key),
    }


def _case_cached_context(
    *,
    services: AppServices,
    username: str,
    case_key: str,
    regen: bool,
) -> dict[str, Any] | None:
    if regen:
        return None
    return services.case_epicrisis_service.get_cached_case_context(username, case_key)


def _has_cached_case_context(cached_context: dict[str, Any] | None) -> bool:
    return bool(cached_context and isinstance(cached_context.get("contexto"), dict))


def _queue_case_epicrisis_job(
    *,
    services: AppServices,
    username: str,
    case_key: str,
    regen: bool = False,
) -> dict[str, object]:
    try:
        return services.case_epicrisis_runtime_service.queue_case_epicrisis(
            username,
            case_key,
            regen=regen,
        )
    except ValueError as exc:
        detail = str(exc)
        if detail == "Caso no encontrado":
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=detail) from exc
        if detail == "El caso aún no esta listo para generar epicrisis.":
            raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=detail) from exc
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=detail) from exc


def _safe_get_case_epicrisis_status(
    services: AppServices,
    username: str,
    case_key: str,
) -> dict[str, Any] | None:
    try:
        return services.case_epicrisis_runtime_service.get_case_epicrisis_status(username, case_key)
    except ValueError:
        return None


def _resolve_case_number(
    *,
    case_key: str,
    case_status: dict[str, Any] | None,
    latest_case_doc: dict[str, Any] | None,
) -> str:
    return str(
        (case_status or {}).get("case_number") or (latest_case_doc or {}).get("case_number") or case_key
    )


def _render_case_epicrisis_blocked_view(
    *,
    request: Request,
    current_user: CurrentUser,
    templates,
    case_key: str,
    case_number: str,
    status_payload: dict[str, Any] | None,
) -> Any:
    rule_diagnostic = _build_epicrisis_rule_diagnostic(status_payload)
    return _render_epicrisis_status_view(
        request=request,
        templates=templates,
        current_user=current_user,
        status_variant="error",
        title="No fue posible generar la epicrisis",
        case_key=case_key,
        case_number=case_number,
        message="No se puede generar la epicrisis con la documentación actual.",
        detail=_build_epicrisis_blocking_detail(status_payload)
        or "Adjunta el soporte requerido y vuelve a intentar.",
        primary_action_href=_build_epicrisis_regen_url(case_key=case_key)
        or _canonical_case_epicrisis_url(case_key),
        primary_action_label="Volver a generar epicrisis",
        status_code=status.HTTP_409_CONFLICT,
        rule_diagnostic=rule_diagnostic,
    )


def _maybe_render_cached_case_epicrisis(
    *,
    request: Request,
    current_user: CurrentUser,
    services: AppServices,
    case_key: str,
    regen: bool,
) -> Any | None:
    cache_doc = _case_cached_context(
        services=services,
        username=current_user.username,
        case_key=case_key,
        regen=regen,
    )
    if not _has_cached_case_context(cache_doc):
        return None
    if not isinstance(cache_doc, dict):
        return None
    context = cache_doc.get("contexto")
    if not isinstance(context, dict):
        return None

    regen_url = _build_epicrisis_regen_url(case_key=case_key)
    if not bool(context.get("epicrisis_generation_allowed", True)):
        return _render_case_epicrisis_blocked_view(
            request=request,
            current_user=current_user,
            templates=services.templates,
            case_key=case_key,
            case_number=str(context.get("case_number") or case_key),
            status_payload={
                "epicrisis_error": context.get("blocking_reason"),
                "epicrisis_blocking_reason": context.get("blocking_reason"),
                "epicrisis_missing_documents": context.get("missing_documents") or [],
                "epicrisis_rule_findings": ((context.get("rule_evaluation") or {}).get("findings") or []),
            },
        )

    prepared = _prepare_epicrisis_template_context(
        context,
        request=request,
        current_user=current_user,
        regen_url=regen_url,
        regen_case_key=case_key,
        epicrisis_cached=True,
        cie10_retriever=services.cie10_retriever,
    )
    return services.templates.TemplateResponse(request, EPICRISIS_TEMPLATE, prepared)


def _maybe_render_failed_case_epicrisis(
    *,
    request: Request,
    current_user: CurrentUser,
    templates,
    case_key: str,
    case_number: str,
    case_status: dict[str, Any] | None,
    regen: bool,
) -> Any | None:
    current_status = str((case_status or {}).get("epicrisis_status") or "").strip().lower()
    if current_status != "fallido" or regen:
        return None

    is_blocked = str((case_status or {}).get("epicrisis_rule_status") or "").strip().lower() == "blocked"
    if is_blocked:
        return _render_case_epicrisis_blocked_view(
            request=request,
            current_user=current_user,
            templates=templates,
            case_key=case_key,
            case_number=case_number,
            status_payload=case_status,
        )
    return _render_epicrisis_status_view(
        request=request,
        templates=templates,
        current_user=current_user,
        status_variant="error",
        title="No fue posible generar la epicrisis",
        case_key=case_key,
        case_number=case_number,
        message="Ocurrió un error al generar la epicrisis.",
        detail=_sanitize_epicrisis_error_detail((case_status or {}).get("epicrisis_error"))
        or "Puedes volver a intentarlo desde esta misma pantalla.",
        primary_action_href=_build_epicrisis_regen_url(case_key=case_key)
        or _canonical_case_epicrisis_url(case_key),
        primary_action_label="Volver a generar epicrisis",
        status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
    )


def _render_case_queue_result(
    *,
    request: Request,
    current_user: CurrentUser,
    services: AppServices,
    case_key: str,
    case_number: str,
    case_status: dict[str, Any] | None,
    queue_result: dict[str, object],
) -> Any:
    regen_url = _build_epicrisis_regen_url(case_key=case_key) or _canonical_case_epicrisis_url(case_key)
    queued_status = str(
        queue_result.get("status") or (case_status or {}).get("epicrisis_status") or "en_cola"
    )
    if queued_status == "fallido":
        refreshed_status = services.case_epicrisis_runtime_service.get_case_epicrisis_status(
            current_user.username,
            case_key,
        )
        return _render_case_epicrisis_blocked_view(
            request=request,
            current_user=current_user,
            templates=services.templates,
            case_key=case_key,
            case_number=case_number,
            status_payload=refreshed_status,
        )

    if queued_status == "completado":
        refreshed_cache = services.case_epicrisis_service.get_cached_case_context(
            current_user.username, case_key
        )
        refreshed_context = refreshed_cache.get("contexto") if isinstance(refreshed_cache, dict) else None
        if isinstance(refreshed_context, dict):
            context = _prepare_epicrisis_template_context(
                refreshed_context,
                request=request,
                current_user=current_user,
                regen_url=regen_url,
                regen_case_key=case_key,
                epicrisis_cached=True,
                cie10_retriever=services.cie10_retriever,
            )
            return services.templates.TemplateResponse(request, EPICRISIS_TEMPLATE, context)

    return _render_epicrisis_status_view(
        request=request,
        templates=services.templates,
        current_user=current_user,
        status_variant="processing",
        title="Epicrisis del caso en preparación",
        case_key=case_key,
        case_number=case_number,
        message="Estamos preparando la epicrisis. Actualiza en unos segundos o vuelve más tarde.",
        detail=f"Estado actual: {queued_status}",
        primary_action_href=_canonical_case_epicrisis_url(case_key),
        primary_action_label="Recargar",
        status_code=status.HTTP_202_ACCEPTED,
    )


def _load_legacy_base_document(mongo, *, document_id: str, username: str) -> dict[str, Any]:
    if not ObjectId.is_valid(document_id):
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="ID no válido")

    base_doc = mongo.collection.find_one({"_id": ObjectId(document_id)})
    if not base_doc or base_doc.get("usuario") != username:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Documento no encontrado o acceso denegado",
        )
    return base_doc


def _maybe_redirect_legacy_epicrisis(
    *,
    base_doc: dict[str, Any],
    regen: bool,
) -> Any | None:
    base_case_key = str(base_doc.get("case_key") or "").strip()
    if not base_case_key:
        return None
    return _apply_legacy_epicrisis_deprecation_headers(
        RedirectResponse(
            url=f"{_canonical_case_epicrisis_url(base_case_key)}{'&regen=1' if regen else ''}",
            status_code=status.HTTP_307_TEMPORARY_REDIRECT,
        )
    )


def _render_epicrisis_unexpected_error(
    *,
    request: Request,
    current_user: CurrentUser,
    templates,
    normalized_case_key: str,
    resolved_document_id: str,
    base_doc: dict[str, Any] | None,
    latest_case_doc: dict[str, Any] | None,
) -> Any:
    if normalized_case_key or resolved_document_id:
        case_number = str(
            (base_doc or {}).get("case_number")
            or (latest_case_doc or {}).get("case_number")
            or normalized_case_key
            or resolved_document_id
        )
        return _render_epicrisis_status_view(
            request=request,
            templates=templates,
            current_user=current_user,
            status_variant="error",
            title="No fue posible generar la epicrisis",
            case_key=normalized_case_key,
            case_number=case_number,
            message="Ocurrió un error al generar la epicrisis.",
            detail="Intenta nuevamente. Si el problema persiste, revisa los documentos asociados al caso.",
            primary_action_href=(
                _build_epicrisis_regen_url(
                    case_key=normalized_case_key,
                    documento_id=resolved_document_id or None,
                )
                or request.url.path
            ),
            primary_action_label="Volver a generar epicrisis",
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
        )
    raise HTTPException(
        status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
        detail="Error al generar la epicrisis",
    )


def _handle_case_key_epicrisis_view(
    *,
    request: Request,
    current_user: CurrentUser,
    services: AppServices,
    case_key: str,
    regen: bool,
) -> Any:
    latest_case_doc = _find_latest_case_document(services.mongo_analyses, current_user.username, case_key)
    cached_response = _maybe_render_cached_case_epicrisis(
        request=request,
        current_user=current_user,
        services=services,
        case_key=case_key,
        regen=regen,
    )
    if cached_response is not None:
        return cached_response

    case_status = _safe_get_case_epicrisis_status(services, current_user.username, case_key)
    if not case_status and not latest_case_doc:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Caso no encontrado")

    case_number = _resolve_case_number(
        case_key=case_key, case_status=case_status, latest_case_doc=latest_case_doc
    )
    failed_response = _maybe_render_failed_case_epicrisis(
        request=request,
        current_user=current_user,
        templates=services.templates,
        case_key=case_key,
        case_number=case_number,
        case_status=case_status,
        regen=regen,
    )
    if failed_response is not None:
        return failed_response

    try:
        queue_result = _queue_case_epicrisis_job(
            services=services,
            username=current_user.username,
            case_key=case_key,
            regen=regen,
        )
    except HTTPException as exc:
        if exc.status_code == status.HTTP_409_CONFLICT:
            refreshed_status = _safe_get_case_epicrisis_status(services, current_user.username, case_key)
            status_payload = refreshed_status or {
                "epicrisis_error": exc.detail,
                "epicrisis_blocking_reason": exc.detail,
            }
            return _render_case_epicrisis_blocked_view(
                request=request,
                current_user=current_user,
                templates=services.templates,
                case_key=case_key,
                case_number=case_number,
                status_payload=status_payload,
            )
        raise
    except Exception:
        logger.exception("Error encolando/generando epicrisis case-aware")
        return _render_epicrisis_status_view(
            request=request,
            templates=services.templates,
            current_user=current_user,
            status_variant="error",
            title="No fue posible generar la epicrisis",
            case_key=case_key,
            case_number=case_number,
            message="Ocurrió un error al generar la epicrisis.",
            detail="Intenta nuevamente. Si el problema persiste, revisa los documentos asociados al caso.",
            primary_action_href=_build_epicrisis_regen_url(case_key=case_key)
            or _canonical_case_epicrisis_url(case_key),
            primary_action_label="Volver a generar epicrisis",
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
        )

    return _render_case_queue_result(
        request=request,
        current_user=current_user,
        services=services,
        case_key=case_key,
        case_number=case_number,
        case_status=case_status,
        queue_result=queue_result,
    )


def _handle_legacy_epicrisis_view(
    *,
    request: Request,
    current_user: CurrentUser,
    services: AppServices,
    resolved_document_id: str,
    regen: bool,
) -> Any:
    base_doc = _load_legacy_base_document(
        services.mongo_analyses,
        document_id=resolved_document_id,
        username=current_user.username,
    )
    redirect_response = _maybe_redirect_legacy_epicrisis(base_doc=base_doc, regen=regen)
    if redirect_response is not None:
        return redirect_response

    nombre_paciente = str(base_doc.get("nombre_paciente") or "").strip() or extraer_nombre_paciente(base_doc)
    bundle = _build_epicrisis_document_bundle(
        services.mongo_analyses,
        current_user.username,
        nombre_paciente,
        base_doc=base_doc,
    )
    regen_url = _build_epicrisis_regen_url(
        case_key=str(base_doc.get("case_key") or "").strip(),
        documento_id=resolved_document_id,
    )

    if not regen:
        cache_doc = _find_legacy_cached_epicrisis(
            services.mongo_analyses,
            username=current_user.username,
            resolved_document_id=resolved_document_id,
            bundle=bundle,
        )
        if cache_doc and isinstance(cache_doc.get("contexto"), dict):
            return _render_legacy_cached_epicrisis(
                request=request,
                current_user=current_user,
                templates=services.templates,
                cie10_retriever=services.cie10_retriever,
                cache_doc=cache_doc,
                bundle=bundle,
                regen_url=regen_url,
            )

    context = _build_full_epicrisis_context(
        services=services,
        bundle=bundle,
        username=current_user.username,
        cie10_retriever=services.cie10_retriever,
        regen_url=regen_url,
        regen_case_key="",
        epicrisis_cached=False,
        request=request,
        current_user=current_user,
    )
    _cache_legacy_epicrisis_context(
        services.mongo_analyses,
        username=current_user.username,
        resolved_document_id=resolved_document_id,
        bundle=bundle,
        contexto=context,
        regen=regen,
    )
    return _apply_legacy_epicrisis_deprecation_headers(
        services.templates.TemplateResponse(request, EPICRISIS_TEMPLATE, context)
    )


@router.get("/epicrisis", response_class=HTMLResponse)
async def epicrisis_view(
    request: Request,
    id: str | None = None,
    documento_id: str | None = None,
    case_key: str | None = None,
    regen: bool = False,
    *,
    current_user: CurrentUser,
    services: ServicesDep,
):
    templates = services.templates
    normalized_case_key = ""
    resolved_document_id = ""
    base_doc: dict[str, Any] | None = None
    latest_case_doc: dict[str, Any] | None = None
    try:
        normalized_case_key = str(case_key or "").strip()
        if normalized_case_key:
            latest_case_doc = _find_latest_case_document(
                services.mongo_analyses,
                current_user.username,
                normalized_case_key,
            )
            return _handle_case_key_epicrisis_view(
                request=request,
                current_user=current_user,
                services=services,
                case_key=normalized_case_key,
                regen=regen,
            )

        resolved_document_id = str(documento_id or id or "").strip()
        if not resolved_document_id:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Debes indicar 'case_key' o 'documento_id' para cargar la epicrisis.",
            )
        return _handle_legacy_epicrisis_view(
            request=request,
            current_user=current_user,
            services=services,
            resolved_document_id=resolved_document_id,
            regen=regen,
        )
    except HTTPException:
        raise
    except Exception:
        logger.exception("Error en epicrisis_view")
        return _render_epicrisis_unexpected_error(
            request=request,
            current_user=current_user,
            templates=templates,
            normalized_case_key=normalized_case_key,
            resolved_document_id=resolved_document_id,
            base_doc=base_doc,
            latest_case_doc=latest_case_doc,
        )


@router.get("/epicrisis/caso/{case_key}", response_class=HTMLResponse)
async def epicrisis_case_view(
    case_key: str,
    current_user: CurrentUser,
):
    case = _case_repo().get_user_case(current_user.username, case_key)
    if not case:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Caso no encontrado")
    return RedirectResponse(
        url=_canonical_case_epicrisis_url(case_key), status_code=status.HTTP_307_TEMPORARY_REDIRECT
    )


@router.post("/epicrisis")
async def solicitar_epicrisis_case_aware(
    case_key: str = "",
    regen: bool = False,
    *,
    current_user: CurrentUser,
    services: ServicesDep,
):
    normalized_case_key = str(case_key or "").strip()
    if not normalized_case_key:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="El parámetro 'case_key' es obligatorio.",
        )

    queue_result = _queue_case_epicrisis_job(
        services=services,
        username=current_user.username,
        case_key=normalized_case_key,
        regen=regen,
    )
    status_data = services.case_epicrisis_runtime_service.get_case_epicrisis_status(
        current_user.username,
        normalized_case_key,
    )
    current_status = str(queue_result.get("status") or status_data.get("epicrisis_status") or "en_cola")
    STATUS_CODE_MAPPING = {
        "completado": status.HTTP_200_OK,
        "fallido": status.HTTP_409_CONFLICT,
        "en_cola": status.HTTP_202_ACCEPTED
    }
    return JSONResponse(
        status_code=STATUS_CODE_MAPPING.get(current_status, status.HTTP_202_ACCEPTED),
        content={
            "case_key": normalized_case_key,
            "case_number": status_data.get("case_number", ""),
            "epicrisis_status": current_status,
            "epicrisis_job_id": queue_result.get("job_id", status_data.get("epicrisis_job_id", "")),
            "epicrisis_url": queue_result.get(
                "epicrisis_url",
                status_data.get("epicrisis_url", _canonical_case_epicrisis_url(normalized_case_key)),
            ),
            "reused": bool(queue_result.get("reused", False)),
            "ready_for_epicrisis": bool(status_data.get("ready_for_epicrisis", False)),
            "epicrisis_rule_status": status_data.get("epicrisis_rule_status", ""),
            "epicrisis_rule_findings": status_data.get("epicrisis_rule_findings", []),
            "epicrisis_blocking_reason": status_data.get("epicrisis_blocking_reason", ""),
            "epicrisis_missing_documents": status_data.get("epicrisis_missing_documents", []),
        },
    )


@router.get("/epicrisis/estado")
async def estado_epicrisis_case_aware(
    case_key: str = "",
    *,
    current_user: CurrentUser,
    services: ServicesDep,
):
    normalized_case_key = str(case_key or "").strip()
    if not normalized_case_key:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="El parámetro 'case_key' es obligatorio.",
        )

    try:
        return services.case_epicrisis_runtime_service.get_case_epicrisis_status(
            current_user.username,
            normalized_case_key,
        )
    except ValueError as exc:
        detail = str(exc)
        if detail == "Caso no encontrado":
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=detail) from exc
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=detail) from exc


@router.patch("/api/epicrisis/cases/{case_key}/draft")
async def actualizar_draft_epicrisis_case_aware(
    case_key: str,
    request: Request,
    current_user: CurrentUser,
    services: ServicesDep,
):
    normalized_case_key = str(case_key or "").strip()
    if not normalized_case_key:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="El parámetro 'case_key' es obligatorio.",
        )

    payload = await request.json()
    if not isinstance(payload, dict):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Payload invalido.",
        )

    context = services.case_epicrisis_service.update_case_draft(
        current_user.username,
        normalized_case_key,
        payload=payload,
    )
    return {
        "case_key": normalized_case_key,
        "manual_soat_results": context.get("manual_soat_results", []),
        "pdf_draft": context.get("pdf_draft", {}),
        "codigos_desde_soat": context.get("codigos_desde_soat", []),
        "diagnosticos_consolidados": context.get("diagnosticos_consolidados", []),
        "diagnosticos_pendientes_validacion": context.get("diagnosticos_pendientes_validacion", []),
        "diagnosticos_consolidacion_hallazgos": context.get("diagnosticos_consolidacion_hallazgos", []),
        "ayudas_diagnosticas": context.get("ayudas_diagnosticas", []),
        "imagenes_diagnosticas": context.get("imagenes_diagnosticas", []),
    }
