from __future__ import annotations

from datetime import UTC, datetime
from typing import TYPE_CHECKING, Annotated, Any
from urllib.parse import quote
from zoneinfo import ZoneInfo

from fastapi import APIRouter, Depends, Query, Request
from fastapi.responses import HTMLResponse, JSONResponse

from app.auth import get_current_user
from app.core.dependencies import get_services
from app.models import UserInDB
from app.routes.history import build_user_histories_snapshot
from app.routes.shell_navigation import build_dashboard_navigation, build_shell_context
from app.services.demo_identity_service import get_demo_identity_service


if TYPE_CHECKING:
    from app.core.services import AppServices
else:
    AppServices = Any


router = APIRouter()
CurrentUser = Annotated[UserInDB, Depends(get_current_user)]
ServicesDep = Annotated[AppServices, Depends(get_services)]
_BOGOTA_TZ = ZoneInfo("America/Bogota")


def _safe_iso_to_datetime(value: str | None) -> datetime:
    if not value:
        return datetime.min.replace(tzinfo=UTC)
    try:
        parsed = datetime.fromisoformat(str(value))
    except ValueError:
        return datetime.min.replace(tzinfo=UTC)
    if parsed.tzinfo is None:
        parsed = parsed.replace(tzinfo=_BOGOTA_TZ)
    return parsed.astimezone(UTC)


def _build_history_href(case_group: dict[str, Any], *, fallback_document_id: str = "") -> str:
    case_key = str(case_group.get("case_key") or "").strip()
    document_id = str(fallback_document_id or "").strip()
    if case_key:
        href = f"/casos?case_key={quote(case_key)}"
        if document_id:
            href = f"{href}&documento_id={quote(document_id)}"
        return href
    return "/casos"


def _case_status_variant(status: str) -> str:
    normalized = str(status or "pendiente").strip().lower()
    if normalized == "completado":
        return "success"
    if normalized in {"en_cola", "procesando"}:
        return "warning"
    return "info"


def _build_case_actions(case_group: dict[str, Any], *, document_id: str = "") -> list[dict[str, str]]:
    case_key = str(case_group.get("case_key") or "").strip()
    actions = [
        {
            "label": "Ver detalle",
            "href": _build_history_href(case_group, fallback_document_id=document_id),
            "variant": "secondary",
        }
    ]
    if case_key:
        actions.append(
            {
                "label": "Epicrisis",
                "href": str(case_group.get("epicrisis_url") or f"/epicrisis?case_key={quote(case_key)}"),
                "variant": "primary",
            }
        )
        actions.append(
            {
                "label": "RIPS",
                "href": f"/rips/operativo?case_key={quote(case_key)}",
                "variant": "secondary",
            }
        )
        actions.append(
            {
                "label": "PDF",
                "href": f"/epicrisis/pdf_cache/ultimo?case_key={quote(case_key)}",
                "variant": "ghost",
            }
        )
    return actions


