from __future__ import annotations

import json
from typing import TYPE_CHECKING, Annotated, Any

from fastapi import APIRouter, Depends, HTTPException, Request, Security
from fastapi.responses import JSONResponse, Response
from pydantic import ValidationError
from starlette.status import HTTP_400_BAD_REQUEST, HTTP_404_NOT_FOUND

from app.auth import get_current_user
from app.core.dependencies import get_services
from app.models import UserInDB
from app.rips.application.models import RipsGenerationOptions
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, Security(get_current_user)]
ServicesDep = Annotated[AppServices, Depends(get_services)]


def _normalize_case_key(case_key: str) -> str:
    normalized = str(case_key or "").strip()
    if not normalized:
        raise HTTPException(
            status_code=HTTP_400_BAD_REQUEST, detail="El parámetro 'case_key' es obligatorio."
        )
    return normalized


def _serialize_result(result: Any) -> dict[str, Any]:
    return result.model_dump(mode="python", exclude_none=True)


def _serialize_download_payload(result: Any) -> dict[str, Any]:
    serialized = _serialize_result(result)
    payload = serialized.get("payload")
    return payload if isinstance(payload, dict) else {}


def _resolve_real_case_key(*, services: ServicesDep, username: str, case_key: str) -> str:
    normalized = _normalize_case_key(case_key)
    return get_demo_identity_service(services).resolve_case_key(
        username=username,
        visible_case_key=normalized,
    )


def _project_case_key(*, services: ServicesDep, username: str, case_key: str) -> str:
    return str(
        get_demo_identity_service(services).project_case_identity(
            username=username,
            case_key=case_key,
        ).get("case_key", case_key)
    ).strip()


def _normalize_scope_level(scope_level: str | None) -> str:
    normalized = str(scope_level or "provider_payer").strip().lower()
    allowed = {"global", "provider", "provider_payer", "provider_payer_site"}
    if normalized not in allowed:
        raise HTTPException(
            status_code=HTTP_400_BAD_REQUEST,
            detail="scope_level invalido. Usa global, provider, provider_payer o provider_payer_site.",
        )
    return normalized


def _build_generation_options(raw_options: Any) -> RipsGenerationOptions:
    try:
        return RipsGenerationOptions(**raw_options)
    except ValidationError as exc:
        raise HTTPException(
            status_code=HTTP_400_BAD_REQUEST,
            detail=exc.errors(),
        ) from exc


@router.post("/api/rips/cases/{case_key}")
async def generate_case_rips(
    case_key: str,
    request: Request,
    current_user: CurrentUser,
    services: ServicesDep,
) -> JSONResponse:
    normalized_case_key = _resolve_real_case_key(
        services=services,
        username=current_user.username,
        case_key=case_key,
    )
    payload = await request.json()
    if not isinstance(payload, dict):
        raise HTTPException(
            status_code=HTTP_400_BAD_REQUEST, detail="Payload invalido."
        )
    if "options" not in payload:
        raise HTTPException(
            status_code=HTTP_400_BAD_REQUEST, detail="El campo 'options' es obligatorio."
        )

    result = services.rips_service.generate_case_rips(
        current_user.username,
        normalized_case_key,
        options=_build_generation_options(payload["options"]),
        regen=bool(payload.get("regen", False)),
        persist=bool(payload.get("persist", True)),
    )
    serialized = _serialize_result(result)
    serialized["case_key"] = _project_case_key(
        services=services,
        username=current_user.username,
        case_key=normalized_case_key,
    )
    return JSONResponse(content=serialized)


@router.get("/api/rips/cases/{case_key}")
async def get_case_rips(
    case_key: str,
    current_user: CurrentUser,
    services: ServicesDep,
) -> JSONResponse:
    normalized_case_key = _resolve_real_case_key(
        services=services,
        username=current_user.username,
        case_key=case_key,
    )
    result = services.rips_service.get_latest_case_rips(current_user.username, normalized_case_key)
    if result is None:
        raise HTTPException(
            status_code=HTTP_404_NOT_FOUND, detail="No existe un RIPS generado para este caso."
        )
    serialized = _serialize_result(result)
    serialized["case_key"] = _project_case_key(
        services=services,
        username=current_user.username,
        case_key=normalized_case_key,
    )
    return JSONResponse(content=serialized)


@router.get("/api/rips/cases/{case_key}/download")
async def download_case_rips(
    case_key: str,
    current_user: CurrentUser,
    services: ServicesDep,
) -> Response:
    normalized_case_key = _resolve_real_case_key(
        services=services,
        username=current_user.username,
        case_key=case_key,
    )
    result = services.rips_service.download_case_rips(current_user.username, normalized_case_key)
    if result is None:
        raise HTTPException(
            status_code=HTTP_404_NOT_FOUND, detail="No existe un RIPS generado para este caso."
        )

    serialized = _serialize_result(result)
    artifact_payload = _serialize_download_payload(result)
    body = json.dumps(artifact_payload, ensure_ascii=False, indent=2, default=str)
    visible_case_key = _project_case_key(
        services=services,
        username=current_user.username,
        case_key=normalized_case_key,
    )
    filename = f"rips-{visible_case_key}-v{serialized.get('version') or 0}.json"
    headers = {"Content-Disposition": f'attachment; filename="{filename}"'}
    return Response(content=body, media_type="application/json", headers=headers)


@router.get("/api/rips/cases/{case_key}/template")
async def get_case_rips_template(
    case_key: str,
    current_user: CurrentUser,
    services: ServicesDep,
) -> JSONResponse:
    normalized_case_key = _resolve_real_case_key(
        services=services,
        username=current_user.username,
        case_key=case_key,
    )
    result = services.rips_service.get_case_template(current_user.username, normalized_case_key)
    return JSONResponse(content=_serialize_result(result))


@router.post("/api/rips/cases/{case_key}/template")
async def save_case_rips_template(
    case_key: str,
    request: Request,
    current_user: CurrentUser,
    services: ServicesDep,
) -> JSONResponse:
    normalized_case_key = _resolve_real_case_key(
        services=services,
        username=current_user.username,
        case_key=case_key,
    )
    payload = await request.json()
    if not isinstance(payload, dict):
        raise HTTPException(status_code=HTTP_400_BAD_REQUEST, detail="Payload invalido.")
    if "options" not in payload:
        raise HTTPException(
            status_code=HTTP_400_BAD_REQUEST, detail="El campo 'options' es obligatorio."
        )

    try:
        result = services.rips_service.save_case_template(
            current_user.username,
            normalized_case_key,
            options=_build_generation_options(payload["options"]),
            scope_level=_normalize_scope_level(payload.get("scope_level")),
        )
    except ValueError as exc:
        raise HTTPException(status_code=HTTP_400_BAD_REQUEST, detail=str(exc)) from exc
    return JSONResponse(content=_serialize_result(result))
