from __future__ import annotations

from typing import TYPE_CHECKING, Annotated, Any

from fastapi import APIRouter, Body, Depends, File, Form, HTTPException, UploadFile
from fastapi.responses import JSONResponse
from pydantic import BaseModel, ConfigDict
from starlette import status

from app.auth import get_current_user
from app.core.dependencies import get_services
from app.models import UserInDB
from app.services.demo_identity_service import get_demo_identity_service


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


CurrentUser = Annotated[UserInDB, Depends(get_current_user)]
ServicesDep = Annotated[AppServices, Depends(get_services)]

router = APIRouter()


def _project_upload_payload(
    *, services: ServicesDep, username: str, payload: dict[str, Any] | None
) -> dict[str, Any]:
    if not isinstance(payload, dict):
        return {}
    demo_service = get_demo_identity_service(services)
    projected = dict(payload)
    case_key = str(
        projected.get("active_case_key")
        or projected.get("session_case_key")
        or projected.get("case_key")
        or ""
    ).strip()
    case_number = str(
        projected.get("confirmed_case_number")
        or projected.get("provisional_case_number")
        or projected.get("case_number")
        or ""
    ).strip()
    patient_id = str(
        projected.get("confirmed_patient_id")
        or projected.get("provisional_patient_id")
        or projected.get("patient_id")
        or ""
    ).strip()
    patient_name = str(
        projected.get("confirmed_patient_name")
        or projected.get("provisional_patient_name")
        or projected.get("patient_name")
        or ""
    ).strip()
    if case_key:
        visible = demo_service.project_case_identity(
            username=username,
            case_key=case_key,
            case_number=case_number,
            patient_id=patient_id,
            patient_name=patient_name,
        )
        projected["case_key"] = visible["case_key"]
        if projected.get("active_case_key") is not None:
            projected["active_case_key"] = visible["case_key"]
        if projected.get("session_case_key") is not None:
            projected["session_case_key"] = visible["case_key"]
        if projected.get("provisional_patient_id") is not None:
            projected["provisional_patient_id"] = visible["patient_id"]
        if projected.get("provisional_patient_name") is not None:
            projected["provisional_patient_name"] = visible["patient_name"]
        if projected.get("confirmed_patient_id") is not None:
            projected["confirmed_patient_id"] = visible["patient_id"]
        if projected.get("confirmed_patient_name") is not None:
            projected["confirmed_patient_name"] = visible["patient_name"]
        if projected.get("patient_id") is not None:
            projected["patient_id"] = visible["patient_id"]
        if projected.get("patient_name") is not None:
            projected["patient_name"] = visible["patient_name"]
    uploads = projected.get("uploads")
    if isinstance(uploads, list):
        projected["uploads"] = [
            _project_upload_payload(services=services, username=username, payload=item)
            for item in uploads
            if isinstance(item, dict)
        ]
    return projected


class ConfirmIndividualUploadBody(BaseModel):
    model_config = ConfigDict(extra="ignore")

    case_number: str = ""
    patient_id: str = ""
    patient_name: str = ""
    effective_document_type: str = ""


class CancelIndividualUploadBody(BaseModel):
    model_config = ConfigDict(extra="ignore")

    reason: str = ""


@router.post("/api/cargue-individual/historia")
async def create_individual_story_upload(
    file: Annotated[UploadFile, File(...)],
    current_user: CurrentUser,
    services: ServicesDep,
) -> JSONResponse:
    try:
        contents = await file.read()
        result = services.individual_ingestion_runtime.create_upload.execute(
            filename=file.filename or "",
            contents=contents,
            username=current_user.username,
            selected_document_type="historia_clinica",
        )
        return JSONResponse(
            status_code=status.HTTP_202_ACCEPTED,
            content=_project_upload_payload(
                services=services, username=current_user.username, payload=result
            ),
        )
    except ValueError as exc:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc))


@router.post("/api/cargue-individual/soporte")
async def create_individual_support_upload(
    file: Annotated[UploadFile, File(...)],
    tipo_documento: Annotated[str, Form(...)],
    case_key: Annotated[str, Form(...)],
    current_user: CurrentUser,
    services: ServicesDep,
    session_id: Annotated[str, Form()] = "",
    lock_case_selection: Annotated[bool, Form()] = False,
) -> JSONResponse:
    try:
        contents = await file.read()
        real_case_key = get_demo_identity_service(services).resolve_case_key(
            username=current_user.username,
            visible_case_key=str(case_key or "").strip(),
        )
        result = services.individual_ingestion_runtime.create_upload.execute(
            filename=file.filename or "",
            contents=contents,
            username=current_user.username,
            selected_document_type=str(tipo_documento or "").strip(),
            provided_case_key=real_case_key,
            session_id=str(session_id or "").strip(),
            lock_case_selection=bool(lock_case_selection),
        )
        return JSONResponse(
            status_code=status.HTTP_202_ACCEPTED,
            content=_project_upload_payload(
                services=services, username=current_user.username, payload=result
            ),
        )
    except ValueError as exc:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc))


@router.get("/api/cargue-individual/pending-latest")
async def get_latest_pending_individual_upload(
    current_user: CurrentUser,
    services: ServicesDep,
) -> JSONResponse:
    payload = services.individual_ingestion_runtime.get_latest_pending.execute(current_user.username)
    return JSONResponse(
        status_code=status.HTTP_200_OK,
        content={
            "upload": _project_upload_payload(
                services=services, username=current_user.username, payload=payload
            )
            if isinstance(payload, dict)
            else payload
        },
    )


