"""Unified execution records: script runs + server batch jobs."""

from __future__ import annotations

import json
from typing import Any, Optional

from server.application.services.script_service import ScriptService
from server.application.services.server_batch_service import get_batch_job, list_batch_jobs
from server.infrastructure.redis import server_batch_store as sbs


def _parse_server_ids(raw: Any) -> list[int]:
    if isinstance(raw, list):
        return [int(x) for x in raw if str(x).isdigit()]
    if isinstance(raw, str):
        try:
            parsed = json.loads(raw or "[]")
        except json.JSONDecodeError:
            return []
        if isinstance(parsed, list):
            return [int(x) for x in parsed if str(x).isdigit()]
    return []


def _script_label(view: dict[str, Any]) -> str:
    label = (view.get("label") or "").strip()
    if label:
        return label
    cmd = (view.get("command") or "").replace("[long_task] ", "").strip()
    if len(cmd) > 48:
        return cmd[:48] + "…"
    return cmd or f"脚本执行 #{view.get('id')}"


def script_view_to_record(view: dict[str, Any]) -> dict[str, Any]:
    sids = _parse_server_ids(view.get("server_ids"))
    if not sids and view.get("results"):
        sids = [int(k) for k in view["results"] if str(k).isdigit()]
    return {
        "id": int(view["id"]),
        "kind": "script",
        "label": _script_label(view),
        "status": view.get("status") or "running",
        "progress": view.get("progress") or "—",
        "server_count": len(sids),
        "operator": view.get("operator") or "",
        "started_at": view.get("started_at"),
        "completed_at": view.get("completed_at"),
        "command": view.get("command"),
        "op": None,
    }


def batch_view_to_record(view: dict[str, Any]) -> dict[str, Any]:
    total = int(view.get("total") or 0)
    if not total:
        total = len(_parse_server_ids(view.get("server_ids")))
    return {
        "id": int(view.get("job_id") or view.get("id") or 0),
        "kind": "server_batch",
        "label": view.get("label") or "服务器批量任务",
        "status": view.get("status") or "running",
        "progress": view.get("progress") or "—",
        "server_count": total,
        "operator": view.get("operator") or "",
        "started_at": view.get("started_at"),
        "completed_at": view.get("completed_at"),
        "command": None,
        "op": view.get("op") or "",
    }


async def _overlay_batch_live(row_api: dict[str, Any]) -> dict[str, Any]:
    job_id = int(row_api.get("job_id") or row_api.get("id") or 0)
    if not job_id:
        return row_api
    live = await sbs.get_live(job_id)
    if live:
        fresh = sbs.live_to_api(live)
        fresh["started_at"] = row_api.get("started_at")
        fresh["completed_at"] = row_api.get("completed_at")
        return fresh
    return row_api


async def list_execution_records(
    *,
    script_service: ScriptService,
    limit: int = 50,
    offset: int = 0,
    status: Optional[str] = None,
    kind: Optional[str] = None,
) -> dict[str, Any]:
    """Merge script executions and server batch jobs, newest first."""
    if kind == "script":
        script_data = await script_service.list_executions(limit=limit, offset=offset, status=status)
        items = [script_view_to_record(v) for v in script_data.get("items") or []]
        return {
            "items": items,
            "total": int(script_data.get("total") or 0),
            "limit": limit,
            "offset": offset,
        }

    if kind == "server_batch":
        batch_data = await list_batch_jobs(limit=limit, offset=offset, op=None)
        items = []
        for raw in batch_data.get("items") or []:
            if status and raw.get("status") != status:
                continue
            overlaid = await _overlay_batch_live(raw)
            items.append(batch_view_to_record(overlaid))
        total = int(batch_data.get("total") or 0)
        if status:
            total = len(items)
        return {"items": items, "total": total, "limit": limit, "offset": offset}

    fetch_n = min(max(limit + offset, limit), 200)
    script_data = await script_service.list_executions(limit=fetch_n, offset=0, status=status)
    batch_data = await list_batch_jobs(limit=fetch_n, offset=0)

    merged: list[dict[str, Any]] = []
    for view in script_data.get("items") or []:
        merged.append(script_view_to_record(view))

    for raw in batch_data.get("items") or []:
        if status and raw.get("status") != status:
            continue
        overlaid = await _overlay_batch_live(raw)
        merged.append(batch_view_to_record(overlaid))

    merged.sort(key=lambda x: x.get("started_at") or "", reverse=True)
    page = merged[offset: offset + limit]
    total = int(script_data.get("total") or 0) + int(batch_data.get("total") or 0)
    return {"items": page, "total": total, "limit": limit, "offset": offset}


async def get_execution_record_detail(
    *,
    script_service: ScriptService,
    record_id: int,
    kind: str,
    fetch_logs: bool = False,
) -> Optional[dict[str, Any]]:
    if kind == "server_batch":
        view = await get_batch_job(record_id)
        if not view:
            return None
        results: dict[str, Any] = {}
        for row in view.get("results") or []:
            sid = str(row.get("server_id") or "")
            if not sid.isdigit():
                continue
            ok = bool(row.get("success"))
            results[sid] = {
                "status": "success" if ok else "failed",
                "stdout": row.get("stdout") or "",
                "stderr": row.get("error") or "",
                "exit_code": 0 if ok else 1,
            }
        server_ids = [int(k) for k in results]
        server_names = {
            str(r.get("server_id")): (r.get("server_name") or "")
            for r in (view.get("results") or [])
            if r.get("server_id") is not None
        }
        return {
            "id": record_id,
            "kind": "server_batch",
            "label": view.get("label") or "",
            "op": view.get("op") or "",
            "status": view.get("status") or "running",
            "progress": view.get("progress") or "—",
            "operator": view.get("operator") or "",
            "started_at": view.get("started_at"),
            "completed_at": view.get("completed_at"),
            "command": view.get("label") or "",
            "server_ids": json.dumps(server_ids),
            "results": results,
            "server_names": server_names,
            "long_task": False,
        }

    view = await script_service.get_execution_detail(record_id, fetch_logs=fetch_logs)
    if not view:
        return None
    view["kind"] = "script"
    return view
