"""Shell mode execution helpers for the TUI."""

from __future__ import annotations

import os
import subprocess
from collections.abc import Callable
from dataclasses import dataclass


@dataclass(slots=True, frozen=True)
class ShellResult:
    """Structured shell execution result."""

    command: str
    exit_code: int
    stdout: str
    stderr: str


def looks_dangerous(command: str) -> bool:
    """Best-effort dangerous command detector."""
    lowered = command.strip().lower()
    patterns = (
        "rm -rf /",
        "git push --force",
        "mkfs.",
        "dd if=",
        ":(){:|:&};:",
    )
    return any(pattern in lowered for pattern in patterns)


def run_shell_command(
    command: str,
    *,
    confirm_dangerous: Callable[[str], bool] | None = None,
    timeout_seconds: int = 30,
) -> ShellResult:
    """Execute shell command with basic safeguards.

    Threat model:
    - Shell mode is a convenience feature for local development, not a sandbox.
    - We block obviously dangerous command patterns unless explicitly confirmed.
    - We apply a timeout to avoid hanging the UI event loop indefinitely.
    """
    if not command.strip():
        return ShellResult(
            command=command, exit_code=2, stdout="", stderr="empty command"
        )
    if os.environ.get("CLEVERAGENTS_DISABLE_SHELL_MODE", "").strip() in {"1", "true"}:
        return ShellResult(
            command=command,
            exit_code=2,
            stdout="",
            stderr="shell mode is disabled",
        )
    if looks_dangerous(command):
        confirmed = False
        if confirm_dangerous is not None:
            confirmed = confirm_dangerous(command)
        if not confirmed:
            return ShellResult(
                command=command,
                exit_code=1,
                stdout="",
                stderr="blocked dangerous shell command",
            )
    try:
        proc = subprocess.run(
            command,
            shell=True,
            text=True,
            capture_output=True,
            timeout=timeout_seconds,
        )  # nosec B602
    except subprocess.TimeoutExpired:
        return ShellResult(
            command=command,
            exit_code=124,
            stdout="",
            stderr=f"command timed out after {timeout_seconds}s",
        )
    return ShellResult(
        command=command,
        exit_code=proc.returncode,
        stdout=proc.stdout or "",
        stderr=proc.stderr or "",
    )
