# apps/aroflo_connector_app/zones/invoices/base.py
from __future__ import annotations

from typing import Any, Dict, List, Tuple
from urllib.parse import urlencode

from ..base import ZoneOperation, ParamSpec


def _request(client: Any, method: str, params_list: List[Tuple[str, str]]) -> Any:
    var_string = urlencode(params_list)
    # OJO: params como lista para permitir where/join repetidos
    return client.request("", method=method, params=params_list, var_string=var_string)


def _raw_wrap(resp: Any, params_list: List[Tuple[str, str]]) -> Dict[str, Any]:
    return {
        "data": resp,
        "meta": {
            "params": params_list,
            "var_string": urlencode(params_list),
        },
    }


def _coerce_maxpageresults(params: Dict[str, Any]) -> str | None:
    """
    Devuelve maxpageresults como string si es válido, si no None.
    """
    if "maxpageresults" not in params:
        return None

    v = params.get("maxpageresults")
    if v is None or v == "":
        return None

    try:
        iv = int(v)
    except Exception:
        raise ValueError("maxpageresults debe ser un entero.")

    if iv <= 0:
        raise ValueError("maxpageresults debe ser > 0.")

    return str(iv)


def get_operations() -> List[ZoneOperation]:
    return [
        ZoneOperation(
            code="list_invoices",
            label="List Invoices",
            description=(
                "Devuelve una lista paginada de invoices (zona=invoices). "
                "Soporta filtros vía cláusula WHERE."
            ),
            http_method="GET",
            side_effect="read",
            idempotent=True,
            default_params={
                "where": "and|createdutc|>|2001-01-01",
                "page": 1,
                "maxpageresults": None,
                "raw": False,
            },
            params=[
                ParamSpec("where", "string", False, "Cláusula WHERE estilo AroFlo."),
                ParamSpec("page", "integer", False, "Número de página (1..N)."),
                ParamSpec("maxpageresults", "integer", False, "Límite de registros por página (si AroFlo lo soporta)."),
                ParamSpec("raw", "boolean", False, "Si es true, devuelve respuesta cruda (debug)."),
            ],
            category="invoices",
            use_cases=["Listar invoices", "Filtrar invoices usando WHERE", "Limitar resultados (maxpageresults)"],
            risk_level="low",
            requires_confirmation=False,
        ),
        ZoneOperation(
            code="get_invoice",
            label="Get Invoice",
            description="Obtiene una invoice específica filtrando por invoiceid.",
            http_method="GET",
            side_effect="read",
            idempotent=True,
            default_params={"raw": False},
            params=[
                ParamSpec("invoiceid", "string", True, "ID codificado de la invoice en AroFlo."),
                ParamSpec("raw", "boolean", False, "Si es true, devuelve respuesta cruda (debug)."),
            ],
            category="invoices",
            use_cases=["Consultar detalle de una invoice por invoiceid"],
            risk_level="low",
            requires_confirmation=False,
        ),
        ZoneOperation(
            code="get_approved_unprocessed_invoices",
            label="Get Approved Unprocessed Invoices",
            description=(
                "Trae invoices con status=Approved y linkprocessed=false. "
                "Útil para integraciones que consumen invoices pendientes."
            ),
            http_method="GET",
            side_effect="read",
            idempotent=True,
            default_params={"page": 1, "maxpageresults": None, "raw": False},
            params=[
                ParamSpec("page", "integer", False, "Número de página (1..N)."),
                ParamSpec("maxpageresults", "integer", False, "Límite de registros por página (si AroFlo lo soporta)."),
                ParamSpec("raw", "boolean", False, "Si es true, devuelve respuesta cruda (debug)."),
            ],
            category="invoices",
            use_cases=["Listar invoices aprobadas y no procesadas", "Limitar resultados (maxpageresults)"],
            risk_level="low",
            requires_confirmation=False,
        ),
        ZoneOperation(
            code="get_approved_unprocessed_invoices_updated_after",
            label="Get Approved Unprocessed Invoices Updated After",
            description=(
                "Trae invoices con status=Approved y linkprocessed=false "
                "y lastupdateddatetime > {updated_after}. "
                "Ideal para polling incremental."
            ),
            http_method="GET",
            side_effect="read",
            idempotent=True,
            default_params={"page": 1, "maxpageresults": None, "raw": False},
            params=[
                ParamSpec(
                    "updated_after",
                    "string",
                    True,
                    "Fecha/hora (YYYY-MM-DD HH:mm:ss) para filtrar lastupdateddatetime.",
                ),
                ParamSpec("page", "integer", False, "Número de página (1..N)."),
                ParamSpec("maxpageresults", "integer", False, "Límite de registros por página (si AroFlo lo soporta)."),
                ParamSpec("raw", "boolean", False, "Si es true, devuelve respuesta cruda (debug)."),
            ],
            category="invoices",
            use_cases=["Consultar invoices aprobadas desde un checkpoint de fecha/hora", "Limitar resultados (maxpageresults)"],
            risk_level="low",
            requires_confirmation=False,
        ),
        ZoneOperation(
            code="get_invoices_for_task",
            label="Get Invoices for Task",
            description="Obtiene todas las invoices asociadas a un taskid.",
            http_method="GET",
            side_effect="read",
            idempotent=True,
            default_params={"page": 1, "maxpageresults": None, "raw": False},
            params=[
                ParamSpec("taskid", "string", True, "TaskID codificado en AroFlo."),
                ParamSpec("page", "integer", False, "Número de página (1..N)."),
                ParamSpec("maxpageresults", "integer", False, "Límite de registros por página (si AroFlo lo soporta)."),
                ParamSpec("raw", "boolean", False, "Si es true, devuelve respuesta cruda (debug)."),
            ],
            category="invoices",
            use_cases=["Listar invoices por taskid (incluye partinvoice)", "Limitar resultados (maxpageresults)"],
            risk_level="low",
            requires_confirmation=False,
        ),
    ]


