import sys
import threading
import uuid
from datetime import datetime
from pathlib import Path

from . import config
from .flow_engine import run_flow

_RUNS: dict[str, dict] = {}
_LOCK = threading.Lock()


def _append_log(run_id: str, message: str) -> None:
    line = f"[{datetime.now().strftime('%H:%M:%S')}] {message}"
    with _LOCK:
        _RUNS[run_id]["logs"].append(line)
    # stderr, inte stdout: MCP-servern (rpa/mcp_server.py) talar JSON-RPC över
    # stdout och en körning startad därifrån får inte skriva dit.
    print(f"[run {run_id}] {message}", file=sys.stderr)


def start_run(
    flow,
    close_mode: str = "always",
    settings: dict | None = None,
    initial_vars: dict | None = None,
    entry_subflow: str | None = None,
) -> str:
    """`flow` = nod/kant-grafen ({nodes, edges, start}) eller gammalt {steps} /
    en steglista (migreras internt). entry_subflow kör bara den lokala subgrafen
    med det id:t (för att testa en subflow-flik)."""
    run_id = uuid.uuid4().hex[:12]
    close_event = threading.Event()
    trace_path = config.TRACE_DIR / f"{run_id}.zip"
    with _LOCK:
        _RUNS[run_id] = {
            "status": "running",
            "logs": [],
            "browser_open": True,
            "close_event": close_event,
            "trace_path": trace_path,
            "trace_ready": False,
            "vars": {},
        }

    def on_finished(error: Exception | None, vars_: dict) -> None:
        with _LOCK:
            _RUNS[run_id]["status"] = "error" if error else "done"
            _RUNS[run_id]["vars"] = vars_

    def target() -> None:
        try:
            run_flow(
                flow,
                log=lambda m: _append_log(run_id, m),
                download_dir=config.DOWNLOAD_DIR,
                close_mode=close_mode,
                close_event=close_event,
                on_finished=on_finished,
                trace_path=trace_path,
                settings=settings,
                initial_vars=initial_vars,
                entry_subflow=entry_subflow,
            )
        except Exception as exc:  # noqa: BLE001
            _append_log(run_id, f"Oväntat fel: {exc}")
            with _LOCK:
                _RUNS[run_id]["status"] = "error"
        finally:
            with _LOCK:
                _RUNS[run_id]["browser_open"] = False
                _RUNS[run_id]["trace_ready"] = trace_path.exists()

    threading.Thread(target=target, daemon=True).start()
    return run_id


def get_run(run_id: str) -> dict | None:
    with _LOCK:
        run = _RUNS.get(run_id)
        if run is None:
            return None
        return {
            "status": run["status"],
            "logs": list(run["logs"]),
            "browser_open": run["browser_open"],
            "trace_ready": run["trace_ready"],
            "vars": run["vars"],
        }


def get_trace_path(run_id: str) -> Path | None:
    with _LOCK:
        run = _RUNS.get(run_id)
        if run is None or not run["trace_ready"]:
            return None
        return run["trace_path"]


def close_browser(run_id: str) -> bool:
    """Signalerar till en väntande körning att den får stänga webbläsaren nu."""
    with _LOCK:
        run = _RUNS.get(run_id)
        if run is None:
            return False
        run["close_event"].set()
        return True
