"""Local command execution helpers.

These mirror the remote SSH helpers but execute commands on the API host
itself. They are used for the built-in local monitoring machine.
"""

from __future__ import annotations

import json
import logging
import posixpath
import shlex
import subprocess
from dataclasses import dataclass

logger = logging.getLogger(__name__)


@dataclass
class CommandResult:
    """Plain result object returned by local command execution."""

    command: str
    exit_status: int
    stdout: str
    stderr: str


class LocalCommandClient:
    """Execute the same POSIX shell snippets used by the SSH client locally."""

    def __init__(self, timeout: int = 20):
        self.timeout = timeout

    def run(self, command: str, timeout: int | None = None) -> CommandResult:
        shell_command = ["/bin/sh", "-c", command]
        logger.debug("Local run timeout=%s command=%s", timeout or self.timeout, command)
        proc = subprocess.run(
            shell_command,
            capture_output=True,
            text=True,
            timeout=timeout or self.timeout,
        )
        result = CommandResult(
            command=command,
            exit_status=proc.returncode,
            stdout=proc.stdout,
            stderr=proc.stderr,
        )
        if result.exit_status == 0:
            logger.debug("Local command ok exit_status=%s", result.exit_status)
        else:
            logger.warning(
                "Local command failed exit_status=%s stderr=%s",
                result.exit_status,
                result.stderr.strip() or result.stdout.strip(),
            )
        return result

    def list_dir(self, path: str) -> CommandResult:
        quoted = shlex.quote(path)
        not_dir_message = shlex.quote(f"Not a directory: {path}")
        command = (
            f"test -d {quoted} || "
            f"{{ echo {not_dir_message} >&2; exit 20; }}; "
            f"find {quoted} -maxdepth 1 -mindepth 1 -printf "
            "'%y\\t%s\\t%T@\\t%f\\0' | python3 -c "
            + shlex.quote(
                "import sys,json; data=sys.stdin.buffer.read().split(b'\\0'); "
                "rows=[]\n"
                "for row in data:\n"
                "    if not row: continue\n"
                "    t,s,m,n=row.decode('utf-8','replace').split('\\t',3)\n"
                "    rows.append({'type':t,'size':int(s),'mtime':float(m),'name':n})\n"
                "print(json.dumps(rows))"
            )
        )
        return self.run(command)

    def stat_path(self, path: str) -> CommandResult:
        quoted = shlex.quote(path)
        return self.run(f"stat --printf='%F\\n%s bytes\\n%y\\n%n\\n' {quoted}")

    def ffprobe_json(self, path: str) -> dict[str, object]:
        quoted = shlex.quote(path)
        result = self.run(
            "ffprobe -v error -show_format -show_streams -print_format json " + quoted,
            timeout=60,
        )
        if result.exit_status != 0:
            raise RuntimeError(result.stderr or result.stdout or "ffprobe failed")
        return json.loads(result.stdout)

    @staticmethod
    def join(parent: str, child: str) -> str:
        return posixpath.normpath(posixpath.join(parent, child))
