"""Location quad: a static OpenStreetMap image centred on the device's IP-geolocated
position, with a marker.

The position comes from the shared LocationService (backend/geolocate.py), which
traceroutes to 1.1.1.1, takes the first public hop (the ISP egress) and resolves it
through a public geolocation API. The "Locate via IP" feature toggle gates that
lookup entirely; when off, this quad shows a placeholder instead.

We render a static map (backend/staticmap.py) rather than an interactive libshumate
map: Shumate does not paint tiles in this environment (the official shumate-demo
shows the same blank map), while tile downloads themselves work fine. The Weather
quad consumes the same LocationService.
"""

from __future__ import annotations

import sys

import gi

gi.require_version("Gtk", "4.0")
from gi.repository import Gtk  # noqa: E402

from lib.proc import run_text
from module_base import Feature, ModuleContext, ModuleInstance, ModuleSpec
from paths import BACKEND_DIR, CACHE_DIR

_SCRIPT = str(BACKEND_DIR / "staticmap.py")


class _MapView(Gtk.Box):
    def __init__(self, ctx: ModuleContext, zoom: int, size: tuple[int, int],
                 tag: str, show_info: bool):
        super().__init__(orientation=Gtk.Orientation.VERTICAL)
        self.add_css_class("map-view")
        self.ctx = ctx
        self.zoom = zoom
        self.size = size
        self._out = str(CACHE_DIR / f"map_{tag}.png")

        self.picture = Gtk.Picture(content_fit=Gtk.ContentFit.COVER, vexpand=True)
        self.picture.add_css_class("map-picture")
        self.append(self.picture)

        self._info = None
        if show_info:
            self._info = Gtk.Label(label="Locating…", xalign=0.0)
            self._info.add_css_class("map-info")
            self.append(self._info)

        ctx.services.location.subscribe(self._on_location)

    def _on_location(self, data: dict) -> None:
        lat, lon = data["lat"], data["lon"]
        w, h = self.size
        run_text([sys.executable, _SCRIPT, str(lat), str(lon), str(self.zoom),
                  str(w), str(h), self._out], self._on_rendered)
        if self._info is not None:
            city = data.get("city") or "Unknown"
            country = data.get("country") or ""
            self._info.set_text(f"{city}, {country}  ·  {lat:.3f}, {lon:.3f}")

    def _on_rendered(self, ok: bool, out: str, err: str) -> None:
        if ok:
            self.picture.set_filename(self._out)


def _placeholder() -> Gtk.Widget:
    box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8,
                  valign=Gtk.Align.CENTER, halign=Gtk.Align.CENTER)
    box.add_css_class("map-view")
    box.append(Gtk.Label(label="IP location is off"))
    return box


def build(ctx: ModuleContext) -> ModuleInstance:
    # "Locate via IP" gates geolocation entirely: when off we neither traceroute nor
    # hit any geolocation API, and just show a placeholder. Toggling it rebuilds the
    # card (via QuadCard), so flipping it back on re-triggers the lookup.
    if not ctx.feature("ip_locate", True):
        return ModuleInstance(compact=_placeholder(), expanded=None)
    ctx.services.location.get()  # kick off geolocation (traceroute → API) if not started
    compact = _MapView(ctx, zoom=12, size=(620, 150), tag="compact", show_info=False)
    expanded = _MapView(ctx, zoom=13, size=(1100, 620), tag="expanded", show_info=True)
    return ModuleInstance(compact=compact, expanded=expanded, scroll_expanded=False)


SPEC = ModuleSpec(
    id="location",
    title="Location",
    icon="",  # nf-fa-map_marker
    build=build,
    features=[Feature("ip_locate", "Locate via IP", True)],
)
