"""
Windows system-tray icon for Lungo Bridge — the analog of macOS ``menubar.py``.

Built on ``pystray``. The asyncio bridge runs in a daemon thread and pushes status
updates through a thread-safe queue; a drainer thread here rebuilds the tray menu
when state changes (pystray has no rumps-style timer, so we poll the queue and call
``icon.update_menu()``).

Menu parity with macOS (minus macOS-only items — no TCC checks in this scaffold):

  Lungo Bridge vX.Y
  ● N active sessions
  ──────────
  Relay: connected
  Agents            ▸
  Paired Devices    ▸
  ──────────
  Show QR Code
  View Logs
  ──────────
  Start at Login          ✓
  Auto-restart if it crashes
  Terminal Access         ✓
  Voice Transcription     ✓
  AI Summaries & Titles   ✓
  Restart Bridge
  Quit Lungo
"""
from __future__ import annotations

import logging
import os
import queue
import subprocess
import tempfile
import threading
from dataclasses import dataclass
from datetime import datetime, timezone

import pystray
from pystray import Menu, MenuItem as Item
from PIL import Image, ImageDraw

from terminull_bridge import config as cfg
from terminull_bridge.session_store import SessionStore
from . import autostart

log = logging.getLogger(__name__)


# ── Status update messages (pushed from the bridge thread) ───────────────────

@dataclass
class RelayStatus:
    connected: bool

@dataclass
class AdapterStatus:
    agent_id: str
    name: str
    state: str  # "connected" | "ready" | "error" | "not_found"
    detail: str = ""

@dataclass
class QRData:
    pair_url: str
    auto_show: bool = False

@dataclass
class UpdateAvailable:
    version: str
    url: str
    mac_url: str = ""

@dataclass
class SessionCount:
    active: int

@dataclass
class AuthExpired:
    pass

StatusMessage = RelayStatus | AdapterStatus | QRData | UpdateAvailable | SessionCount | AuthExpired


# ── Tray icon image (coloured dot by connection state) ───────────────────────

_COLORS = {
    "ok":   (52, 199, 89),    # green  — relay connected
    "warn": (255, 159, 10),   # amber  — adapters only / auth expired / update
    "off":  (142, 142, 147),  # grey   — offline
}


def _icon_image(state: str) -> Image.Image:
    img = Image.new("RGBA", (64, 64), (0, 0, 0, 0))
    d = ImageDraw.Draw(img)
    d.ellipse((8, 8, 56, 56), fill=_COLORS.get(state, _COLORS["off"]))
    return img


def _humanize_ts(iso: str | None) -> str:
    if not iso:
        return "never"
    try:
        dt = datetime.fromisoformat(iso)
        if dt.tzinfo is None:
            dt = dt.replace(tzinfo=timezone.utc)
        secs = int((datetime.now(timezone.utc) - dt).total_seconds())
    except Exception:
        return iso
    if secs < 60:
        return "just now"
    if secs < 3600:
        return f"{secs // 60} min ago"
    if secs < 86400:
        return f"{secs // 3600} h ago"
    days = secs // 86400
    return f"{days} d ago" if days < 30 else dt.strftime("%b %d, %Y")


# ── Tray app ──────────────────────────────────────────────────────────────────

