"""
Sichere Ausführung registrierter Python-Skripte (nur aus erlaubtem Ordner).
"""

from __future__ import annotations

import json
import subprocess
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parent.parent
DATA_DIR = ROOT / "data"
DEFAULT_REGISTRY = DATA_DIR / "discord_scripts.json"
SCRIPT_TIMEOUT_SEC = 45


def load_registry(path: Path | None = None) -> dict:
    p = path or DEFAULT_REGISTRY
    if not p.is_file():
        return {"scripts": []}
    try:
        return json.loads(p.read_text(encoding="utf-8"))
    except (OSError, json.JSONDecodeError):
        return {"scripts": []}


def save_registry(data: dict, path: Path | None = None) -> None:
    p = path or DEFAULT_REGISTRY
    p.parent.mkdir(parents=True, exist_ok=True)
    p.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")


def list_scripts(registry: dict | None = None) -> list[dict]:
    reg = registry if registry is not None else load_registry()
    return [s for s in reg.get("scripts", []) if isinstance(s, dict) and s.get("id")]


def find_script(script_id: str, registry: dict | None = None) -> dict | None:
    sid = (script_id or "").strip().lower()
    for s in list_scripts(registry):
        if s.get("id", "").lower() == sid:
            return s
        if s.get("discord_command", "").lower() == sid:
            return s
    return None


def _resolve_script_file(scripts_dir: Path, entry: dict) -> Path | None:
    name = (entry.get("file") or "").strip()
    if not name or not name.endswith(".py"):
        return None
    base = scripts_dir.resolve()
    target = (base / name).resolve()
    try:
        target.relative_to(base)
    except ValueError:
        return None
    if target.is_file():
        return target
    return None


def run_script(
    script_id: str,
    *,
    scripts_dir: Path,
    registry_path: Path | None = None,
    args: list[str] | None = None,
) -> tuple[bool, str]:
    reg = load_registry(registry_path)
    entry = find_script(script_id, reg)
    if not entry:
        return False, f"Unbekanntes Skript: {script_id}. Nutze !scripts"

    path = _resolve_script_file(scripts_dir, entry)
    if not path:
        return False, f"Datei fehlt: {entry.get('file')}"

    cmd = [sys.executable, str(path), *(args or [])]
    try:
        proc = subprocess.run(
            cmd,
            cwd=str(scripts_dir),
            capture_output=True,
            text=True,
            timeout=SCRIPT_TIMEOUT_SEC,
            encoding="utf-8",
            errors="replace",
        )
    except subprocess.TimeoutExpired:
        return False, f"Timeout nach {SCRIPT_TIMEOUT_SEC}s"
    except OSError as e:
        return False, str(e)

    out = (proc.stdout or "").strip()
    err = (proc.stderr or "").strip()
    if proc.returncode != 0:
        msg = err or out or f"Exit-Code {proc.returncode}"
        return False, msg[:1500]
    return True, (out or "OK (keine Ausgabe)")[:1500]
