"""Ensure loopback ui_bridge is running; return console URL."""

from __future__ import annotations

import json
import os
import subprocess
import time
import urllib.error
import urllib.request
from pathlib import Path

from .auth import resolve_or_create_token
from .server import DEFAULT_HOST, DEFAULT_PORT


def sys_executable() -> str:
    import sys

    return sys.executable or "python3"


def _healthz_ok(base_url: str, timeout: float = 0.8) -> bool:
    try:
        with urllib.request.urlopen(f"{base_url.rstrip('/')}/healthz", timeout=timeout) as resp:
            if resp.status != 200:
                return False
            payload = json.loads(resp.read().decode("utf-8"))
            return bool(payload.get("ok"))
    except (urllib.error.URLError, TimeoutError, OSError, json.JSONDecodeError, ValueError):
        return False


def default_config_path() -> Path:
    return Path.home() / ".config" / "iuve-connect-agent" / "desktop-config.json"


def ensure_bridge(
    *,
    host: str = DEFAULT_HOST,
    port: int = DEFAULT_PORT,
    config_path: Path | None = None,
    ui_root: Path | None = None,
    token: str | None = None,
    spawn_if_needed: bool = True,
) -> dict[str, object]:
    """Return console URL; start background bridge process if healthz is down."""
    cfg = config_path if config_path is not None else default_config_path()
    tok = (token or "").strip() or resolve_or_create_token()
    bind_host = "127.0.0.1" if host in {"localhost", "127.0.0.1", "::1"} else host
    if bind_host not in {"127.0.0.1", "::1"}:
        raise ValueError("bridge_must_bind_loopback_only")
    base_url = f"http://{bind_host}:{int(port)}"
    console_url = f"{base_url}/?token={tok}"

    if _healthz_ok(base_url):
        return {
            "ok": True,
            "started": False,
            "already_running": True,
            "base_url": base_url,
            "console_url": console_url,
            "ui_surface": "native_console",
            "phase": "C.3",
        }

    if not spawn_if_needed:
        return {
            "ok": False,
            "error": "bridge_not_running",
            "base_url": base_url,
            "console_url": console_url,
        }

    log_dir = Path.home() / ".config" / "iuve-connect-agent"
    log_dir.mkdir(parents=True, exist_ok=True)
    log_path = log_dir / "ui-bridge.log"
    env = os.environ.copy()
    env["IUVE_UI_BRIDGE_TOKEN"] = tok
    env["IUVE_UI_BRIDGE_HOST"] = bind_host
    env["IUVE_UI_BRIDGE_PORT"] = str(int(port))
    # Preserve agent tree on PYTHONPATH for spawned bridge.
    agent_bits: list[str] = []
    try:
        import iuve_connect_agents

        root = Path(iuve_connect_agents.__file__).resolve().parents[1]
        agent_bits.append(str(root))
        vendor = root / "vendor"
        if vendor.is_dir():
            agent_bits.append(str(vendor))
    except Exception:
        pass
    existing_pp = str(env.get("PYTHONPATH") or "")
    merged = os.pathsep.join([*agent_bits, existing_pp] if existing_pp else agent_bits)
    if merged:
        env["PYTHONPATH"] = merged
    python_exe = (
        env.get("IUVE_AGENT_PYTHON")
        or env.get("PYTHON")
        or sys_executable()
    )
    cmd = [
        python_exe,
        "-m",
        "iuve_connect_agents.desktop.ui_bridge",
        "--host",
        bind_host,
        "--port",
        str(int(port)),
        "--config",
        str(cfg),
    ]
    if ui_root is not None:
        cmd.extend(["--ui-root", str(ui_root)])

    with log_path.open("a", encoding="utf-8") as log_fh:
        log_fh.write(f"\n# ensure_bridge spawn {time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())}\n")
        subprocess.Popen(  # noqa: S603
            cmd,
            stdout=log_fh,
            stderr=subprocess.STDOUT,
            env=env,
            start_new_session=True,
        )

    for _ in range(40):
        time.sleep(0.1)
        if _healthz_ok(base_url):
            return {
                "ok": True,
                "started": True,
                "already_running": False,
                "base_url": base_url,
                "console_url": console_url,
                "ui_surface": "native_console",
                "phase": "C.3",
                "log_path": str(log_path),
            }

    return {
        "ok": False,
        "error": "bridge_start_timeout",
        "base_url": base_url,
        "console_url": console_url,
        "log_path": str(log_path),
        "hint": "Check ~/.config/iuve-connect-agent/ui-bridge.log",
    }


def open_console(*, url: str, prefer_webview: bool = True) -> dict[str, object]:
    """Open Console URL in pywebview window when available, else default browser."""
    if prefer_webview:
        try:
            import sys

            import webview  # type: ignore

            webview.create_window(
                "IuVe Connect Agent Console",
                url,
                width=1280,
                height=820,
                min_size=(900, 600),
            )
            # Windows: Edge WebView2 (requires Evergreen Runtime — preinstalled on Win11).
            if sys.platform == "win32":
                webview.start(gui="edgechromium")
            else:
                webview.start()
            return {"ok": True, "opener": "pywebview"}
        except Exception:
            pass
    import webbrowser

    opened = bool(webbrowser.open(url))
    return {"ok": opened, "opener": "webbrowser"}


def launch_console(
    *,
    config_path: Path | None = None,
    open_ui: bool = True,
    prefer_webview: bool = True,
) -> dict[str, object]:
    port = int(os.environ.get("IUVE_UI_BRIDGE_PORT") or DEFAULT_PORT)
    host = os.environ.get("IUVE_UI_BRIDGE_HOST") or DEFAULT_HOST
    result = ensure_bridge(host=host, port=port, config_path=config_path)
    if not result.get("ok"):
        return result
    if open_ui:
        opener = open_console(url=str(result["console_url"]), prefer_webview=prefer_webview)
        result["opener"] = opener
    return result