def _build_recent_cases(snapshot: dict[str, Any], *, services: ServicesDep, username: str) -> list[dict[str, Any]]:
    cases = []
    demo_service = get_demo_identity_service(services)
    for case_group in snapshot["historias_por_caso"].values():
        latest_doc = case_group["documentos"][0] if case_group["documentos"] else {}
        document_id = str(latest_doc.get("_id") or "").strip()
        latest_at = str(latest_doc.get("fecha_analisis") or "")
        visible_identity = demo_service.project_case_identity(
            username=username,
            case_key=str(case_group.get("case_key") or "").strip(),
            case_number=str(case_group.get("case_number") or "").strip(),
            patient_id=str(case_group.get("patient_id") or "").strip(),
            patient_name=str(case_group.get("patient_name") or ""),
        )
        visible_case_key = str(visible_identity.get("case_key") or case_group.get("case_key") or "").strip()
        cases.append(
            {
                "case_key": visible_case_key,
                "case_number": str(case_group.get("case_number") or "").strip() or "Sin número",
                "patient_name": str(visible_identity.get("patient_name") or "Paciente sin nombre"),
                "document_count": len(case_group.get("documentos") or []),
                "document_types": case_group.get("tipos_documento") or [],
                "epicrisis_status": str(case_group.get("epicrisis_status") or "pendiente"),
                "status_variant": _case_status_variant(case_group.get("epicrisis_status")),
                "ready_for_epicrisis": bool(case_group.get("ready_for_epicrisis", False)),
                "epicrisis_rule_status": str(case_group.get("epicrisis_rule_status") or ""),
                "epicrisis_blocking_reason": str(case_group.get("epicrisis_blocking_reason") or ""),
                "epicrisis_missing_documents": list(case_group.get("epicrisis_missing_documents") or []),
                "updated_at": latest_at,
                "href": _build_history_href({**case_group, "case_key": visible_case_key}, fallback_document_id=document_id),
                "actions": _build_case_actions({**case_group, "case_key": visible_case_key}, document_id=document_id),
            }
        )
    return sorted(cases, key=lambda item: _safe_iso_to_datetime(item["updated_at"]), reverse=True)[:6]


def _build_recent_activity(snapshot: dict[str, Any], *, services: ServicesDep, username: str) -> list[dict[str, Any]]:
    activity = []
    demo_service = get_demo_identity_service(services)
    for case_group in snapshot["historias_por_caso"].values():
        latest_doc = case_group["documentos"][0] if case_group["documentos"] else {}
        if not latest_doc:
            continue
        document_id = str(latest_doc.get("_id") or "").strip()
        document_type = str(latest_doc.get("tipo_documento") or "sin_tipo").replace("_", " ")
        epicrisis_status = str(case_group.get("epicrisis_status") or "pendiente")
        visible_identity = demo_service.project_case_identity(
            username=username,
            case_key=str(case_group.get("case_key") or "").strip(),
            case_number=str(case_group.get("case_number") or "").strip(),
            patient_id=str(case_group.get("patient_id") or "").strip(),
            patient_name=str(case_group.get("patient_name") or ""),
        )
        visible_case_key = str(visible_identity.get("case_key") or case_group.get("case_key") or "").strip()
        activity.append(
            {
                "kind": "case",
                "title": str(visible_identity.get("patient_name") or "Paciente sin nombre"),
                "subtitle": f"{document_type.title()} · {str(latest_doc.get('nombre_archivo') or 'Documento reciente')}",
                "status": epicrisis_status,
                "status_variant": _case_status_variant(epicrisis_status),
                "updated_at": str(latest_doc.get("fecha_analisis") or ""),
                "href": _build_history_href({**case_group, "case_key": visible_case_key}, fallback_document_id=document_id),
                "actions": _build_case_actions({**case_group, "case_key": visible_case_key}, document_id=document_id)[:3],
            }
        )
    return sorted(activity, key=lambda item: _safe_iso_to_datetime(item["updated_at"]), reverse=True)[:8]


def _batch_status_variant(status: str) -> str:
    normalized = str(status or "recibido").strip().lower()
    if normalized in {"completado", "asociado"}:
        return "success"
    if normalized in {"pendiente_validacion", "completado_con_errores"}:
        return "warning"
    if normalized == "fallido":
        return "error"
    return "info"


def _build_recent_batches(services: ServicesDep, *, username: str) -> list[dict[str, Any]]:
    batches = services.batch_runtime.list_user_batches.execute(username, limit=6)
    items = []
    for batch in batches:
        batch_id = str(batch.get("batch_id") or "").strip()
        status = str(batch.get("status") or "recibido")
        items.append(
            {
                "batch_id": batch_id,
                "title": str(batch.get("nombre_archivo") or "ZIP"),
                "status": status,
                "status_variant": _batch_status_variant(status),
                "created_at": str(batch.get("created_at") or ""),
                "updated_at": str(batch.get("updated_at") or ""),
                "total_files": int(batch.get("total_files") or 0),
                "associated_files": int(batch.get("associated_files") or 0),
                "pending_validation_files": int(batch.get("pending_validation_files") or 0),
                "failed_files": int(batch.get("failed_files") or 0),
                "href": f"/subir_lote?batch_id={quote(batch_id)}" if batch_id else "/subir_lote",
                "actions": [
                    {
                        "label": "Abrir lote",
                        "href": f"/subir_lote?batch_id={quote(batch_id)}" if batch_id else "/subir_lote",
                        "variant": "secondary",
                    }
                ],
            }
        )
    return items


