"""
BridgeFlow local dashboard — ops panel.
URL: http://localhost:18765

Endpoints:
  GET  /                  — Dashboard HTML
  GET  /api/status        — Live connection & device state
  GET  /api/preflight     — Full pre-check (env, network, Cursor, agents, version)
  GET  /api/upgrade-status — Poll pip upgrade progress
  GET  /api/qr            — QR code PNG for mobile binding
  POST /api/upgrade       — Trigger pip install --upgrade bridgeflow
  POST /api/restart       — Restart the bridgeflow process
"""
from __future__ import annotations

import json
import os
import socket
import subprocess
import sys
import threading
import time
from http.server import BaseHTTPRequestHandler, HTTPServer
from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from bridgeflow.config import PatrolConfig

DASHBOARD_PORT = int(os.environ.get("BRIDGEFLOW_DASHBOARD_PORT", "18765"))

_status: dict = {
    "relay_connected": False,
    "relay_url": "",
    "room_key": "",
    "device_id": "",
    "device_name": "",
    "machine_code": "",
    "bind_status": "unbound",
    "env": {},
    "task_count": 0,
    "reply_count": 0,
}
_lock = threading.Lock()
_upgrade_log: list[str] = []
_upgrade_running = False


def update_status(**kwargs) -> None:
    with _lock:
        _status.update(kwargs)


def get_status() -> dict:
    with _lock:
        return dict(_status)


# ── Preflight helpers ─────────────────────────────────────────────────────────

def _tcp_ping(host: str, port: int = 443, timeout: float = 3.0) -> bool:
    try:
        sock = socket.create_connection((host, port), timeout=timeout)
        sock.close()
        return True
    except Exception:
        return False


def _check_network() -> dict:
    pypi_ok = _tcp_ping("pypi.org", 443)
    relay_url = get_status().get("relay_url", "")
    relay_host = ""
    relay_ok = False
    if relay_url:
        try:
            from urllib.parse import urlparse
            parsed = urlparse(relay_url)
            relay_host = parsed.hostname or ""
            relay_port = parsed.port or (443 if parsed.scheme == "wss" else 80)
            if relay_host:
                relay_ok = _tcp_ping(relay_host, relay_port)
        except Exception:
            pass
    return {"pypi_reachable": pypi_ok, "relay_reachable": relay_ok, "relay_host": relay_host}


def _check_version() -> dict:
    from bridgeflow import __version__
    latest = None
    try:
        from bridgeflow.version_check import _fetch_latest_version, _parse_version
        latest = _fetch_latest_version()
    except Exception:
        pass
    update_available = False
    if latest:
        try:
            from bridgeflow.version_check import _parse_version
            update_available = _parse_version(latest) > _parse_version(__version__)
        except Exception:
            pass
    return {"current": __version__, "latest": latest, "update_available": update_available}


def _check_agents() -> dict:
    try:
        from bridgeflow.desktop.cursor_probe import REQUIRED_AGENTS, check_required_agents
        found, missing = check_required_agents()
        return {"required": REQUIRED_AGENTS, "found": found, "missing": missing, "all_ready": len(missing) == 0}
    except Exception as exc:
        return {"required": ["1-PM", "2-DEV", "3-QA", "4-OPS"], "found": [], "missing": ["1-PM", "2-DEV", "3-QA", "4-OPS"], "all_ready": False, "error": str(exc)}


def _run_preflight() -> dict:
    from bridgeflow.env_check import check_env
    env = check_env()
    return {
        "env": env.to_dict(),
        "network": _check_network(),
        "agents": _check_agents(),
        "version": _check_version(),
    }


# ── Upgrade worker ────────────────────────────────────────────────────────────

def _do_upgrade() -> None:
    global _upgrade_running
    _upgrade_running = True
    _upgrade_log.clear()
    _upgrade_log.append("Starting upgrade...\n")
    try:
        proc = subprocess.Popen(
            [sys.executable, "-m", "pip", "install", "--upgrade",
             "--no-cache-dir", "--index-url", "https://pypi.org/simple/", "bridgeflow"],
            stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
            text=True, encoding="utf-8", errors="replace",
        )
        for line in proc.stdout:
            _upgrade_log.append(line)
        proc.wait()
        if proc.returncode == 0:
            _upgrade_log.append("\nUpgrade complete. Click [Restart] to apply.\n")
        else:
            _upgrade_log.append(f"\nUpgrade failed (exit code {proc.returncode}).\n")
    except Exception as exc:
        _upgrade_log.append(f"\nError: {exc}\n")
    finally:
        _upgrade_running = False


