"""
Entry point for Lungo Bridge on Windows (system-tray mode).

Startup sequence (mirrors the macOS wrapper):
  1. Configure file logging (a tray app has no console).
  2. Auto-install the Run-key autostart on first run.
  3. Start the asyncio bridge (HeadlessApp subclass) in a daemon thread, under a
     supervisor that respawns it if it dies and "Auto-restart" is enabled.
  4. Run the pystray tray on the main thread.

This wrapper deliberately omits the macOS-only pieces (TCC checks).
"""
from __future__ import annotations

import asyncio
import logging
import queue
import socket
import threading
import time

from dotenv import load_dotenv
load_dotenv()

from terminull_bridge.headless import HeadlessApp, RELAY_URL, API_URL
from terminull_bridge.detector import detect_all
from terminull_bridge import config as cfg
from . import autostart
from .tray import (
    make_status_queue, run_tray,
    RelayStatus, AdapterStatus, QRData, UpdateAvailable, SessionCount, AuthExpired,
)

log = logging.getLogger(__name__)


class BridgeApp(HeadlessApp):
    """HeadlessApp wired to push status into the tray queue. No screen share."""

    def __init__(self, status_queue: "queue.Queue") -> None:
        super().__init__()
        self._sq = status_queue

    # ── HeadlessApp extension points ──────────────────────────────────────────

    def _on_relay_status(self, connected: bool) -> None:
        self._sq.put(RelayStatus(connected=connected))
        log.info("Relay %s", "connected" if connected else "disconnected")

    def _extra_relay_kwargs(self) -> dict:
        return {"machine_name": socket.gethostname()}

    def _extra_tasks(self) -> list:
        return super()._extra_tasks() + [self._agent_refresh_loop(), self._update_check_loop()]

    async def _after_agents_detected(self) -> None:
        for agent in self._agents:
            self._sq.put(AdapterStatus(agent_id=agent.id, name=agent.name, state="ready"))

    async def _before_relay(self) -> None:
        # Vision via cloud OCR can still be advertised; on-device Apple Vision is
        # macOS-only, so default vision off here unless the operator enables it.
        if "vision_enabled" not in cfg.load().get("features", {}):
            cfg.set_feature("vision_enabled", False)

    def _build_pair_url(self) -> str:
        return (
            f"terminull://pair"
            f"?id={self._auth.beacon_id}"
            f"&pk={self._auth.pubkey_b64}"
            f"&epk={self._auth.epk_b64}"
            f"&relay={RELAY_URL}"
            f"&claim={self._auth.get_claim_code()}"
        )

    async def _poll_for_auth_token(self) -> None:
        self._sq.put(QRData(pair_url=self._build_pair_url(), auto_show=True))
        await super()._poll_for_auth_token()

    async def _after_paired(self) -> None:
        self._sq.put(QRData(pair_url=self._build_pair_url()))

    def _on_auth_error(self) -> None:
        super()._on_auth_error()
        self._sq.put(AuthExpired())

    async def _on_update_available(self, version: str, url: str, mac_url: str = "") -> None:
        self._sq.put(UpdateAvailable(version=version, url=url, mac_url=mac_url))

    async def _on_session_request(
        self, session_id: str, agent_id: str, cwd: str | None = None,
        device_id: str | None = None, resume_claude_session_id: str | None = None,
        is_resume: bool = False, ios_epk: str | None = None,
        model: str | None = None,
        resume_external_session_id: str | None = None,
    ) -> None:
        await super()._on_session_request(
            session_id, agent_id, cwd=cwd, device_id=device_id,
            resume_claude_session_id=resume_claude_session_id, is_resume=is_resume,
            ios_epk=ios_epk, model=model,
            resume_external_session_id=resume_external_session_id,
        )
        self._sq.put(SessionCount(active=len(self._sessions)))

    # ── Background loops ──────────────────────────────────────────────────────

    _AGENT_REFRESH_INTERVAL = 300

    async def _agent_refresh_loop(self) -> None:
        while True:
            await asyncio.sleep(self._AGENT_REFRESH_INTERVAL)
            try:
                fresh = detect_all()
                if {a.id for a in fresh} == {a.id for a in self._agents}:
                    continue
                self._agents = fresh
                for agent in fresh:
                    self._sq.put(AdapterStatus(agent_id=agent.id, name=agent.name, state="ready"))
                if self._relay:
                    await self._relay.update_agents(
                        [{"id": a.id, "name": a.name, "tools": len(a.mcp_tools)} for a in fresh]
                    )
            except Exception as e:
                log.warning("Agent refresh failed: %s", e)

    _UPDATE_CHECK_INTERVAL = 86400

    async def _update_check_loop(self) -> None:
        from terminull_bridge.updater import check_for_update
        await asyncio.sleep(30)
        while True:
            try:
                result = await check_for_update(API_URL)
                if result:
                    version, url, mac_url = result
                    self._sq.put(UpdateAvailable(version=version, url=url, mac_url=mac_url))
            except Exception as e:
                log.debug("Update check error: %s", e)
            await asyncio.sleep(self._UPDATE_CHECK_INTERVAL)


# ── Supervisor + runner ─────────────────────────────────────────────────────

def _run_bridge_supervised(status_queue: "queue.Queue") -> None:
    """Run the asyncio bridge; respawn it on crash while Auto-restart is enabled."""
    while True:
        try:
            asyncio.run(BridgeApp(status_queue).run())
            return  # clean exit — don't respawn
        except Exception:
            log.exception("Bridge crashed")
            if not autostart.keep_alive_enabled():
                return
            time.sleep(5)  # throttle, like launchd ThrottleInterval


def main() -> None:
    logging.basicConfig(
        level=logging.INFO,
        format="%(asctime)s %(levelname)s %(name)s: %(message)s",
        handlers=[
            logging.FileHandler(autostart.err_path(), encoding="utf-8"),
            logging.StreamHandler(),
        ],
    )
    logging.getLogger("terminull_bridge.headless").setLevel(logging.DEBUG)

    if not autostart.is_installed():
        autostart.install()

    status_queue = make_status_queue()
    threading.Thread(
        target=_run_bridge_supervised, args=(status_queue,),
        daemon=True, name="terminull-bridge",
    ).start()

    run_tray(status_queue, api_url=API_URL)


if __name__ == "__main__":
    main()
