"""OS launch helpers (kept outside windows/macos adapter trees for parity scan rules)."""

from __future__ import annotations

import os
import platform
import shutil
import subprocess
import sys
from pathlib import Path
from typing import Any


def spawn_detached(argv: list[str], *, cwd: str | None = None) -> int:
    if not argv:
        raise ValueError("empty_launch_command")
    kwargs: dict[str, Any] = {
        "stdout": subprocess.DEVNULL,
        "stderr": subprocess.DEVNULL,
        "start_new_session": True,
        "close_fds": True,
    }
    if cwd:
        kwargs["cwd"] = cwd
    proc = subprocess.Popen(argv, **kwargs)
    return int(proc.pid)


def launch_uri_windows(uri: str) -> tuple[bool, list[str], int, str]:
    uri = str(uri or "").strip()
    if not uri:
        return False, [], 0, "missing_uri"
    if sys.platform == "win32":
        try:
            os.startfile(uri)  # noqa: S606 — Windows shell association launch
            return True, ["os.startfile", uri], 0, ""
        except OSError as exc:
            return False, ["os.startfile", uri], 0, str(exc)
    cmd = ["cmd", "/c", "start", "", uri]
    try:
        pid = spawn_detached(cmd)
    except (FileNotFoundError, OSError) as exc:
        return False, cmd, 0, str(exc)
    return True, cmd, pid, ""


def launch_uri_macos(uri: str) -> tuple[bool, list[str], int, str]:
    uri = str(uri or "").strip()
    if not uri:
        return False, [], 0, "missing_uri"
    open_bin = shutil.which("open") or "/usr/bin/open"
    cmd = [open_bin, uri]
    try:
        pid = spawn_detached(cmd)
    except (FileNotFoundError, OSError) as exc:
        return False, cmd, 0, str(exc)
    return True, cmd, pid, ""


def launch_windows_entry(entry: dict[str, Any]) -> tuple[bool, list[str], int, str]:
    launch_spec = entry.get("launch_spec")
    if not isinstance(launch_spec, dict):
        return False, [], 0, "missing_launch_spec"
    argv = launch_spec.get("argv")
    argv_parts = [str(part) for part in argv] if isinstance(argv, list) else []
    binary_path = str(entry.get("binary_path") or (argv_parts[0] if argv_parts else "") or "").strip()
    display_name = str(entry.get("display_name") or "").strip()
    if binary_path:
        path = Path(binary_path)
        if path.exists():
            cmd = [str(path), *argv_parts[1:]]
            try:
                pid = spawn_detached(cmd)
            except (FileNotFoundError, OSError) as exc:
                return False, cmd, 0, str(exc)
            return True, cmd, pid, ""
    if argv_parts:
        cmd = argv_parts
        try:
            pid = spawn_detached(cmd)
        except (FileNotFoundError, OSError) as exc:
            return False, cmd, 0, str(exc)
        return True, cmd, pid, ""
    if display_name:
        return _launch_windows_start_process(display_name)
    return False, [], 0, "launch_target_missing"


def _launch_windows_start_process(display_name: str) -> tuple[bool, list[str], int, str]:
    """Start-Process fallback when inventory has display name but no binary path."""
    name = str(display_name or "").strip()
    if not name:
        return False, [], 0, "missing_display_name"
    ps = shutil.which("powershell.exe") or shutil.which("powershell") or "powershell.exe"
    escaped = name.replace("'", "''")
    cmd = [ps, "-NoProfile", "-NonInteractive", "-Command", f"Start-Process -FilePath '{escaped}'"]
    try:
        pid = spawn_detached(cmd)
    except (FileNotFoundError, OSError) as exc:
        return False, cmd, 0, str(exc)
    return True, cmd, pid, ""


def open_folder_windows(folder_path: str) -> tuple[bool, list[str], int, str]:
    """Open a folder in Explorer (preferred over generic shell association)."""
    path = str(folder_path or "").strip()
    if not path:
        return False, [], 0, "missing_path"
    if sys.platform == "win32":
        explorer = shutil.which("explorer.exe") or "explorer.exe"
        cmd = [explorer, path]
        try:
            os.startfile(path)  # noqa: S606 — native Explorer folder open
            return True, ["explorer.exe", path], 0, ""
        except OSError:
            try:
                pid = spawn_detached(cmd)
            except (FileNotFoundError, OSError) as exc:
                return False, cmd, 0, str(exc)
            return True, cmd, pid, ""
    cmd = ["cmd", "/c", "explorer.exe", path]
    try:
        pid = spawn_detached(cmd)
    except (FileNotFoundError, OSError) as exc:
        return False, cmd, 0, str(exc)
    return True, cmd, pid, ""