# ── HTTP Handler ──────────────────────────────────────────────────────────────

class _Handler(BaseHTTPRequestHandler):
    def log_message(self, *args):
        pass

    def _send_json(self, data: dict, code: int = 200) -> None:
        body = json.dumps(data, ensure_ascii=False).encode("utf-8")
        self.send_response(code)
        self.send_header("Content-Type", "application/json; charset=utf-8")
        self.send_header("Content-Length", str(len(body)))
        self.send_header("Access-Control-Allow-Origin", "*")
        self.end_headers()
        self.wfile.write(body)

    def _send_html(self, html: str) -> None:
        body = html.encode("utf-8")
        self.send_response(200)
        self.send_header("Content-Type", "text/html; charset=utf-8")
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)

    def _send_png(self, data: bytes) -> None:
        self.send_response(200)
        self.send_header("Content-Type", "image/png")
        self.send_header("Content-Length", str(len(data)))
        self.send_header("Access-Control-Allow-Origin", "*")
        self.end_headers()
        self.wfile.write(data)

    def _read_body(self) -> bytes:
        length = int(self.headers.get("Content-Length", 0))
        return self.rfile.read(length) if length else b""

    # ── GET ────────────────────────────────────────────────────────────────
    def do_GET(self):
        if self.path == "/api/status":
            self._send_json(get_status())

        elif self.path.startswith("/api/preflight"):
            self._send_json(_run_preflight())

        elif self.path.startswith("/api/upgrade-status"):
            self._send_json({"running": _upgrade_running, "log": "".join(_upgrade_log)})

        elif self.path.startswith("/api/qr"):
            try:
                import io
                import segno
                status = get_status()
                mc = status.get("machine_code") or "BF-UNKNOWN"
                device_id = status.get("device_id", "")
                qr_content = f"bf:{mc}|{device_id}"
                qr = segno.make_qr(qr_content, error="M")
                buf = io.BytesIO()
                qr.save(buf, kind="png", scale=8, border=2)
                self._send_png(buf.getvalue())
            except Exception:
                self.send_response(500)
                self.end_headers()

        elif self.path in ("/", "/index.html"):
            html_path = Path(__file__).parent / "index.html"
            self._send_html(html_path.read_text(encoding="utf-8"))

        else:
            self.send_response(404)
            self.end_headers()

    # ── POST ───────────────────────────────────────────────────────────────
    def do_POST(self):
        self._read_body()

        if self.path == "/api/upgrade":
            if _upgrade_running:
                self._send_json({"ok": False, "message": "Upgrade already running"})
                return
            threading.Thread(target=_do_upgrade, daemon=True).start()
            self._send_json({"ok": True, "message": "Upgrade started"})

        elif self.path == "/api/restart":
            self._send_json({"ok": True, "message": "Restarting..."})
            def _restart():
                time.sleep(1)
                try:
                    os.execv(sys.argv[0], sys.argv)
                except Exception:
                    subprocess.Popen([sys.argv[0]] + sys.argv[1:])
                    sys.exit(0)
            threading.Thread(target=_restart, daemon=True).start()

        else:
            self.send_response(404)
            self.end_headers()


# ── Startup ───────────────────────────────────────────────────────────────────

def start_dashboard(config: "PatrolConfig") -> None:
    """Start the dashboard HTTP server on a background thread."""
    from bridgeflow.env_check import check_env

    env = check_env()
    update_status(
        relay_url=config.relay_url,
        room_key=config.room_key,
        device_id=config.device_id,
        device_name=config.device_name,
        machine_code=config.machine_code,
        bind_status=config.bind_status,
        env=env.to_dict(),
    )

    server = HTTPServer(("127.0.0.1", DASHBOARD_PORT), _Handler)
    t = threading.Thread(target=server.serve_forever, daemon=True)
    t.start()
