"""Cross-platform desktop shell helpers (open_url / open_folder).

Thin facade over AppAdapter + runtime_launch so command_executor and UI
share one entry point instead of Linux-only xdg-open fallbacks.
"""

from __future__ import annotations

import shutil
import subprocess
from dataclasses import dataclass
from typing import Any
from urllib.parse import urlparse

from .detect import os_family
from .runtime_launch import (
    launch_uri_macos,
    launch_uri_windows,
    open_folder_windows,
    spawn_detached,
)


@dataclass(frozen=True)
class ShellLaunchResult:
    success: bool
    command: list[str]
    pid: int = 0
    error: str = ""
    backend: str = ""

    def as_dict(self) -> dict[str, Any]:
        return {
            "success": self.success,
            "command": list(self.command),
            "pid": self.pid,
            "error": self.error,
            "backend": self.backend,
        }


def open_url(url: str) -> ShellLaunchResult:
    """Open http(s) URL with the OS default browser / association."""
    uri = str(url or "").strip()
    if not uri:
        return ShellLaunchResult(False, [], error="missing_url", backend="none")
    parsed = urlparse(uri)
    if parsed.scheme not in {"http", "https"}:
        return ShellLaunchResult(False, [uri], error="invalid_url_scheme", backend="none")

    family = os_family()
    if family == "windows":
        ok, cmd, pid, err = launch_uri_windows(uri)
        return ShellLaunchResult(ok, list(cmd), pid=pid, error=err, backend="windows_startfile")
    if family == "macos":
        ok, cmd, pid, err = launch_uri_macos(uri)
        return ShellLaunchResult(ok, list(cmd), pid=pid, error=err, backend="macos_open")

    # Prefer AppAdapter when available (xdg-open / gtk-launch path).
    try:
        from .adapter_registry import get_app_adapter

        adapter = get_app_adapter("linux")
        if adapter is not None and hasattr(adapter, "launch_uri"):
            launch = adapter.launch_uri(uri)
            return ShellLaunchResult(
                bool(launch.success),
                list(launch.launch_command or []),
                pid=int(getattr(launch, "pid", 0) or 0),
                error=str(getattr(launch, "error", "") or ""),
                backend="linux_app_adapter",
            )
    except Exception as exc:  # noqa: BLE001 — fall through to xdg-open
        last_err = str(exc)
    else:
        last_err = ""

    xdg = shutil.which("xdg-open") or "xdg-open"
    cmd = [xdg, uri]
    try:
        pid = spawn_detached(cmd)
        return ShellLaunchResult(True, cmd, pid=pid, backend="xdg-open")
    except (FileNotFoundError, OSError) as exc:
        return ShellLaunchResult(
            False,
            cmd,
            error=str(exc) or last_err or "xdg_open_failed",
            backend="xdg-open",
        )


def open_folder(path: str) -> ShellLaunchResult:
    """Reveal/open a folder in the native file manager."""
    folder = str(path or "").strip()
    if not folder:
        return ShellLaunchResult(False, [], error="missing_path", backend="none")

    family = os_family()
    if family == "windows":
        ok, cmd, pid, err = open_folder_windows(folder)
        return ShellLaunchResult(ok, list(cmd), pid=pid, error=err, backend="windows_explorer")
    if family == "macos":
        open_bin = shutil.which("open") or "/usr/bin/open"
        cmd = [open_bin, folder]
        try:
            pid = spawn_detached(cmd)
            return ShellLaunchResult(True, cmd, pid=pid, backend="macos_open")
        except (FileNotFoundError, OSError) as exc:
            return ShellLaunchResult(False, cmd, error=str(exc), backend="macos_open")

    opener = shutil.which("xdg-open") or "xdg-open"
    cmd = [opener, folder]
    try:
        pid = spawn_detached(cmd)
        return ShellLaunchResult(True, cmd, pid=pid, backend="xdg-open")
    except (FileNotFoundError, OSError) as exc:
        return ShellLaunchResult(False, cmd, error=str(exc), backend="xdg-open")


def open_url_sync_check(url: str, *, timeout_sec: float = 15.0) -> ShellLaunchResult:
    """Linux test helper: run xdg-open synchronously (not for production Avatar path)."""
    del timeout_sec
    return open_url(url)
