#!/usr/bin/env python3
import gi
import os
import sys
import json
import requests
from urllib.parse import quote
import shutil

gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")
from gi.repository import Gtk, Gio, Gdk, GLib, Adw

# ---------------------------------------------------------------------
# Pfad zu den Settings / CSS
# Wenn installiert: Ressourcen liegen unter /usr/share/localfuel
# Wenn aus Quellverzeichnis gestartet: neben main.py
if os.path.exists(os.path.join(os.path.dirname(__file__), "data")):
    # Entwicklungsmodus (main.py im Projektordner)
    BASE_DIR = os.path.dirname(__file__)
else:
    # Installiert als /usr/bin/localfuel -> Daten in /usr/share/localfuel
    BASE_DIR = "/usr/share/localfuel"

# CSS und andere read-only Ressourcen
CSS_PATH = os.path.join(BASE_DIR, "data", "style.css")
LICENSE_PATH = os.path.join(BASE_DIR, "data", "LICENSE")

# Settings immer im User-Verzeichnis (XDG), unabhängig von Installation
XDG_CONFIG = os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config"))
USER_CONFIG_DIR = os.path.join(XDG_CONFIG, "localfuel")
SETTINGS_PATH = os.path.join(USER_CONFIG_DIR, "settings.json")

# --- NEU: API-Konfiguration für Proxy ---
# Standard-Proxy-URL, kann per Umgebungsvariable überschrieben werden
API_BASE = os.environ.get("LOCALFUEL_API_BASE", "https://localfuelapi.localprogger.de").rstrip("/")
# Fallback auf lokalen API-Key, falls Proxy nicht genutzt wird
ENV_API_KEY = os.environ.get("LOCALFUEL_API_KEY", "")
# --- Ende Neu ---

def load_settings():
    """Lädt Settings aus JSON-Datei."""
    # Zuerst User-Settings versuchen
    if os.path.exists(SETTINGS_PATH):
        try:
            with open(SETTINGS_PATH, "r", encoding="utf-8") as f:
                return json.load(f)
        except Exception:
            pass
    
    # Fallback: Default-Settings aus Installation oder Projekt-data/ kopieren
    default_settings_path = os.path.join(BASE_DIR, "data", "settings.json")
    if os.path.exists(default_settings_path):
        try:
            with open(default_settings_path, "r", encoding="utf-8") as f:
                settings = json.load(f)
            # Einmalig ins User-Verzeichnis kopieren
            save_settings(settings)
            return settings
        except Exception:
            pass
    
    return {}

def save_settings(settings):
    """Speichert Settings in JSON-Datei."""
    try:
        os.makedirs(os.path.dirname(SETTINGS_PATH), exist_ok=True)
        with open(SETTINGS_PATH, "w", encoding="utf-8") as f:
            json.dump(settings, f, indent=2, ensure_ascii=False)
    except Exception as e:
        print("Konnte Einstellungen nicht speichern:", e)

def get_coords_for_location(location):
    """Wandelt Ort/PLZ in Koordinaten um via Nominatim."""
    headers = {"User-Agent": "LocalFuelApp/1.0"}
    url = f"https://nominatim.openstreetmap.org/search?q={quote(location)}&format=json&limit=1"
    try:
        r = requests.get(url, timeout=10, headers=headers)
        data = r.json()
        if data:
            return float(data[0]["lat"]), float(data[0]["lon"])
    except Exception:
        return None, None
    return None, None

