from __future__ import annotations

from typing import Any, Dict, List
import json
import os
import urllib.request
import sys
import subprocess

from ..base import ZoneOperation, ParamSpec


OP_UI_CREATE = "ui_create_timesheet_entries"


def get_operations() -> List[ZoneOperation]:
    return [
        ZoneOperation(
            code=OP_UI_CREATE,
            label="UI Create Timesheet entries",
            description=(
                "Crea entradas de timesheet usando UI automation (Playwright). "
                "Requiere storageState (bootstrap). "
                "Soporta rows opcional para definir filas dinámicas; "
                "si no se envía rows, el flujo usa filas por defecto."
            ),
            http_method="UI",
            side_effect="write",
            idempotent=False,
            default_params={
                "timesheet_bu": None,
                "timesheet_user_id": None,
                "timesheet_user_name": None,
                "timesheet_date": None,
                "rows": None,
                "dry_run": False,
                "raw": False,
            },
            params=[
                ParamSpec("timesheet_bu", "string", True, "Business Unit a seleccionar en AroFlo."),
                ParamSpec("timesheet_user_id", "string", False, "AroFlo User ID (preferido, único)."),
                ParamSpec("timesheet_user_name", "string", False, "Nombre completo del usuario (fallback)."),
                ParamSpec("timesheet_date", "string", True, "Fecha objetivo (YYYY-MM-DD)."),
                ParamSpec(
                    "rows",
                    "array",
                    False,
                    (
                        "Lista de filas (opcional). Cada item es un string con formato: "
                        "'hours=5;overhead=Admin Duties;worktype=NT;tracking=ADMIN;note=...'"
                    ),
                    items_schema={"type": "string"},
                ),
                ParamSpec("dry_run", "boolean", False, "Si true, no ejecuta UI; solo preview del comando."),
                ParamSpec("raw", "boolean", False, "Si true, incluye stdout/stderr completos del runner."),
            ],
            category="timesheets",
            use_cases=["Crear overheads por UI", "Inserción controlada cuando API es READ ONLY"],
            risk_level="high",
            requires_confirmation=True,
        ),
    ]


def supports(operation_code: str) -> bool:
    return operation_code == OP_UI_CREATE


def _preview(argv: List[str]) -> Dict[str, Any]:
    return {
        "dry_run": True,
        "invocation": "subprocess",
        "argv": argv,
    }


def _run_ui(argv: List[str], *, raw: bool = False) -> Dict[str, Any]:
    proc = subprocess.run(argv, capture_output=True, text=True, check=False)
    if raw:
        return {
            "returncode": proc.returncode,
            "stdout": proc.stdout,
            "stderr": proc.stderr,
            "argv": argv,
        }
    out_tail = (proc.stdout or "").strip().splitlines()[-30:]
    err_tail = (proc.stderr or "").strip().splitlines()[-30:]
    return {
        "returncode": proc.returncode,
        "stdout_tail": "\n".join(out_tail),
        "stderr_tail": "\n".join(err_tail),
        "argv": argv,
    }


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

    tenant_id = str(params.get("tenant_id") or params.get("tenant-id") or "").strip()
    worker_url = str(params.get("worker_url") or params.get("worker-url") or os.getenv("AROFLO_UI_WORKER_URL") or "").strip()
    if not worker_url:
        worker_url = "http://127.0.0.1:5010"
    timesheet_date = params.get("timesheet_date") or params.get("timesheet-date")
    if not timesheet_date:
        raise ValueError("timesheet_date es requerido (YYYY-MM-DD).")

    timesheet_bu = params.get("timesheet_bu") or params.get("timesheet-bu")
    if not timesheet_bu or not str(timesheet_bu).strip():
        raise ValueError("timesheet_bu es requerido (ej: 'Utility Solutions Group').")

    timesheet_user_id = str(params.get("timesheet_user_id") or params.get("timesheet-user-id") or "").strip()
    timesheet_user_name = str(params.get("timesheet_user_name") or params.get("timesheet-user-name") or "").strip()

    if not timesheet_user_id and not timesheet_user_name:
        raise ValueError("Debes enviar timesheet_user_id o timesheet_user_name (al menos uno).")

    if operation_code == OP_UI_CREATE:
        rows = params.get("rows") or []
        if rows:
            if not isinstance(rows, list):
                raise ValueError("rows debe ser una lista de strings.")
        payload = {
            "tenant_id": tenant_id,
            "timesheet_bu": str(timesheet_bu),
            "timesheet_date": str(timesheet_date),
            "timesheet_user_id": timesheet_user_id,
            "timesheet_user_name": timesheet_user_name,
            "rows": [str(r).strip() for r in rows if str(r).strip()],
        }

        if dry_run:
            return {
                "dry_run": True,
                "invocation": "http",
                "url": f"{worker_url}/ui/timesheets/create",
                "payload": payload,
            }

        req = urllib.request.Request(
            f"{worker_url}/ui/timesheets/create",
            data=json.dumps(payload).encode("utf-8"),
            headers={"Content-Type": "application/json"},
            method="POST",
        )
        with urllib.request.urlopen(req, timeout=120) as resp:
            body = resp.read().decode("utf-8")
        return {"returncode": 0, "stdout": body, "stderr": "", "url": req.full_url}

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