def _build_summary(snapshot: dict[str, Any]) -> dict[str, Any]:
    patient_count = len(snapshot["historias_por_paciente"])
    case_count = len(snapshot["historias_por_caso"])
    document_count = sum(len(items) for items in snapshot["historias_por_paciente"].values())
    completed_cases = sum(
        1
        for case_group in snapshot["historias_por_caso"].values()
        if case_group.get("epicrisis_status") == "completado"
    )
    ready_cases = sum(
        1 for case_group in snapshot["historias_por_caso"].values() if case_group.get("ready_for_epicrisis")
    )
    return {
        "patients": patient_count,
        "cases": case_count,
        "documents": document_count,
        "completed_epicrisis": completed_cases,
        "ready_for_epicrisis": ready_cases,
    }


def _build_overview_payload(
    snapshot: dict[str, Any], *, services: ServicesDep, username: str
) -> dict[str, Any]:
    recent_cases = _build_recent_cases(snapshot, services=services, username=username)
    recent_activity = _build_recent_activity(snapshot, services=services, username=username)
    return {
        "summary": _build_summary(snapshot),
        "recent_cases": recent_cases,
        "recent_activity": recent_activity,
        "recent_batches": _build_recent_batches(services, username=username),
        "empty": not snapshot["historias_por_paciente"],
    }