# ---------------------------------------------------------------------
class LocalFuelApp(Adw.Application):
    def __init__(self):
        super().__init__(application_id="de.localfuel.app")
        self.settings = load_settings()
        self.connect("activate", self.on_activate)
        Adw.StyleManager.get_default().set_color_scheme(Adw.ColorScheme.PREFER_DARK)

        # CSS laden (Akzente/Gradient)
        if os.path.exists(CSS_PATH):
            css = Gtk.CssProvider()
            css.load_from_path(CSS_PATH)
            Gtk.StyleContext.add_provider_for_display(
                Gdk.Display.get_default(), css, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
            )

    # -----------------------------------------------------------------
    def on_activate(self, app):
        # Fenster + Header
        self.win = Adw.ApplicationWindow(application=app)
        self.win.set_title("LocalFuel")
        self.win.set_default_size(420, 720)

        self.toolbar = Adw.ToolbarView()
        self.header = Adw.HeaderBar()
        self.title_label = Gtk.Label(label="LocalFuel")
        self.header.set_title_widget(self.title_label)

        # Back-Button (wird auf Results- und Details-Seite sichtbar)
        self.back_btn = Gtk.Button()
        self.back_btn.set_child(Gtk.Image.new_from_icon_name("go-previous-symbolic"))
        self.back_btn.set_visible(False)
        self.back_btn.connect("clicked", self.on_back_clicked)
        self.header.pack_start(self.back_btn)

        # Spinner (Ladeindikator)
        self.spinner = Gtk.Spinner()
        self.spinner.set_visible(False)
        self.header.pack_end(self.spinner)

        # GPS-Button (nur auf Suchseite sichtbar)
        self.gps_button = Gtk.Button()
        self.gps_button.set_tooltip_text("Aktuelle Position verwenden")
        self.gps_button.add_css_class("flat")
        gps_icon = Gtk.Image.new_from_icon_name("find-location-symbolic")
        gps_icon.set_pixel_size(24)
        self.gps_button.set_child(gps_icon)
        self.header.pack_end(self.gps_button)

        # Menü-Button (Über/Impressum)
        self.menu_btn = Gtk.MenuButton()
        self.menu_btn.set_icon_name("open-menu-symbolic")
        self.header.pack_end(self.menu_btn)

        # Menümodell und Aktionen
        menu = Gio.Menu()
        menu.append("Über/Impressum", "app.about")
        self.menu_btn.set_menu_model(menu)

        about_action = Gio.SimpleAction.new("about", None)
        about_action.connect("activate", self.on_about)
        self.add_action(about_action)

        self.toolbar.add_top_bar(self.header)

        # Stack (Suche <-> Ergebnisse <-> Details)
        self.stack = Gtk.Stack()
        self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)

        # 1. Suche-Seite (nur Filter)
        self.search_page, self.entry, self.fuel, self.radius_spin, self.sort_dd = self.build_search_page()
        self.stack.add_named(self.search_page, "search")

        # 2. Ergebnisse-Seite (nur Liste)
        self.results_page, self.result_list = self.build_results_page()
        self.stack.add_named(self.results_page, "results")

        # 3. Details-Seite
        self.details_box, self.details_list = self.build_details_page()
        self.stack.add_named(self.details_box, "details")

        self.toolbar.set_content(self.stack)

        # ToastOverlay (Hinweise)
        self.toast_overlay = Adw.ToastOverlay()
        self.toast_overlay.set_child(self.toolbar)
        self.win.set_content(self.toast_overlay)

        # Events
        self.gps_button.connect("clicked", self.on_gps_clicked)
        self.entry.connect("activate", self.on_search_clicked)

        self.win.present()

        # Settings laden
        if "last_location" in self.settings:
            self.entry.set_text(self.settings["last_location"])
        if "default_fuel" in self.settings:
            try:
                idx = self.fuel_ids.index(self.settings["default_fuel"])
                self.fuel.set_selected(idx)
            except ValueError:
                self.fuel.set_selected(0)
        if "last_radius" in self.settings:
            self.radius_spin.set_value(max(5, self.settings["last_radius"]))
        if "default_sort" in self.settings:
            try:
                sidx = self.sort_ids.index(self.settings["default_sort"])
                self.sort_dd.set_selected(sidx)
            except ValueError:
                pass

    # UI-Bausteine -----------------------------------------------------
    def build_search_page(self):
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
        box.set_margin_top(12)
        box.set_margin_bottom(12)
        box.set_margin_start(12)
        box.set_margin_end(12)

        # Hero
        hero = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4)
        hero.add_css_class("hero")
        hero.set_margin_bottom(6)
        title = Gtk.Label(label="Finde günstige Preise")
        title.add_css_class("title-2")
        subtitle = Gtk.Label(label="Nach Ort/PLZ oder via GPS – bis 50 km")
        subtitle.add_css_class("dim-label")
        hero.append(title); hero.append(subtitle)
        box.append(hero)

        # Ort
        l = Gtk.Label(label="Ort oder PLZ:"); l.set_xalign(0)
        box.append(l)
        entry = Gtk.Entry()
        entry.set_placeholder_text("z.B. Dresden oder 01067")
        entry.set_hexpand(True)
        box.append(entry)

        # Kraftstoff
        l = Gtk.Label(label="Kraftstofftyp:"); l.set_xalign(0)
        box.append(l)
        self.fuel_ids = ["e5", "e10", "diesel"]
        self.fuel_items = ["Super E5", "Super E10", "Diesel"]
        string_list = Gtk.StringList.new(self.fuel_items)
        fuel = Gtk.DropDown.new(string_list, None)
        fuel.set_selected(0)
        box.append(fuel)

        # Umkreis
        l = Gtk.Label(label="Umkreis (km):"); l.set_xalign(0)
        box.append(l)
        adjustment = Gtk.Adjustment(value=5, lower=5, upper=50, step_increment=1, page_increment=5, page_size=0)
        radius_spin = Gtk.SpinButton(adjustment=adjustment, climb_rate=1, digits=0)
        box.append(radius_spin)

        # Sortierung
        l = Gtk.Label(label="Sortierung:"); l.set_xalign(0)
        box.append(l)
        self.sort_ids = ["price", "dist"]
        self.sort_items = ["Preis", "Entfernung"]
        sort_model = Gtk.StringList.new(self.sort_items)
        sort_dd = Gtk.DropDown.new(sort_model, None)
        sort_dd.set_selected(0)
        box.append(sort_dd)

        # Suchen
        btn = Gtk.Button(label="Preise suchen")
        btn.add_css_class("suggested-action"); btn.add_css_class("cta")
        btn.set_margin_top(10)
        btn.connect("clicked", self.on_search_clicked)
        box.append(btn)

        return box, entry, fuel, radius_spin, sort_dd

    def build_results_page(self):
        scrolled = Gtk.ScrolledWindow()
        scrolled.set_vexpand(True)
        scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)

        result_list = Gtk.ListBox()
        result_list.set_selection_mode(Gtk.SelectionMode.NONE)
        result_list.add_css_class("boxed-list")
        result_list.connect("row-activated", self.on_row_activated)
        scrolled.set_child(result_list)

        return scrolled, result_list

    def build_details_page(self):
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
        box.set_margin_top(16)
        box.set_margin_bottom(16)
        box.set_margin_start(16)
        box.set_margin_end(16)

        scrolled = Gtk.ScrolledWindow()
        scrolled.set_vexpand(True)
        scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)

        details_list = Gtk.ListBox()
        details_list.set_selection_mode(Gtk.SelectionMode.NONE)
        details_list.add_css_class("boxed-list")
        scrolled.set_child(details_list)

        # Diese Zeile hat gefehlt:
        box.append(scrolled)
        
        return box, details_list

    # Navigation -------------------------------------------------------
    def on_back_clicked(self, _btn):
        current_page = self.stack.get_visible_child_name()
        if current_page == "details":
            self.stack.set_visible_child_name("results")
            self.title_label.set_text("Ergebnisse")
            # Back-Button bleibt sichtbar
        elif current_page == "results":
            self.stack.set_visible_child_name("search")
            self.title_label.set_text("LocalFuel")
            self.back_btn.set_visible(False)
            self.gps_button.set_visible(True)

    # Suche / GPS ------------------------------------------------------
    def on_search_clicked(self, _btn):
        ort = self.entry.get_text().strip()
        if not ort:
            self.show_message("Bitte gib eine PLZ oder einen Ort ein.")
            return

        radius = int(max(5, self.radius_spin.get_value()))
        selected_idx = self.fuel.get_selected()
        fuel_id = self.fuel_ids[selected_idx]
        sort_by = self.sort_ids[self.sort_dd.get_selected()]

        self.settings["last_location"] = ort
        self.settings["default_fuel"] = fuel_id
        self.settings["last_radius"] = radius
        self.settings["default_sort"] = sort_by
        save_settings(self.settings)

        self.load_prices(fuel_id, ort, radius, sort_by, self.result_list, self.win, coords=None)

    def on_gps_clicked(self, _btn):
        radius = int(max(5, self.radius_spin.get_value()))
        selected_idx = self.fuel.get_selected()
        fuel_id = self.fuel_ids[selected_idx]
        sort_by = self.sort_ids[self.sort_dd.get_selected()]

        self.settings["last_radius"] = radius
        self.settings["default_fuel"] = fuel_id
        self.settings["default_sort"] = sort_by
        save_settings(self.settings)
        coords = self.get_current_position()
        if not coords:
            self.show_message("Aktuelle Position konnte nicht ermittelt werden.")
            return
        self.entry.set_text("Aktuelle Position")
        self.load_prices(fuel_id, None, radius, sort_by, self.result_list, self.win, coords=coords)

    def set_busy(self, busy: bool):
        if busy:
            self.spinner.set_visible(True)
            self.spinner.start()
        else:
            self.spinner.stop()
            self.spinner.set_visible(False)

    # Daten laden ------------------------------------------------------
    def load_prices(self, fuel_type, location, radius, sort_by, listbox, win, coords=None):
        """Ruft API auf und zeigt Ergebnisliste an."""
        # Liste leeren
        while True:
            child = listbox.get_first_child()
            if child is None: break
            listbox.remove(child)

        self.set_busy(True)
        try:
            # Koordinaten
            if coords:
                lat, lng = coords
            else:
                lat, lng = get_coords_for_location(location)
                if lat is None or lng is None:
                    self.show_message(f"Ort '{location}' konnte nicht gefunden werden.")
                    return

            # --- NEU: URL-Logik mit Proxy ---
            api_key = (ENV_API_KEY or self.settings.get("api_key", "")).strip()
            if not API_BASE and not api_key:
                self.show_message("Kein Proxy konfiguriert und API-Key fehlt.")
                return

            sort_param = sort_by if sort_by in ("price", "dist") else "price"
            headers = {"User-Agent": "LocalFuel/0.1"}

            if API_BASE:
                # Proxy-URL verwenden
                url = (
                    f"{API_BASE}/list?"
                    f"lat={lat}&lng={lng}&rad={radius}&sort={sort_param}&type={fuel_type}"
                )
            else:
                # Direkte API-URL als Fallback
                url = (
                    f"https://creativecommons.tankerkoenig.de/json/list.php?"
                    f"lat={lat}&lng={lng}&rad={radius}&sort={sort_param}&type={fuel_type}&apikey={api_key}"
                )
            
            r = requests.get(url, timeout=10, headers=headers)
            # --- Ende Neu ---
            data = r.json()
        except Exception as e:
            self.show_message(f"Fehler beim Laden der Daten:\n{e}")
            return
        finally:
            self.set_busy(False)

        if not data.get("ok"):
            self.show_message("Fehlerhafte API-Antwort oder ungültiger API-Key.")
            return

        stations = data.get("stations", [])
        if not stations:
            self.show_message("Keine Tankstellen in der Nähe gefunden.")
            # Zurück zur Suche, wenn keine Ergebnisse da sind
            self.stack.set_visible_child_name("search")
            return

        # Zur Ergebnisseite wechseln
        self.stack.set_visible_child_name("results")
        self.title_label.set_text("Ergebnisse")
        self.back_btn.set_visible(True)
        self.gps_button.set_visible(False)

        # Ergebniszeilen
        for s in stations:
            price_val = s.get("price")
            price_str = f"{price_val}⁹" if price_val is not None else "–" # Hochgestellte 9 für €/L
            brand = s.get("brand", "Unbekannt")
            place = s.get("place", "")
            street = s.get("street", "")
            dist = round(s.get("dist", 0), 1)
            is_open = s.get("isOpen", None)

            row = Adw.ActionRow()
            # Titel jetzt nur noch die Marke
            row.set_title(brand)
            row.set_subtitle(f"{street}, {place} ({dist} km)")
            row.add_prefix(Gtk.Image.new_from_icon_name("mark-location-symbolic"))
            row.set_activatable(True)

            # Suffixe in der gewünschten Reihenfolge hinzufügen:
            # 1. "Geöffnet/Geschlossen"-Status
            if is_open is not None:
                tag = Gtk.Label(label=("Geöffnet" if is_open else "Geschlossen"))
                tag.add_css_class("tag")
                tag.add_css_class("open" if is_open else "closed")
                row.add_suffix(tag)

            # 2. Preis als prominentes Label
            price_label = Gtk.Label(label=price_str)
            price_label.add_css_class("price-tag")
            row.add_suffix(price_label)

            # 3. Pfeil-Icon ganz am Ende
            row.add_suffix(Gtk.Image.new_from_icon_name("go-next-symbolic"))


            # Station-Daten am Row-Objekt speichern
            row.station = {
                "id": s.get("id"),
                "brand": brand,
                "name": s.get("name", brand),
                "street": street,
                "houseNumber": s.get("houseNumber", ""),
                "postCode": s.get("postCode", ""),
                "place": place,
                "lat": s.get("lat"),
                "lng": s.get("lng"),
                "dist": dist,
            }

            listbox.append(row)

        self.toast_overlay.add_toast(Adw.Toast(title=f"{len(stations)} Stationen gefunden"))

    def on_row_activated(self, _listbox, row):
        """Wechselt zur Detailseite und lädt Details."""
        if not hasattr(row, "station"):
            return
        st = row.station
        self.show_station_details(st)

    # Detailseite ------------------------------------------------------
    def show_station_details(self, st):
        """Details laden und anzeigen."""
        # Header anpassen
        self.title_label.set_text(st.get("brand") or st.get("name") or "Details")
        self.back_btn.set_visible(True)
        self.gps_button.set_visible(False)
        self.stack.set_visible_child_name("details")

        # Liste leeren
        while True:
            child = self.details_list.get_first_child()
            if child is None:
                break
            self.details_list.remove(child)

        # Basisdaten
        address = f"{st.get('street','')} {st.get('houseNumber','')}, {st.get('postCode','')} {st.get('place','')}".strip()
        head = Adw.ActionRow()
        head.set_title(st.get("name") or st.get("brand") or "Tankstelle")
        head.set_subtitle(address)
        # Standard-Icon für Standort
        head.add_prefix(Gtk.Image.new_from_icon_name("mark-location-symbolic"))
        self.details_list.append(head)

        # Standard-Koordinaten aus der Liste
        map_lat = st.get("lat")
        map_lng = st.get("lng")

        # Öffnung / Services (aus detail.php) + ggf. präzisere Koordinaten
        details = self.fetch_station_detail(st.get("id"))
        if details and details.get("ok") and details.get("station"):
            d = details["station"]

            # Präzisere Koordinaten nutzen, falls vorhanden
            map_lat = d.get("lat") or map_lat
            map_lng = d.get("lng") or map_lng

            # Öffnungszeiten
            opening = d.get("openingTimes") or []
            if opening:
                sep = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
                self.details_list.append(sep)
                for o in opening:
                    row = Adw.ActionRow()
                    text = o.get("text", "")
                    start_raw = o.get("start", "")
                    end_raw = o.get("end", "")

                    # Sekunden entfernen (Format HH:MM:SS -> HH:MM)
                    start = start_raw[:5] if len(start_raw) >= 5 else start_raw
                    end = end_raw[:5] if len(end_raw) >= 5 else end_raw

                    time_label = f"{start}–{end}" if start and end else o.get("time", "")
                    row.set_title(text or "Öffnungszeiten")
                    row.set_subtitle(time_label)
                    # Standard-Icon für Zeit/Uhr
                    row.add_prefix(Gtk.Image.new_from_icon_name("alarm-symbolic"))
                    self.details_list.append(row)

            if d.get("wholeDay"):
                row = Adw.ActionRow()
                row.set_title("Durchgehend geöffnet")
                # Standard-Icon für "OK/Erledigt"
                row.add_prefix(Gtk.Image.new_from_icon_name("object-select-symbolic"))
                self.details_list.append(row)

        # Preise aller Sorten (prices.php)
        prices = self.fetch_station_prices(st.get("id"))
        if prices and prices.get("ok"):
            p = (prices.get("prices") or {}).get(st.get("id")) or {}
            e5 = p.get("e5"); e10 = p.get("e10"); diesel = p.get("diesel")
            if any(v is not None for v in (e5, e10, diesel)):
                sep2 = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
                self.details_list.append(sep2)
                pr = Adw.ActionRow()
                pr.set_title("Preise")
                sub = []
                if e5 is not None: sub.append(f"E5: {e5} €/L")
                if e10 is not None: sub.append(f"E10: {e10} €/L")
                if diesel is not None: sub.append(f"Diesel: {diesel} €/L")
                pr.set_subtitle(" · ".join(sub))
                # Standard-Icon für Preise/Tags
                pr.add_prefix(Gtk.Image.new_from_icon_name("edit-find-replace-symbolic"))
                self.details_list.append(pr)

        # In Karten öffnen (mit präziseren Koordinaten)
        maps = Adw.ActionRow()
        maps.set_title("In Karten öffnen")
        maps.set_subtitle("Position auf der Karte anzeigen")
        # Standard-Icon für Standort/Karte
        maps.add_prefix(Gtk.Image.new_from_icon_name("find-location-symbolic"))
        btn = Gtk.Button.new_from_icon_name("go-next-symbolic")
        btn.add_css_class("flat")
        btn.connect("clicked", lambda _b, lat=map_lat, lng=map_lng: self.open_in_maps(
            lat, lng, st.get("name") or st.get("brand") or "Tankstelle"
        ))
        maps.add_suffix(btn)
        maps.set_activatable(False)
        self.details_list.append(maps)

        # Route planen (OSM Directions)
        route = Adw.ActionRow()
        route.set_title("Route planen")
        route.set_subtitle("Von aktueller Position")
        # Standard-Icon für Navigation/Richtungen
        route.add_prefix(Gtk.Image.new_from_icon_name("send-to-symbolic"))
        rbtn = Gtk.Button.new_from_icon_name("go-next-symbolic")
        rbtn.add_css_class("flat")
        rbtn.connect("clicked", lambda _b, lat=map_lat, lng=map_lng: self.open_route(lat, lng))
        route.add_suffix(rbtn)
        route.set_activatable(False)
        self.details_list.append(route)

    def fetch_station_detail(self, station_id):
        """detail.php – Detaildaten einer Station."""
        if not station_id:
            return None
        
        # --- NEU: URL-Logik mit Proxy ---
        api_key = (ENV_API_KEY or self.settings.get("api_key", "")).strip()
        if not API_BASE and not api_key:
            self.show_message("Kein Proxy konfiguriert und API-Key fehlt.")
            return None
        
        headers = {"User-Agent": "LocalFuel/0.1"}
        if API_BASE:
            url = f"{API_BASE}/detail?id={station_id}"
        else:
            url = f"https://creativecommons.tankerkoenig.de/json/detail.php?id={station_id}&apikey={api_key}"
        # --- Ende Neu ---

        try:
            r = requests.get(url, timeout=10, headers=headers)
            return r.json()
        except Exception:
            return None

    def fetch_station_prices(self, station_id):
        """prices.php – Preise aller Sorten für eine Station."""
        if not station_id:
            return None

        # --- NEU: URL-Logik mit Proxy ---
        api_key = (ENV_API_KEY or self.settings.get("api_key", "")).strip()
        if not API_BASE and not api_key:
            self.show_message("Kein Proxy konfiguriert und API-Key fehlt.")
            return None

        headers = {"User-Agent": "LocalFuel/0.1"}
        if API_BASE:
            url = f"{API_BASE}/prices?ids={station_id}"
        else:
            url = f"https://creativecommons.tankerkoenig.de/json/prices.php?ids={station_id}&apikey={api_key}"
        # --- Ende Neu ---

        try:
            r = requests.get(url, timeout=10, headers=headers)
            return r.json()
        except Exception:
            return None

    def open_in_maps(self, lat, lng, name):
        if lat is None or lng is None:
            self.show_message("Koordinaten fehlen.")
            return

        # mehr Präzision und näherer Zoom
        lat_str = f"{float(lat):.7f}"
        lng_str = f"{float(lng):.7f}"
        osm_url = f"https://www.openstreetmap.org/?mlat={lat_str}&mlon={lng_str}#map=19/{lat_str}/{lng_str}"

        # Nur in grafischer Sitzung versuchen zu öffnen
        if not (os.environ.get("WAYLAND_DISPLAY") or os.environ.get("DISPLAY")):
            print(f"[LocalFuel] Kein Display. Link im Browser öffnen:\n{osm_url}")
            self.show_message("Bitte die App aus Phosh starten (nicht per SSH).")
            return

        # 1) Direkt Firefox (PostmarketOS/Phosh)
        try:
            if shutil.which("firefox"):
                Gio.Subprocess.new(["firefox", osm_url], Gio.SubprocessFlags.NONE)
                return
        except Exception:
            pass

        # 2) Standard-Handler per GIO (öffnet Standard-Browser)
        try:
            Gio.AppInfo.launch_default_for_uri(osm_url, None)
            return
        except Exception:
            pass

        # 3) Fallback: xdg-open
        try:
            Gio.Subprocess.new(["xdg-open", osm_url], Gio.SubprocessFlags.NONE)
            return
        except Exception:
            pass

        # 4) Letzter Fallback: geo:-URI
        try:
            Gio.AppInfo.launch_default_for_uri(f"geo:{lat_str},{lng_str}", None)
        except Exception:
            self.show_message("Konnte keine Karten-App oder Browser öffnen.")

    def open_route(self, dest_lat, dest_lng):
        coords = self.get_current_position()
        if not coords:
            self.show_message("Aktuelle Position konnte nicht ermittelt werden.")
            return
        s_lat, s_lng = coords
        lat1 = f"{float(s_lat):.7f}"
        lng1 = f"{float(s_lng):.7f}"
        lat2 = f"{float(dest_lat):.7f}"
        lng2 = f"{float(dest_lng):.7f}"
        url = f"https://www.openstreetmap.org/directions?engine=fossgis_osrm_car&route={lat1},{lng1};{lat2},{lng2}"

        # Öffnen wie in open_in_maps (Firefox bevorzugt)
        if not (os.environ.get("WAYLAND_DISPLAY") or os.environ.get("DISPLAY")):
            print(f"[LocalFuel] Kein Display. Route im Browser öffnen:\n{url}")
            return
        try:
            if shutil.which("firefox"):
                Gio.Subprocess.new(["firefox", url], Gio.SubprocessFlags.NONE)
                return
        except Exception:
            pass
        try:
            Gio.AppInfo.launch_default_for_uri(url, None)
        except Exception:
            try:
                Gio.Subprocess.new(["xdg-open", url], Gio.SubprocessFlags.NONE)
            except Exception:
                self.show_message("Konnte Route nicht öffnen.")

    def on_filter_toggle(self, _btn):
        """Filter ein-/ausblenden und Zustand speichern."""
        if not hasattr(self, "filter_revealer"):
            return
        reveal = not self.filter_revealer.get_reveal_child()
        self.filter_revealer.set_reveal_child(reveal)
        # Icon anpassen
        icon = "view-less-symbolic" if reveal else "view-more-symbolic"
        self.filter_btn.set_child(Gtk.Image.new_from_icon_name(icon))
        # Zustand merken
        self.settings["filters_collapsed"] = not reveal
        save_settings(self.settings)

    def _auto_collapse_if_small(self):
        """Beim Start auf kleinen Displays automatisch einklappen."""
        if not hasattr(self, "filter_revealer"):
            return False
        
        # Moderne Alternative zu get_allocated_height()
        surface = self.win.get_surface()
        if surface is None:
            return True  # später erneut versuchen
        
        h = surface.get_height()
        if h == 0:
            return True  # später erneut versuchen
        
        if h < 700 and self.filter_revealer.get_reveal_child():
            self.filter_revealer.set_reveal_child(False)
            self.filter_btn.set_child(Gtk.Image.new_from_icon_name("view-more-symbolic"))
            self.settings["filters_collapsed"] = True
            save_settings(self.settings)
        return False

    def get_current_position(self):
        """Ermittelt GPS-Position via GeoClue2 (Phosh/GNOME)."""
        try:
            bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
            mgr = Gio.DBusProxy.new_sync(
                bus, 0, None,
                "org.freedesktop.GeoClue2",
                "/org/freedesktop/GeoClue2/Manager",
                "org.freedesktop.GeoClue2.Manager",
                None,
            )
            res = mgr.call_sync("CreateClient", None, 0, -1, None)
            client_path = res.unpack()[0]

            client = Gio.DBusProxy.new_sync(
                bus, 0, None,
                "org.freedesktop.GeoClue2",
                client_path,
                "org.freedesktop.GeoClue2.Client",
                None,
            )
            props = Gio.DBusProxy.new_sync(
                bus, 0, None,
                "org.freedesktop.GeoClue2",
                client_path,
                "org.freedesktop.DBus.Properties",
                None,
            )
            props.call_sync(
                "Set",
                GLib.Variant("(ssv)", ("org.freedesktop.GeoClue2.Client", "DesktopId", GLib.Variant("s", "de.localfuel.app.desktop"))),
                0, -1, None,
            )
            props.call_sync(
                "Set",
                GLib.Variant("(ssv)", ("org.freedesktop.GeoClue2.Client", "RequestedAccuracyLevel", GLib.Variant("u", 4))),
                0, -1, None,
            )
            client.call_sync("Start", None, 0, -1, None)

            loc_path_v = client.get_cached_property("Location")
            if loc_path_v is None:
                for _ in range(20):
                    GLib.usleep(200_000)
                    loc_path_v = client.get_cached_property("Location")
                    if loc_path_v is not None:
                        break
            if loc_path_v is None:
                return None

            loc_path = loc_path_v.unpack()
            location = Gio.DBusProxy.new_sync(
                bus, 0, None,
                "org.freedesktop.GeoClue2",
                loc_path,
                "org.freedesktop.GeoClue2.Location",
                None,
            )
            lat = location.get_cached_property("Latitude").unpack()
            lng = location.get_cached_property("Longitude").unpack()

            try:
                client.call_sync("Stop", None, 0, -1, None)
            except Exception:
                pass

            return (float(lat), float(lng))
        except Exception:
            return None

    def on_about(self, _action, _param):
        """Zeigt das 'Über'-Fenster an."""
        # Lizenztext laden (optional)
        license_text = ""
        try:
            if os.path.exists(LICENSE_PATH):
                with open(LICENSE_PATH, "r", encoding="utf-8") as f:
                    license_text = f.read()
        except Exception:
            pass

        about = Adw.AboutWindow()
        about.set_transient_for(self.win)
        about.set_application_name("LocalFuel")
        about.set_application_icon("dialog-information-symbolic")
        about.set_version("0.1.0")
        about.set_developers(["LocalProgger"])
        about.set_comments(
            "Preisvergleich für Tankstellen.\n"
            "Daten: tankerkoenig.de (CC BY 4.0)\n"
            "Karten: © OpenStreetMap-Mitwirkende"
        )
        about.set_website("https://www.localprogger.de")
        about.set_copyright("© 2025 Tommy")
        if license_text:
            about.set_license(license_text)

        about.present()

    # -----------------------------------------------------------------
    def show_message(self, text):
        """Zeigt Toast-Nachricht oder Dialog an."""
        if hasattr(self, "toast_overlay"):
            self.toast_overlay.add_toast(Adw.Toast(title=text))
        else:
            dlg = Gtk.MessageDialog(
                transient_for=self.win,
                modal=True,
                buttons=Gtk.ButtonsType.OK,
                message_type=Gtk.MessageType.INFO,
                text=text,
            )
            dlg.connect("response", lambda d, r: d.destroy())
            dlg.present()

# ---------------------------------------------------------------------
if __name__ == "__main__":
    app = LocalFuelApp()
    app.run()