class BeaconTray:
    def __init__(self, status_queue: "queue.Queue[StatusMessage]", api_url: str = "") -> None:
        self._q = status_queue
        self._api_url = api_url
        self._relay_connected = False
        self._adapters: dict[str, AdapterStatus] = {}
        self._pair_url: str | None = None
        self._first_run_shown = False
        self._update_version: str | None = None
        self._update_url: str | None = None
        self._update_mac_url: str | None = None
        self._active_sessions = 0
        self._auth_expired = False
        self._icon_state = "off"
        self._store = SessionStore()

        self._icon = pystray.Icon(
            "lungo", _icon_image("off"), "Lungo Bridge", menu=self._build_menu()
        )

    # ── State → icon ──────────────────────────────────────────────────────────

    def _state(self) -> str:
        if self._auth_expired or self._update_version:
            return "warn"
        if self._relay_connected:
            return "ok"
        if self._adapters:
            return "warn"
        return "off"

    # ── Menu construction ───────────────────────────────────────────────────────

    def _build_menu(self) -> Menu:
        from terminull_bridge.updater import current_version

        sess = (
            f"● {self._active_sessions} active session"
            f"{'s' if self._active_sessions != 1 else ''}"
            if self._active_sessions > 0 else "No active sessions"
        )
        relay = "Relay: connected" if self._relay_connected else "Relay: disconnected"

        items = [
            Item(f"Lungo Bridge v{current_version()}", None, enabled=False),
            Item(sess, None, enabled=False),
        ]
        if self._update_version:
            items.append(Item(f"↓  Update to v{self._update_version}", self._do_update))
        if self._auth_expired:
            items.append(Item("⚠ Re-authenticate…", self._reauth))
        items += [
            Menu.SEPARATOR,
            Item(relay, None, enabled=False),
            Item("Agents", self._agents_submenu()),
            Item("Paired Devices", self._devices_submenu()),
            Menu.SEPARATOR,
            Item("Show QR Code", self._show_qr),
            Item("View Logs", self._open_logs),
            Menu.SEPARATOR,
            Item("Start at Login", self._toggle_login,
                 checked=lambda i: autostart.run_at_login_enabled()),
            Item("Auto-restart if it crashes", self._toggle_keepalive,
                 checked=lambda i: autostart.keep_alive_enabled()),
            Item("Terminal Access", self._toggle_terminal,
                 checked=lambda i: cfg.get_feature("terminal_enabled", default=True)),
            Item("Voice Transcription (Whisper)", self._toggle_stt,
                 checked=lambda i: cfg.get_feature("stt_enabled")),
            Item("AI Summaries & Titles", self._toggle_summarizer,
                 checked=lambda i: cfg.get_feature("summarizer_enabled", default=True)),
            Item("Restart Bridge", self._restart),
            Item("Quit Lungo", self._quit),
        ]
        return Menu(*items)

    def _agents_submenu(self) -> Menu:
        if not self._adapters:
            return Menu(Item("No agents detected", None, enabled=False))
        out = []
        for a in self._adapters.values():
            dot = "● " if a.state in ("connected", "ready") else "○ "
            suffix = f" — {a.detail}" if a.detail else f" — {a.state}"
            out.append(Item(dot + a.name + suffix, None, enabled=False))
        return Menu(*out)

    def _devices_submenu(self) -> Menu:
        try:
            pairings = self._store.list_active_pairings()
        except Exception as e:
            return Menu(Item(f"(unavailable: {e})", None, enabled=False))
        if not pairings:
            return Menu(Item("No paired devices", None, enabled=False))
        out = []
        for p in pairings:
            label = p["device_name"] or (p["device_id"] or "")[:12] or "Unknown device"
            sub = [Item(f"Last used: {_humanize_ts(p['last_used'])}", None, enabled=False)]
            if p["user_email"]:
                sub.append(Item(p["user_email"], None, enabled=False))
            sub += [
                Menu.SEPARATOR,
                Item("Drop This Device", self._make_drop_cb(p["device_id"], label)),
            ]
            out.append(Item("● " + label, Menu(*sub)))
        return Menu(*out)

    def _make_drop_cb(self, device_id: str, label: str):
        def _cb(icon, item) -> None:
            self._store.revoke_pairing(device_id)
            self._refresh()
            icon.notify(f"“{label}” dropped — new connections refused.", "Lungo")
        return _cb

    # ── Queue drainer ───────────────────────────────────────────────────────────

    def _drain_loop(self) -> None:
        while True:
            changed = False
            msg = self._q.get()  # blocks
            batch = [msg]
            while True:
                try:
                    batch.append(self._q.get_nowait())
                except queue.Empty:
                    break
            for m in batch:
                if isinstance(m, RelayStatus):
                    self._relay_connected = m.connected; changed = True
                elif isinstance(m, AdapterStatus):
                    self._adapters[m.agent_id] = m; changed = True
                elif isinstance(m, QRData):
                    self._pair_url = m.pair_url
                    if m.auto_show and not self._first_run_shown:
                        self._first_run_shown = True
                        self._show_first_run_pairing()
                elif isinstance(m, UpdateAvailable):
                    self._update_version, self._update_url, self._update_mac_url = (
                        m.version, m.url, m.mac_url)
                    changed = True
                elif isinstance(m, SessionCount):
                    self._active_sessions = m.active; changed = True
                elif isinstance(m, AuthExpired):
                    self._auth_expired = True; self._relay_connected = False; changed = True
            if changed:
                self._refresh()

    def _refresh(self) -> None:
        state = self._state()
        if state != self._icon_state:
            self._icon_state = state
            self._icon.icon = _icon_image(state)
        self._icon.menu = self._build_menu()
        self._icon.update_menu()

    # ── Actions ───────────────────────────────────────────────────────────────

    def _show_first_run_pairing(self) -> None:
        self._icon.notify(
            "Open the Lungo app on your iPhone and scan the QR code "
            "(tray → Show QR Code) to pair.", "Welcome to Lungo")
        self._show_qr(self._icon, None)

    def _show_qr(self, icon, item) -> None:
        if not self._pair_url:
            icon.notify("Bridge hasn't connected to the relay yet.", "No QR yet")
            return
        try:
            import qrcode
            img = qrcode.make(self._pair_url)
            tmp = tempfile.NamedTemporaryFile(suffix=".png", delete=False, prefix="lungo_qr_")
            tmp.close()
            img.save(tmp.name)
            os.startfile(tmp.name)  # default image viewer  # noqa: S606
        except Exception as e:
            log.warning("QR render failed: %s", e)
        # Clipboard fallback (clip.exe reads stdin)
        with subprocess.Popen(["clip"], stdin=subprocess.PIPE) as p:
            p.communicate(self._pair_url.encode("utf-16"))

    def _open_logs(self, icon, item) -> None:
        os.startfile(str(autostart.err_path()))  # noqa: S606

    def _toggle_login(self, icon, item) -> None:
        if autostart.is_installed():
            autostart.uninstall()
        else:
            autostart.install()
        self._refresh()

    def _toggle_keepalive(self, icon, item) -> None:
        autostart.set_keep_alive(not autostart.keep_alive_enabled())
        self._refresh()

    def _toggle_terminal(self, icon, item) -> None:
        cfg.set_feature("terminal_enabled", not cfg.get_feature("terminal_enabled", default=True))
        self._refresh()

    def _toggle_stt(self, icon, item) -> None:
        cfg.set_feature("stt_enabled", not cfg.get_feature("stt_enabled"))
        self._refresh()

    def _toggle_summarizer(self, icon, item) -> None:
        cfg.set_feature("summarizer_enabled", not cfg.get_feature("summarizer_enabled", default=True))
        self._refresh()

    def _do_update(self, icon, item) -> None:
        url, version = self._update_url, self._update_version
        self._update_version = self._update_url = self._update_mac_url = None
        self._refresh()

        def _run() -> None:
            from terminull_bridge.updater import install_update
            try:
                install_update(url or "")           # venv build → pip upgrade
                autostart.restart()
            except Exception as e:
                icon.notify(f"Update failed: {e}", "Lungo")
        threading.Thread(target=_run, daemon=True).start()

    def _reauth(self, icon, item) -> None:
        icon.notify("Auth expired — open the Lungo iOS app and scan the QR again.", "Lungo")

    def _restart(self, icon, item) -> None:
        autostart.restart()

    def _quit(self, icon, item) -> None:
        icon.stop()
        os._exit(0)

    # ── Run ─────────────────────────────────────────────────────────────────────

    def run(self) -> None:
        threading.Thread(target=self._drain_loop, daemon=True, name="tray-drainer").start()
        self._icon.run()  # blocks on the main thread until Quit


def make_status_queue() -> "queue.Queue[StatusMessage]":
    return queue.Queue()


def run_tray(status_queue: "queue.Queue[StatusMessage]", api_url: str = "") -> None:
    BeaconTray(status_queue, api_url=api_url).run()