def empty_trash_windows() -> tuple[bool, list[str], str]:
    """Empty Recycle Bin via PowerShell Clear-RecycleBin."""
    ps = shutil.which("powershell.exe") or shutil.which("powershell") or "powershell.exe"
    cmd = [
        ps,
        "-NoProfile",
        "-NonInteractive",
        "-Command",
        "Clear-RecycleBin -Force -ErrorAction Stop",
    ]
    try:
        proc = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            timeout=60,
            check=False,
        )
    except (FileNotFoundError, OSError) as exc:
        return False, cmd, str(exc)
    if proc.returncode != 0:
        detail = (proc.stderr or proc.stdout or "clear_recycle_bin_failed").strip()
        return False, cmd, detail
    return True, cmd, ""


def empty_trash_macos() -> tuple[bool, list[str], str]:
    """Empty Trash via Finder AppleScript (requires Automation permission on macOS)."""
    osascript = shutil.which("osascript") or "/usr/bin/osascript"
    script = 'tell application "Finder" to empty trash'
    cmd = [osascript, "-e", script]
    try:
        proc = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            timeout=60,
            check=False,
        )
    except (FileNotFoundError, OSError) as exc:
        return False, cmd, str(exc)
    if proc.returncode != 0:
        detail = (proc.stderr or proc.stdout or "finder_empty_trash_failed").strip()
        return False, cmd, detail
    return True, cmd, ""


def launch_macos_entry(entry: dict[str, Any]) -> tuple[bool, list[str], int, str]:
    launch_spec = entry.get("launch_spec")
    if not isinstance(launch_spec, dict):
        return False, [], 0, "missing_launch_spec"
    open_bin = shutil.which("open") or "/usr/bin/open"
    bundle_id = str(launch_spec.get("bundle_id") or "").strip()
    display_name = str(entry.get("display_name") or "").strip()
    argv = launch_spec.get("argv")
    argv_parts = [str(part) for part in argv] if isinstance(argv, list) else []
    binary_path = str(entry.get("binary_path") or "").strip()

    if bundle_id:
        cmd = [open_bin, "-b", bundle_id]
        if argv_parts:
            cmd.extend(["--args", *argv_parts])
        try:
            pid = spawn_detached(cmd)
        except (FileNotFoundError, OSError) as exc:
            return False, cmd, 0, str(exc)
        return True, cmd, pid, ""

    if display_name:
        cmd = [open_bin, "-a", display_name]
        if argv_parts:
            cmd.extend(["--args", *argv_parts])
        try:
            pid = spawn_detached(cmd)
        except (FileNotFoundError, OSError) as exc:
            return False, cmd, 0, str(exc)
        return True, cmd, pid, ""

    if binary_path and Path(binary_path).exists():
        cmd = [open_bin, binary_path]
        try:
            pid = spawn_detached(cmd)
        except (FileNotFoundError, OSError) as exc:
            return False, cmd, 0, str(exc)
        return True, cmd, pid, ""

    return False, [], 0, "launch_target_missing"


def process_running_windows(process_name: str) -> bool:
    name = Path(str(process_name or "").strip()).name
    if not name or platform.system().lower() != "windows":
        return False
    try:
        proc = subprocess.run(
            ["tasklist", "/FI", f"IMAGENAME eq {name}", "/NH"],
            capture_output=True,
            text=True,
            timeout=8,
            check=False,
        )
    except (FileNotFoundError, OSError):
        return False
    output = str(proc.stdout or "").lower()
    return name.lower() in output and "no tasks" not in output


def process_running_macos(process_name: str) -> bool:
    name = Path(str(process_name or "").strip()).name
    if not name:
        return False
    pgrep = shutil.which("pgrep") or "/usr/bin/pgrep"
    try:
        proc = subprocess.run(
            [pgrep, "-x", name],
            capture_output=True,
            text=True,
            timeout=5,
            check=False,
        )
    except (FileNotFoundError, OSError):
        return False
    return proc.returncode == 0