def _build_search_results(
    snapshot: dict[str, Any], *, query: str, services: ServicesDep, username: str
) -> list[dict[str, Any]]:
    normalized_query = query.strip().lower()
    if not normalized_query:
        return []

    results: list[dict[str, Any]] = []
    seen: set[tuple[str, str]] = set()
    demo_service = get_demo_identity_service(services)

    for patient_name, documents in snapshot["historias_por_paciente"].items():
        visible_patient_name = patient_name
        if documents:
            projected_document = demo_service.project_document(username=username, document=documents[0])
            if isinstance(projected_document, dict):
                visible_patient_name = str(projected_document.get("nombre_paciente") or patient_name)
        haystack = " ".join(
            [
                visible_patient_name.lower(),
                " ".join(str(doc.get("nombre_archivo") or "").lower() for doc in documents),
                " ".join(str(doc.get("tipo_documento") or "").lower() for doc in documents),
            ]
        )
        if normalized_query not in haystack:
            continue
        key = ("patient", visible_patient_name.lower())
        if key in seen:
            continue
        seen.add(key)
        results.append(
            {
                "kind": "patient",
                "title": visible_patient_name,
                "subtitle": f"{len(documents)} documento(s) disponibles",
                "href": "/casos",
                "status": "",
                "actions": [{"label": "Ver casos", "href": "/casos", "variant": "secondary"}],
            }
        )

    for case_group in snapshot["historias_por_caso"].values():
        latest_doc = case_group["documentos"][0] if case_group["documentos"] else {}
        visible_identity = demo_service.project_case_identity(
            username=username,
            case_key=str(case_group.get("case_key") or "").strip(),
            case_number=str(case_group.get("case_number") or "").strip(),
            patient_id=str(case_group.get("patient_id") or "").strip(),
            patient_name=str(case_group.get("patient_name") or ""),
        )
        visible_case_key = str(visible_identity.get("case_key") or case_group.get("case_key") or "").strip()
        search_blob = " ".join(
            [
                visible_case_key.lower(),
                str(case_group.get("case_number") or "").lower(),
                str(visible_identity.get("patient_name") or case_group.get("patient_name") or "").lower(),
                " ".join(str(item).lower() for item in case_group.get("tipos_documento") or []),
            ]
        )
        if normalized_query not in search_blob:
            continue
        result_key = ("case", str(case_group.get("group_key") or "").lower())
        if result_key in seen:
            continue
        seen.add(result_key)
        document_id = str(latest_doc.get("_id") or "").strip()
        results.append(
            {
                "kind": "case",
                "title": str(case_group.get("case_number") or visible_case_key or "Caso"),
                "subtitle": str(visible_identity.get("patient_name") or "Paciente sin nombre"),
                "href": _build_history_href({**case_group, "case_key": visible_case_key}, fallback_document_id=document_id),
                "status": str(case_group.get("epicrisis_status") or "pendiente"),
                "actions": _build_case_actions({**case_group, "case_key": visible_case_key}, document_id=document_id)[:3],
            }
        )

    for case_group in snapshot["historias_por_caso"].values():
        for document in case_group.get("documentos") or []:
            projected_document = demo_service.project_document(username=username, document=document)
            visible_document = projected_document if isinstance(projected_document, dict) else document
            visible_case_key = str(visible_document.get("case_key") or case_group.get("case_key") or "").strip()
            filename = str(document.get("nombre_archivo") or "")
            document_type = str(document.get("tipo_documento") or "sin_tipo")
            patient_name = str(
                visible_document.get("nombre_paciente") or visible_document.get("patient_name") or "Paciente sin nombre"
            )
            case_number = str(case_group.get("case_number") or visible_case_key or "")
            search_blob = " ".join(
                [filename.lower(), document_type.lower(), patient_name.lower(), case_number.lower(), visible_case_key.lower()]
            )
            if normalized_query not in search_blob:
                continue
            document_id = str(document.get("_id") or "").strip()
            result_key = ("document", document_id)
            if result_key in seen:
                continue
            seen.add(result_key)
            results.append(
                {
                    "kind": "document",
                    "title": filename or "Documento",
                    "subtitle": f"{document_type.replace('_', ' ').title()} · {patient_name}",
                    "href": _build_history_href({**case_group, "case_key": visible_case_key}, fallback_document_id=document_id),
                    "status": "",
                    "actions": [
                        {
                            "label": "Abrir",
                            "href": _build_history_href(
                                {**case_group, "case_key": visible_case_key},
                                fallback_document_id=document_id,
                            ),
                            "variant": "secondary",
                        }
                    ],
                }
            )

    return results[:12]


@router.get("/dashboard", response_class=HTMLResponse)
async def dashboard(request: Request, user: CurrentUser, services: ServicesDep):
    return services.templates.TemplateResponse(
        request,
        "dashboard.html",
        {
            "request": request,
            "user": user,
            "dashboard_nav": build_dashboard_navigation(user),
            **build_shell_context(request=request, user=user),
        },
    )


@router.get("/api/dashboard/overview")
async def dashboard_overview(user: CurrentUser, services: ServicesDep) -> JSONResponse:
    snapshot = build_user_histories_snapshot(username=user.username, services=services)
    return JSONResponse(content=_build_overview_payload(snapshot, services=services, username=user.username))


@router.get("/api/dashboard/search")
async def dashboard_search(
    user: CurrentUser,
    services: ServicesDep,
    q: Annotated[str, Query(min_length=1, max_length=120)],
) -> JSONResponse:
    snapshot = build_user_histories_snapshot(username=user.username, services=services)
    return JSONResponse(
        content={
            "query": q,
            "results": _build_search_results(snapshot, query=q, services=services, username=user.username),
        }
    )


@router.get("/analizar", response_class=HTMLResponse)
async def analizar(request: Request, user: CurrentUser, services: ServicesDep):
    return services.templates.TemplateResponse(
        request,
        "analizar_CIE10.html",
        {"request": request, "user": user, **build_shell_context(request=request, user=user)},
    )
