"""shell_tools.py - shell tool s policy: read=auto, write=approval."""
from __future__ import annotations

import re
import shlex
import subprocess
from pathlib import Path
from typing import Any

PROJECT_ROOT = Path("/home/oem/CyberBrain26-lab").resolve()
WORKSPACE = PROJECT_ROOT / "runner-workspace"

# Cteci whitelist (bez operatoru)
READ_PATTERNS = [
    r"^(pwd|whoami|hostname|date|uptime)\s*$",
    r"^ls\b",
    r"^cat\b",
    r"^head\b", r"^tail\b", r"^wc\b",
    r"^grep\b", r"^find\b",
    r"^df\b", r"^du\b", r"^free\b",
    r"^ps\b", r"^top\s+-b\b",
    r"^git\s+(status|log|diff|show|branch|remote)\b",
    r"^python3?\s+--version",
    r"^pip\s+(list|show)\b",
    r"^uv\s+(pip\s+list|run\s+python\s+--version)",
    r"^curl\s+-s?[SX]?\s+http",
]

HARD_DENY_PATTERNS = [
    r"rm\s+-rf\s+/",
    r"\bdd\s+if=", r"\bmkfs\b",
    r">\s*/dev/",
    r":\(\)\s*\{",  # fork bomb
    r"\.env\b", r"id_rsa", r"id_ed25519",
    r"\.ssh/", r"\.aws/",
]

SHELL_META = re.compile(r"[|><;&`$\n]")


def _classify(command: str) -> str:
    c = command.strip()
    if not c:
        return "deny"
    low = c.lower()
    for pat in HARD_DENY_PATTERNS:
        if re.search(pat, low):
            return "deny"
    if SHELL_META.search(c):
        return "write"  # pipes/redirects = write
    for pat in READ_PATTERNS:
        if re.match(pat, c):
            return "read"
    return "write"


def run_shell(command: str, cwd: str = ".", timeout: int = 30, auto_run: bool = False) -> dict[str, Any]:
    """Read prikazy spusti, write vrati needs_approval."""
    command = (command or "").strip()
    if not command:
        return {"status": "error", "error": "prazdny prikaz"}

    category = _classify(command)

    if category == "deny":
        return {"status": "denied",
                "command": command,
                "reason": "prikaz je v hard-deny seznamu"}

    if category == "write" and not auto_run:
        return {"status": "needs_approval",
                "command": command,
                "reason": "write/rizikovy prikaz - vyzaduje schvaleni uzivatelem"}

    # read = spustit
    try:
        wd = (PROJECT_ROOT / cwd).resolve() if cwd != "." else PROJECT_ROOT
        try:
            wd.relative_to(PROJECT_ROOT)
        except ValueError:
            wd = PROJECT_ROOT

        argv = shlex.split(command)
        if not argv:
            return {
                "status": "error",
                "command": command,
                "error": "prázdný příkaz",
            }

        r = subprocess.run(
            argv,
            shell=False,
            cwd=str(wd),
            capture_output=True,
            text=True,
            timeout=min(max(int(timeout), 1), 60),
        )

        out = (r.stdout or "")[:5000]
        err = (r.stderr or "")[:2000]

        return {
            "status": "success" if r.returncode == 0 else "error",
            "exit_code": r.returncode,
            "command": command,
            "cwd": str(wd.relative_to(PROJECT_ROOT)) if wd != PROJECT_ROOT else ".",
            "stdout": out,
            "stderr": err,
        }

    except subprocess.TimeoutExpired:
        return {"status": "timeout", "command": command,
                "error": f"prekrocen timeout ({timeout}s)"}
    except Exception as e:
        return {"status": "error", "command": command,
                "error": f"{type(e).__name__}: {e}"}


RUN_SHELL_TOOL = {"type": "function", "function": {
    "name": "run_shell",
    "description": (
        "Spusti shell prikaz. Read-only prikazy (ls, cat, git status, df) "
        "se spusti automaticky. Write prikazy vraci 'needs_approval'."
    ),
    "parameters": {"type": "object",
        "properties": {
            "command": {"type": "string", "description": "Shell prikaz"},
            "cwd": {"type": "string", "description": "Pracovni adresar (relativne k projektu)"},
            "timeout": {"type": "integer", "minimum": 1, "maximum": 60},
        },
        "required": ["command"], "additionalProperties": False}}}

__all__ = ["RUN_SHELL_TOOL", "run_shell"]
