from __future__ import annotations

import logging
import shutil
from pathlib import Path
from typing import Any

import requests

from config.app_config import (
    API_EXPEDIENTES_URL,
    API_PRECALIFICADOR_URL,
    COMPLETADOS_DIR,
    HTTP_TIMEOUT,
    LOTE_PATH,
    NO_INICIADOS_DIR,
    PENDIENTES_DIR,
    RESULTADO_COMPLETADO_PATH,
    RESULTADO_PENDIENTE_PATH,
)
from utils.db_helper import get_connection, get_loans
from utils.json_helper import as_number, pick, read_json, write_json

logger = logging.getLogger(__name__)


def process() -> dict[str, Any]:
    """Process the pending batch and persist a consolidated JSON."""
    lote = read_json(LOTE_PATH, default=None)
    if not lote:
        raise FileNotFoundError(
            f"No existe lote pendiente en {LOTE_PATH}. Ejecute primero la ingesta."
        )

    items = lote.get("pendientes") or []
    if not items:
        logger.warning("El lote en %s no contiene oportunidades.", LOTE_PATH)
        return {}

    PENDIENTES_DIR.mkdir(parents=True, exist_ok=True)
    result: dict[str, Any] = read_json(RESULTADO_PENDIENTE_PATH, default={}) or {}
    total = len(items)

    conexion = None
    try:
        conexion = get_connection()
    except Exception as error:
        logger.warning(
            "No se pudo abrir conexión persistente (%s). Se reintentará por oportunidad.",
            error,
        )

    with requests.Session() as session:
        for index, op in enumerate(items, start=1):
            prefix = f"[Procesando {index}/{total}] {op}"
            try:
                if op in result:
                    logger.info("%s -> OMITIDO (ya procesada)", prefix)
                    continue

                result[op] = _build(session, conexion, op)
                write_json(RESULTADO_PENDIENTE_PATH, result)
                logger.info("%s -> OK", prefix)
            except Exception as error:
                logger.error("%s -> ERROR: %s", prefix, error)

    if conexion is not None:
        try:
            conexion.close()
        except Exception:
            pass

    _finish()
    logger.info("Lote finalizado. Resultado en %s", RESULTADO_COMPLETADO_PATH)
    return result


def _build(session: requests.Session, conexion, op: str) -> dict[str, Any]:
    identidad = _identity(session, op)
    cliente = _profile(session, identidad)

    try:
        prestamos = get_loans(identidad, conexion=conexion)
    except Exception as error:
        logger.error("Fallo DB en %s (identidad %s): %s", op, identidad, error)
        return {
            "cliente": cliente,
            "prestamos": [],
            "db_error": str(error),
        }

    return {
        "cliente": cliente,
        "prestamos": prestamos,
    }


def _identity(session: requests.Session, op: str) -> str:
    response = session.post(
        API_EXPEDIENTES_URL,
        files={"oportunidad": (None, op)},
        timeout=HTTP_TIMEOUT,
    )
    response.raise_for_status()

    try:
        payload = response.json()
    except ValueError as error:
        raise ValueError(f"Respuesta de expedientes no es JSON válido: {error}") from error

    identidad = pick(payload, "identidad", "IDENTIDAD", "id_cliente")
    if not identidad:
        raise ValueError("La API de expedientes no retornó identidad")
    return str(identidad).strip()


def _profile(session: requests.Session, identidad: str) -> dict[str, Any]:
    response = session.post(
        API_PRECALIFICADOR_URL,
        files={"identidad": (None, identidad)},
        timeout=HTTP_TIMEOUT,
    )
    response.raise_for_status()

    try:
        payload = response.json()
    except ValueError:
        payload = {}
        logger.warning("Precalificador no retornó JSON válido para identidad %s", identidad)

    nombre = pick(payload, "nombre", "nombre_cliente", "cliente") or ""
    identidad_resp = pick(payload, "identidad", "IDENTIDAD") or identidad
    categoria = pick(payload, "tipo_cliente", "categoria", "tipoCliente") or ""
    monto = pick(payload, "monto", "equifax_monto", "monto_preaprobado")

    return {
        "nombre": str(nombre).strip(),
        "identidad": str(identidad_resp).strip(),
        "categoria": str(categoria).strip(),
        "monto_preaprobado": as_number(monto, 0.0),
    }


def _finish() -> None:
    if RESULTADO_PENDIENTE_PATH.exists():
        COMPLETADOS_DIR.mkdir(parents=True, exist_ok=True)
        shutil.copy2(RESULTADO_PENDIENTE_PATH, RESULTADO_COMPLETADO_PATH)
        RESULTADO_PENDIENTE_PATH.unlink(missing_ok=True)

    _clear(PENDIENTES_DIR, keep=(".gitkeep",))
    _clear(NO_INICIADOS_DIR, keep=(".gitkeep",))


def _clear(directory: Path, keep: tuple[str, ...] = ()) -> None:
    if not directory.exists():
        return
    for item in directory.iterdir():
        if item.name in keep:
            continue
        if item.is_file():
            item.unlink(missing_ok=True)
