#/apps/aroflo_connector_app/zones/timesheets/queries.py
from __future__ import annotations

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

from ..base import ZoneOperation, ParamSpec


# -------------------------
# Helpers internos (igual estilo lastupdate)
# -------------------------
def _request(client: Any, method: str, params_list: List[Tuple[str, str]]) -> Any:
    """
    Construye params como lista de tuplas para:
    - permitir where repetidos
    - mantener orden estable
    """
    var_string = urlencode(params_list)
    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 _compact_timesheets(resp: Any, max_items: int) -> Any:
    """
    Recorte local para UX/CLI/telemetría.
    No afecta server-side.
    """
    try:
        if not isinstance(resp, dict):
            return resp
        zr = resp.get("zoneresponse") or {}
        items = zr.get("timesheets")
        if not isinstance(items, list):
            return resp

        hint = len(items)
        zr["compact_applied"] = True
        zr["compact_max_items"] = max_items
        zr["original_items_hint"] = hint

        if max_items > 0 and len(items) > max_items:
            zr["timesheets"] = items[:max_items]

        resp["zoneresponse"] = zr
        return resp
    except Exception:
        return resp


def _build_where_clauses(
    *,
    timesheetid: Optional[str],
    taskid: Optional[str],
    userid: Optional[str],
    type_: Optional[str],
    workdate: Optional[str],
    workdate_op: str,
) -> List[Tuple[str, str]]:
    """
    Construye wheres AroFlo para la zona timesheets.

    Campos soportados por doc:
      - timesheetid, taskid, userid, type, workdate

    Nota:
    - doc lista workdate como DATE(YYYY-MM-DD)
    - ejemplos usan fechas con slash y timestamp; nosotros normalizamos a string sin validar aquí
    """
    wheres: List[Tuple[str, str]] = []

    if timesheetid:
        wheres.append(("where", f"and|timesheetid|=|{timesheetid}"))
    if taskid:
        wheres.append(("where", f"and|taskid|=|{taskid}"))
    if userid:
        wheres.append(("where", f"and|userid|=|{userid}"))
    if type_:
        # La doc muestra valores Productive / Non-Productive / Expense
        # (mantener el casing que mande el usuario; AroFlo suele tolerar pero no asumimos)
        wheres.append(("where", f"and|type|=|{type_}"))
    if workdate:
        op = (workdate_op or ">").strip()
        wheres.append(("where", f"and|workdate|{op}|{workdate}"))

    return wheres