def supports(operation_code: str) -> bool:
    return any(op.code == operation_code for op in get_operations())


def execute(operation_code: str, client: Any, params: Dict[str, Any]) -> Any:
    raw = bool(params.get("raw", False))
    maxpageresults = _coerce_maxpageresults(params)

    if operation_code == "list_invoices":
        where = params.get("where", "and|createdutc|>|2001-01-01")
        page = str(params.get("page", 1))
        params_list: List[Tuple[str, str]] = [("zone", "invoices"), ("where", where)]
        if maxpageresults:
            params_list.append(("maxpageresults", maxpageresults))
        params_list.append(("page", page))
        resp = _request(client, "GET", params_list)
        return _raw_wrap(resp, params_list) if raw else resp

    if operation_code == "get_invoice":
        invoiceid = params["invoiceid"]
        where = f"and|invoiceid|=|{invoiceid}"
        params_list = [("zone", "invoices"), ("where", where), ("page", "1")]
        resp = _request(client, "GET", params_list)
        return _raw_wrap(resp, params_list) if raw else resp

    if operation_code == "get_approved_unprocessed_invoices":
        page = str(params.get("page", 1))
        params_list = [
            ("zone", "invoices"),
            ("where", "and|status|=|Approved"),
            ("where", "and|linkprocessed|=|false"),
        ]
        if maxpageresults:
            params_list.append(("maxpageresults", maxpageresults))
        params_list.append(("page", page))
        resp = _request(client, "GET", params_list)
        return _raw_wrap(resp, params_list) if raw else resp

    if operation_code == "get_approved_unprocessed_invoices_updated_after":
        updated_after = params["updated_after"]
        page = str(params.get("page", 1))
        params_list = [
            ("zone", "invoices"),
            ("where", "and|status|=|Approved"),
            ("where", "and|linkprocessed|=|false"),
            ("where", f"and|lastupdateddatetime|>|{updated_after}"),
        ]
        if maxpageresults:
            params_list.append(("maxpageresults", maxpageresults))
        params_list.append(("page", page))
        resp = _request(client, "GET", params_list)
        return _raw_wrap(resp, params_list) if raw else resp

    if operation_code == "get_invoices_for_task":
        taskid = params["taskid"]
        page = str(params.get("page", 1))
        params_list: List[Tuple[str, str]] = [("zone", "invoices"), ("where", f"and|taskid|=|{taskid}")]
        if maxpageresults:
            params_list.append(("maxpageresults", maxpageresults))
        params_list.append(("page", page))
        resp = _request(client, "GET", params_list)
        return _raw_wrap(resp, params_list) if raw else resp

    raise ValueError(f"[Invoices.base] Operación no soportada: {operation_code}")