@router.get("/api/cargue-individual/sesion-activa")
async def get_active_individual_upload_session(
    current_user: CurrentUser,
    services: ServicesDep,
) -> JSONResponse:
    payload = services.individual_ingestion_runtime.get_active_session.execute(current_user.username)
    return JSONResponse(
        status_code=status.HTTP_200_OK,
        content={
            "session": _project_upload_payload(
                services=services, username=current_user.username, payload=payload
            )
            if isinstance(payload, dict)
            else payload
        },
    )


@router.get("/api/cargue-individual/sesiones")
async def list_individual_upload_sessions(
    current_user: CurrentUser,
    services: ServicesDep,
    include_terminal: bool = False,
) -> JSONResponse:
    sessions = services.individual_ingestion_runtime.list_sessions.execute(
        current_user.username,
        include_terminal=include_terminal,
    )
    return JSONResponse(
        status_code=status.HTTP_200_OK,
        content={
            "sessions": [
                _project_upload_payload(services=services, username=current_user.username, payload=item)
                for item in sessions
            ]
        },
    )


@router.get("/api/cargue-individual/sesiones/{session_id}")
async def get_individual_upload_session(
    session_id: str,
    current_user: CurrentUser,
    services: ServicesDep,
) -> JSONResponse:
    payload = services.individual_ingestion_runtime.get_session.execute(
        current_user.username,
        session_id=session_id,
    )
    if not isinstance(payload, dict):
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Sesión manual no encontrada.")
    return JSONResponse(
        status_code=status.HTTP_200_OK,
        content={
            "session": _project_upload_payload(
                services=services, username=current_user.username, payload=payload
            )
        },
    )


@router.get("/api/cargue-individual/{upload_id}")
async def get_individual_upload_status(
    upload_id: str,
    current_user: CurrentUser,
    services: ServicesDep,
) -> JSONResponse:
    payload = services.individual_ingestion_runtime.get_upload_status.execute(
        upload_id,
        username=current_user.username,
    )
    if payload is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Carga individual no encontrada.")
    return JSONResponse(
        status_code=status.HTTP_200_OK,
        content=_project_upload_payload(services=services, username=current_user.username, payload=payload),
    )


@router.post("/api/cargue-individual/{upload_id}/confirmar")
async def confirm_individual_upload(
    upload_id: str,
    body: Annotated[ConfirmIndividualUploadBody, Body(default_factory=ConfirmIndividualUploadBody)],
    current_user: CurrentUser,
    services: ServicesDep,
) -> JSONResponse:
    try:
        real_patient_id = get_demo_identity_service(services).resolve_patient_id(
            username=current_user.username,
            visible_patient_id=body.patient_id,
        )
        real_patient_name = get_demo_identity_service(services).resolve_patient_name(
            username=current_user.username,
            visible_patient_name=body.patient_name,
        )
        payload = services.individual_ingestion_runtime.confirm_upload.execute(
            upload_id=upload_id,
            username=current_user.username,
            confirmed_case_number=body.case_number,
            confirmed_patient_id=real_patient_id,
            confirmed_patient_name=real_patient_name,
            confirmed_effective_document_type=body.effective_document_type,
        )
        return JSONResponse(
            status_code=status.HTTP_202_ACCEPTED,
            content={"upload_id": upload_id, "status": payload.get("status", "")},
        )
    except ValueError as exc:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc))


@router.post("/api/cargue-individual/{upload_id}/cancelar")
async def cancel_individual_upload(
    upload_id: str,
    body: Annotated[CancelIndividualUploadBody, Body(default_factory=CancelIndividualUploadBody)],
    current_user: CurrentUser,
    services: ServicesDep,
) -> JSONResponse:
    try:
        payload = services.individual_ingestion_runtime.cancel_upload.execute(
            upload_id=upload_id,
            username=current_user.username,
            reason=body.reason,
        )
        return JSONResponse(
            status_code=status.HTTP_200_OK,
            content={"upload_id": upload_id, "status": payload.get("status", "")},
        )
    except ValueError as exc:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc))


@router.post("/api/cargue-individual/{upload_id}/reintentar")
async def retry_individual_upload(
    upload_id: str,
    current_user: CurrentUser,
    services: ServicesDep,
) -> JSONResponse:
    try:
        payload = services.individual_ingestion_runtime.retry_upload.execute(
            upload_id=upload_id,
            username=current_user.username,
        )
        return JSONResponse(
            status_code=status.HTTP_202_ACCEPTED,
            content={
                "upload_id": upload_id,
                "status": payload.get("status", ""),
                "message": "El procesamiento fue reencolado sobre la carga existente.",
            },
        )
    except ValueError as exc:
        raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(exc))


@router.post("/api/cargue-individual/sesiones/{session_id}/cerrar")
async def close_individual_upload_session(
    session_id: str,
    current_user: CurrentUser,
    services: ServicesDep,
) -> JSONResponse:
    try:
        payload = services.individual_ingestion_runtime.close_session.execute(
            username=current_user.username,
            session_id=session_id,
        )
        return JSONResponse(
            status_code=status.HTTP_200_OK,
            content={
                "session": _project_upload_payload(
                    services=services,
                    username=current_user.username,
                    payload=payload,
                )
            },
        )
    except ValueError as exc:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc))