# -------------------------
# Operaciones (manifest IA)
# -------------------------
def get_operations() -> List[ZoneOperation]:
    return [
        ZoneOperation(
            code="list_timesheets",
            label="Get Timesheets",
            description=(
                "Lista timesheets. Soporta filtros por timesheetid, taskid, userid, type y workdate. "
                "Por defecto AroFlo aplica: workdate > (UTC - 30 días)."
            ),
            http_method="GET",
            side_effect="read",
            idempotent=True,
            default_params={
                "timesheetid": None,
                "taskid": None,
                "userid": None,
                "type": None,  # Productive, Non-Productive, Expense
                "workdate": None,
                "workdate_op": ">",  # =, >, <, etc.
                "page": 1,
                "page_size": 500,  # maxpageresults
                "order": "workdate|asc",
                "raw": False,
                "max_items": 50,   # recorte local
            },
            params=[
                ParamSpec("timesheetid", "string", False, "Filtro exacto por timesheetid (AroFlo ID)."),
                ParamSpec("taskid", "string", False, "Filtro exacto por taskid (AroFlo ID)."),
                ParamSpec("userid", "string", False, "Filtro exacto por userid (AroFlo ID)."),
                ParamSpec(
                    "type",
                    "string",
                    False,
                    "Filtro por type: Productive, Non-Productive, Expense.",
                    enum=["Productive", "Non-Productive", "Expense"],
                ),
                ParamSpec("workdate", "string", False, 'Fecha para filtrar. Recomendado "YYYY-MM-DD".'),
                ParamSpec("workdate_op", "string", False, "Operador para workdate: =, >, <, != ..."),
                ParamSpec("page", "integer", False, "Número de página (1..N)."),
                ParamSpec("page_size", "integer", False, "maxpageresults server-side (default 500)."),
                ParamSpec("order", "string", False, 'Orden, ejemplo: "workdate|asc" o "workdate|desc".'),
                ParamSpec("raw", "boolean", False, "Si true, devuelve respuesta cruda + meta debug."),
                ParamSpec("max_items", "integer", False, "Recorte local para lista timesheets (UX/CLI)."),
            ],
            category="timesheets",
            use_cases=[
                "Listar horas cargadas en los últimos 30 días (default AroFlo)",
                "Filtrar por usuario para auditoría de horas",
                "Filtrar por taskid para revisar mano de obra de una tarea",
                "Filtrar por type para separar Productive vs overhead/leave (Non-Productive) vs Expense",
            ],
            examples=[
                {
                    "prompt": "Listame los timesheets del usuario JCQ6XyRRUCAgCg==",
                    "params": {"userid": "JCQ6XyRRUCAgCg==", "page": 1},
                },
                {
                    "prompt": "Buscá el timesheet JSYqRyRRPFggCg==",
                    "params": {"timesheetid": "JSYqRyRRPFggCg==", "page": 1},
                },
                {
                    "prompt": "Timesheets productivos desde 2021-05-19",
                    "params": {"type": "Productive", "workdate": "2021-05-19", "workdate_op": ">"},
                },
            ],
            risk_level="low",
            requires_confirmation=False,
        ),

        # Alias por consistencia (si querés el patrón list vs get)
        ZoneOperation(
            code="get_timesheets",
            label="Get Timesheets (alias)",
            description="Alias de list_timesheets.",
            http_method="GET",
            side_effect="read",
            idempotent=True,
            default_params={
                "page": 1,
                "page_size": 500,
                "order": "workdate|asc",
                "raw": False,
                "max_items": 50,
            },
            params=[
                ParamSpec("timesheetid", "string", False, "Filtro exacto por timesheetid (AroFlo ID)."),
                ParamSpec("taskid", "string", False, "Filtro exacto por taskid (AroFlo ID)."),
                ParamSpec("userid", "string", False, "Filtro exacto por userid (AroFlo ID)."),
                ParamSpec(
                    "type",
                    "string",
                    False,
                    "Filtro por type: Productive, Non-Productive, Expense.",
                    enum=["Productive", "Non-Productive", "Expense"],
                ),
                ParamSpec("workdate", "string", False, 'Fecha para filtrar. Recomendado "YYYY-MM-DD".'),
                ParamSpec("workdate_op", "string", False, "Operador para workdate: =, >, <, != ..."),
                ParamSpec("page", "integer", False, "Número de página (1..N)."),
                ParamSpec("page_size", "integer", False, "maxpageresults server-side (default 500)."),
                ParamSpec("order", "string", False, 'Orden, ejemplo: "workdate|asc" o "workdate|desc".'),
                ParamSpec("raw", "boolean", False, "Si true, devuelve respuesta cruda + meta debug."),
                ParamSpec("max_items", "integer", False, "Recorte local para lista timesheets (UX/CLI)."),
            ],
            category="timesheets",
            use_cases=["Mismo uso que list_timesheets"],
            risk_level="low",
            requires_confirmation=False,
        ),
    ]


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


# -------------------------
# Ejecución
# -------------------------
def execute(operation_code: str, client: Any, params: Dict[str, Any]) -> Any:
    # Normalizamos alias
    if operation_code == "get_timesheets":
        operation_code = "list_timesheets"

    if operation_code != "list_timesheets":
        raise ValueError(f"[Timesheets.queries] Operación no soportada: {operation_code}")

    raw = bool(params.get("raw", False))

    timesheetid = (params.get("timesheetid") or "").strip() or None
    taskid = (params.get("taskid") or "").strip() or None
    userid = (params.get("userid") or "").strip() or None
    type_ = (params.get("type") or "").strip() or None
    workdate = (params.get("workdate") or "").strip() or None
    workdate_op = (params.get("workdate_op") or ">").strip() or ">"

    page = str(params.get("page", 1))
    page_size = str(params.get("page_size", 500))
    order = (params.get("order") or "workdate|asc").strip()
    max_items = int(params.get("max_items") or 0)

    params_list: List[Tuple[str, str]] = [
        ("zone", "timesheets"),
        ("page", page),
        ("maxpageresults", page_size),
    ]

    # order es opcional; la doc menciona workdate
    if order:
        params_list.append(("order", order))

    # WHERE repetidos
    params_list.extend(
        _build_where_clauses(
            timesheetid=timesheetid,
            taskid=taskid,
            userid=userid,
            type_=type_,
            workdate=workdate,
            workdate_op=workdate_op,
        )
    )

    resp = _request(client, "GET", params_list)

    if raw:
        return _raw_wrap(resp, params_list)

    if max_items > 0:
        return _compact_timesheets(resp, max_items)

    return resp
