#!/usr/bin/env python3
"""
Blitzortung onweer verklikker
Verbindt via WebSocket met Blitzortung en toont blikseminlagen in real-time.
Stuurt alerts via eigen WebSocket server naar clients (bijv. Android app).
"""

import asyncio
import json
import math
import time
import signal
import argparse
import urllib.request
import urllib.parse
from datetime import datetime, timezone
from colorama import init, Fore, Style, Back

init(autoreset=True)

# ─── Standaard configuratie ───────────────────────────────────────────────────
DEFAULT_HOME_LAT = 51.37     # Venlo regio (pas aan naar jouw locatie)
DEFAULT_HOME_LON = 6.17

# Regio filter: heel Europa
REGION = {
    "west":  -11.0,
    "east":   30.0,
    "north":  62.0,
    "south":  36.0,
}

BLITZORTUNG_SERVERS = [
    "wss://ws1.blitzortung.org/",
    "wss://ws2.blitzortung.org/",
    "wss://ws3.blitzortung.org/",
    "wss://ws4.blitzortung.org/",
    "wss://ws5.blitzortung.org/",
]

SERVER_POORT   = 8094   # WebSocket data
HTTP_POORT     = 8098   # Dashboard website

# ─── Pushover ─────────────────────────────────────────────────────────────────
PUSHOVER_TOKEN = "akcoyykp6r5w66a9c2d1zbpbr5f8et"
PUSHOVER_USER  = "uogq5u6whwz8e4d3c39t9unhwygx58"
# Minimale tijd tussen twee meldingen per zone (seconden)
PUSHOVER_COOLDOWN = {
    50:  5 * 60,   # max 1x per 5 min voor <50 km
    100: 10 * 60,  # max 1x per 10 min voor <100 km
}
# ──────────────────────────────────────────────────────────────────────────────


def pushover_stuur(titel: str, bericht: str, prioriteit: int = 0):
    """Stuur Pushover notificatie (blokkerend, dus in thread aanroepen)."""
    try:
        data = urllib.parse.urlencode({
            "token":    PUSHOVER_TOKEN,
            "user":     PUSHOVER_USER,
            "title":    titel,
            "message":  bericht,
            "priority": prioriteit,
        }).encode()
        req = urllib.request.Request(
            "https://api.pushover.net/1/messages.json",
            data=data,
            method="POST",
        )
        with urllib.request.urlopen(req, timeout=5) as resp:
            return resp.status == 200
    except Exception as e:
        print(f"{Fore.RED}[pushover] Fout: {e}{Style.RESET_ALL}")
        return False


def lzw_decode(data: str) -> str:
    """Decomprimeer LZW-gecomprimeerde Blitzortung WebSocket data."""
    if isinstance(data, bytes):
        data = data.decode('latin-1')
    e = {}
    d = list(data)
    c = d[0]; f = c; result = [c]
    h = 256; o = h
    for i in range(1, len(d)):
        n = ord(d[i])
        entry = d[i] if n < h else (e[n] if n in e else f + c)
        result.append(entry)
        c = entry[0]
        e[o] = f + c
        o += 1
        f = entry
    return ''.join(result)


def haversine(lat1, lon1, lat2, lon2) -> float:
    """Berekent afstand in km tussen twee GPS-coördinaten."""
    R = 6371.0
    dlat = math.radians(lat2 - lat1)
    dlon = math.radians(lon2 - lon1)
    a = (math.sin(dlat / 2) ** 2
         + math.cos(math.radians(lat1))
         * math.cos(math.radians(lat2))
         * math.sin(dlon / 2) ** 2)
    return R * 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))


def richting(lat1, lon1, lat2, lon2) -> str:
    """Geeft kompasrichting van (lat1,lon1) naar (lat2,lon2)."""
    dlon = math.radians(lon2 - lon1)
    x = math.sin(dlon) * math.cos(math.radians(lat2))
    y = (math.cos(math.radians(lat1)) * math.sin(math.radians(lat2))
         - math.sin(math.radians(lat1)) * math.cos(math.radians(lat2)) * math.cos(dlon))
    bearing = (math.degrees(math.atan2(x, y)) + 360) % 360
    richtingen = ["N", "NO", "O", "ZO", "Z", "ZW", "W", "NW"]
    return richtingen[round(bearing / 45) % 8]


def bereken_eta(buffer: list, home_lat: float, home_lon: float, warn_km: float) -> dict | None:
    """
    Schat ETA op basis van centroid-beweging van twee tijdvensters.
    Alleen inslagen binnen 400 km worden meegenomen — verre buien verstoren anders het beeld.
    Venster A: 10-20 min geleden  |  Venster B: 0-10 min geleden
    Snelheid + richting → projectie → moment dat onweer warn_km bereikt.
    """
    LOKAAL_KM = 400  # alleen inslagen binnen deze straal meenemen
    nu = time.time()
    lokaal = [r for r in buffer
              if haversine(home_lat, home_lon, r["lat"], r["lon"]) <= LOKAAL_KM]

    venster_b = [r for r in lokaal if nu - r["server_ts"] <=  600]   # 0–10 min
    venster_a = [r for r in lokaal if 600 < nu - r["server_ts"] <= 1200]  # 10–20 min

    if len(venster_b) < 5 or len(venster_a) < 5:
        return None  # te weinig lokale data

    def centroid(strikes):
        return (sum(s["lat"] for s in strikes) / len(strikes),
                sum(s["lon"] for s in strikes) / len(strikes))

    b_lat, b_lon = centroid(venster_b)
    a_lat, a_lon = centroid(venster_a)

    dt_s = 600  # 10 minuten tussen de twee vensters
    v_lat = (b_lat - a_lat) / dt_s   # graden per seconde
    v_lon = (b_lon - a_lon) / dt_s

    snelheid_kmh = haversine(a_lat, a_lon, b_lat, b_lon) / (dt_s / 3600)
    afstand_nu   = haversine(home_lat, home_lon, b_lat, b_lon)
    afstand_oud  = haversine(home_lat, home_lon, a_lat, a_lon)
    richting_str = richting(home_lat, home_lon, b_lat, b_lon)
    nadert       = afstand_nu < afstand_oud

    # Projecteer elke minuut vooruit, max 3 uur
    eta_s = None
    if nadert and snelheid_kmh > 1:
        for t in range(60, 10800, 60):
            proj_lat = b_lat + v_lat * t
            proj_lon = b_lon + v_lon * t
            if haversine(home_lat, home_lon, proj_lat, proj_lon) <= warn_km:
                eta_s = t
                break

    return {
        "type":          "eta",
        "afstand_km":    round(afstand_nu, 1),
        "snelheid_kmh":  round(snelheid_kmh, 1),
        "richting":      richting_str,
        "nadert":        nadert,
        "eta_s":         eta_s,          # None = geen ETA (beweegt weg / te ver)
        "centroid_lat":  round(b_lat, 3),
        "centroid_lon":  round(b_lon, 3),
    }


class Config:
    def __init__(self, home_lat: float, home_lon: float,
                 km_kritiek: int, km_dichtbij: int, km_regionaal: int):
        self.home_lat = home_lat
        self.home_lon = home_lon
        self.km_kritiek = km_kritiek
        self.km_dichtbij = km_dichtbij
        self.km_regionaal = km_regionaal

    def alert_kleur(self, afstand_km: float) -> str:
        if afstand_km <= self.km_kritiek:
            return Back.RED + Fore.WHITE + Style.BRIGHT
        elif afstand_km <= self.km_dichtbij:
            return Fore.RED + Style.BRIGHT
        elif afstand_km <= self.km_regionaal:
            return Fore.YELLOW + Style.BRIGHT
        return Fore.CYAN

    def alert_label(self, afstand_km: float) -> str:
        if afstand_km <= self.km_kritiek:
            return "KRITIEK"
        elif afstand_km <= self.km_dichtbij:
            return "DICHTBIJ"
        elif afstand_km <= self.km_regionaal:
            return "REGIONAAL"
        return "ver weg"


class Statistieken:
    def __init__(self, cfg: Config):
        self.cfg = cfg
        self.totaal = 0
        self.per_zone = {"kritiek": 0, "dichtbij": 0, "regionaal": 0, "ver": 0}
        self.dichtstbij_km = float("inf")
        self.dichtstbij_tijd = None
        self.start = time.time()

    def registreer(self, afstand_km: float):
        self.totaal += 1
        if afstand_km <= self.cfg.km_kritiek:
            self.per_zone["kritiek"] += 1
        elif afstand_km <= self.cfg.km_dichtbij:
            self.per_zone["dichtbij"] += 1
        elif afstand_km <= self.cfg.km_regionaal:
            self.per_zone["regionaal"] += 1
        else:
            self.per_zone["ver"] += 1
        if afstand_km < self.dichtstbij_km:
            self.dichtstbij_km = afstand_km
            self.dichtstbij_tijd = datetime.now()

    def print_samenvatting(self):
        looptijd = time.time() - self.start
        cfg = self.cfg
        print(f"\n{Fore.CYAN}{'─'*60}")
        print(f"SAMENVATTING  (looptijd: {looptijd/60:.1f} min)")
        print(f"{'─'*60}")
        print(f"  Totaal inlagen   : {self.totaal}")
        print(f"  Kritiek  (<{cfg.km_kritiek}km) : {self.per_zone['kritiek']}")
        print(f"  Dichtbij (<{cfg.km_dichtbij}km) : {self.per_zone['dichtbij']}")
        print(f"  Regionaal(<{cfg.km_regionaal}km): {self.per_zone['regionaal']}")
        if self.dichtstbij_km < float("inf"):
            print(f"  Dichtstbij       : {self.dichtstbij_km:.1f} km  "
                  f"om {self.dichtstbij_tijd.strftime('%H:%M:%S')}")
        print(f"{'─'*60}{Style.RESET_ALL}")


BUFFER_BESTAND = "/root/OnweerApp/server/buffer.json"


class BlitzortungClient:
    BUFFER_DUUR_S = 3600  # 1 uur

    def __init__(self, cfg: Config, ws_server: bool = False):
        self.cfg = cfg
        self.stats = Statistieken(cfg)
        self.ws_server = ws_server
        self.verbonden_clients: set = set()
        self._stop = asyncio.Event()
        self._laatste_pushover: dict[int, float] = {}  # zone_km → timestamp
        self._buffer: list[dict] = self._laad_buffer()
        self._laatste_eta_push: float = 0
        self._laatste_eta: dict | None = None
        self._batch: list[dict] = []

    def _laad_buffer(self) -> list:
        """Laad buffer van schijf, filter inslagen ouder dan 1 uur."""
        try:
            import os
            if not os.path.exists(BUFFER_BESTAND):
                return []
            with open(BUFFER_BESTAND) as f:
                data = json.load(f)
            cutoff = time.time() - self.BUFFER_DUUR_S
            geldig = [r for r in data if r.get("server_ts", 0) >= cutoff]
            print(f"{Fore.CYAN}[buffer] {len(geldig)} inslagen herladen van schijf{Style.RESET_ALL}")
            return geldig
        except Exception as e:
            print(f"{Fore.YELLOW}[buffer] Laden mislukt: {e}{Style.RESET_ALL}")
            return []

    def _sla_buffer_op(self):
        """Sla buffer op naar schijf."""
        try:
            with open(BUFFER_BESTAND, "w") as f:
                json.dump(self._buffer, f)
        except Exception as e:
            print(f"{Fore.YELLOW}[buffer] Opslaan mislukt: {e}{Style.RESET_ALL}")

    async def verwerk_inslag(self, data: dict):
        lat = data.get("lat")
        lon = data.get("lon")
        ts_ns = data.get("time", 0)
        if lat is None or lon is None:
            return

        afstand = haversine(self.cfg.home_lat, self.cfg.home_lon, lat, lon)
        richting_str = richting(self.cfg.home_lat, self.cfg.home_lon, lat, lon)
        ts = datetime.fromtimestamp(ts_ns / 1e9, tz=timezone.utc).astimezone()
        kleur = self.cfg.alert_kleur(afstand)
        label = self.cfg.alert_label(afstand)

        self.stats.registreer(afstand)

        # Terminal output
        tijdstr = ts.strftime("%H:%M:%S")
        symbool = "⚡" if afstand <= self.cfg.km_dichtbij else "·"
        print(f"{kleur}{symbool} {tijdstr}  {label:<10}  "
              f"{afstand:6.1f} km  {richting_str:<3}  "
              f"({lat:.3f}, {lon:.3f}){Style.RESET_ALL}")

        # Sla op in buffer (met server-timestamp voor leeftijdsbepaling)
        record = {
            "lat": lat, "lon": lon,
            "afstand_km": round(afstand, 1),
            "richting": richting_str,
            "label": self.cfg.alert_label(afstand),
            "tijd": tijdstr,
            "timestamp": ts_ns,
            "server_ts": time.time(),  # voor leeftijdsberekening in client
        }
        self._buffer.append(record)
        # Verwijder inslagen ouder dan 1 uur
        cutoff = time.time() - self.BUFFER_DUUR_S
        self._buffer = [r for r in self._buffer if r["server_ts"] >= cutoff]

        # Pushover notificatie
        await self._check_pushover(afstand, richting_str, tijdstr)

        # Toevoegen aan batch (wordt elke 500ms gebundeld verstuurd)
        self._batch.append({
            "lat": lat,
            "lon": lon,
            "afstand_km": round(afstand, 1),
            "richting": richting_str,
            "label": label,
            "tijd": tijdstr,
            "timestamp": ts_ns,
            "server_ts": time.time(),
        })

    async def client_handler(self, websocket):
        """Behandelt inkomende WebSocket client verbindingen."""
        self.verbonden_clients.add(websocket)
        addr = websocket.remote_address
        print(f"{Fore.GREEN}[server] Client verbonden: {addr}{Style.RESET_ALL}")

        # Stuur welkomstbericht
        await websocket.send(json.dumps({
            "type": "verbonden",
            "regio": REGION,
            "home": {"lat": self.cfg.home_lat, "lon": self.cfg.home_lon},
            "zones_km": {
                "kritiek": self.cfg.km_kritiek,
                "dichtbij": self.cfg.km_dichtbij,
                "regionaal": self.cfg.km_regionaal,
            },
        }))

        # Stuur gebufferde inslagen als één batch
        nu = time.time()
        cutoff = nu - self.BUFFER_DUUR_S
        buffer_snapshot = [r for r in self._buffer if r["server_ts"] >= cutoff]
        if buffer_snapshot:
            print(f"{Fore.GREEN}[server] Stuur {len(buffer_snapshot)} gebufferde inslagen naar {addr}{Style.RESET_ALL}")
            try:
                await websocket.send(json.dumps({"type": "batch", "strikes": buffer_snapshot}))
            except Exception:
                pass

        try:
            await websocket.wait_closed()
        except Exception:
            pass
        finally:
            self.verbonden_clients.discard(websocket)
            print(f"{Fore.YELLOW}[server] Client verbroken: {addr}{Style.RESET_ALL}")

    async def start_ws_server(self):
        import websockets
        try:
            async with websockets.serve(self.client_handler, "0.0.0.0", SERVER_POORT):
                print(f"{Fore.GREEN}[server] WebSocket server op ws://0.0.0.0:{SERVER_POORT}{Style.RESET_ALL}")
                await self._stop.wait()
        except OSError as e:
            print(f"{Fore.RED}[server] WebSocket poort {SERVER_POORT} bezet: {e}{Style.RESET_ALL}")
            raise

    async def start_http_server(self):
        """Serveert dashboard.html via HTTP."""
        from http.server import SimpleHTTPRequestHandler
        import socketserver
        import threading
        import os

        dashboard_dir = os.path.dirname(os.path.abspath(__file__))

        class Handler(SimpleHTTPRequestHandler):
            def __init__(self, *args, **kwargs):
                super().__init__(*args, directory=dashboard_dir, **kwargs)

            def log_message(self, fmt, *args):
                pass  # geen HTTP access logs

        class ReuseServer(socketserver.TCPServer):
            allow_reuse_address = True

        def run():
            with ReuseServer(("0.0.0.0", HTTP_POORT), Handler) as httpd:
                httpd.serve_forever()

        thread = threading.Thread(target=run, daemon=True)
        thread.start()
        print(f"{Fore.GREEN}[server] Dashboard op http://0.0.0.0:{HTTP_POORT}/dashboard.html{Style.RESET_ALL}")
        await self._stop.wait()

    def _eta_regel(self) -> str:
        """Geeft een compacte ETA-regel voor in Pushover berichten."""
        eta = self._laatste_eta
        if not eta:
            return ""
        if eta["nadert"]:
            return f"\nOnweer nadert vanuit {eta['richting']} — {eta['afstand_km']} km"
        return f"\nOnweer trekt weg naar {eta['richting']} — {eta['afstand_km']} km"

    async def _check_pushover(self, afstand: float, richting: str, tijdstr: str):
        """Stuur Pushover melding als afstand binnen een zone valt en cooldown voorbij is."""
        import threading
        nu = time.time()
        for zone_km, cooldown in sorted(PUSHOVER_COOLDOWN.items()):
            if afstand <= zone_km:
                laatste = self._laatste_pushover.get(zone_km, 0)
                if nu - laatste >= cooldown:
                    self._laatste_pushover[zone_km] = nu
                    if zone_km == 50:
                        titel = "🚨 Onweer dichtbij!"
                        prio  = 1
                    else:
                        titel = "⚠️ Onweer in de buurt"
                        prio  = 0
                    bericht = (f"Bliksem op {afstand:.1f} km ({richting}) om {tijdstr}"
                               f"\nZone: binnen {zone_km} km"
                               + self._eta_regel())
                    print(f"{Fore.MAGENTA}[pushover] → {titel}: {bericht}{Style.RESET_ALL}")
                    threading.Thread(
                        target=pushover_stuur,
                        args=(titel, bericht, prio),
                        daemon=True,
                    ).start()
                break  # alleen de kleinste toepasselijke zone melden

    def in_regio(self, lat: float, lon: float) -> bool:
        return (REGION["south"] <= lat <= REGION["north"] and
                REGION["west"]  <= lon <= REGION["east"])

    async def verbind_blitzortung(self):
        import websockets
        from websockets.asyncio.client import connect

        headers = {
            "User-Agent": "Mozilla/5.0",
            "Origin": "https://maps.blitzortung.org",
        }
        server_idx = 0
        while not self._stop.is_set():
            url = BLITZORTUNG_SERVERS[server_idx % len(BLITZORTUNG_SERVERS)]
            server_idx += 1
            print(f"{Fore.BLUE}[blitzortung] Verbinden met {url}...{Style.RESET_ALL}")
            try:
                async with connect(
                    url,
                    ping_interval=20,
                    ping_timeout=15,
                    open_timeout=10,
                    additional_headers=headers,
                    proxy=None,
                ) as ws:
                    # Subscription: {"a": 111} = volledig wereldstream
                    # Regio-filter doen we zelf in Python
                    await ws.send(json.dumps({"a": 111}))
                    print(f"{Fore.GREEN}[blitzortung] Verbonden! "
                          f"Filter: W{REGION['west']} E{REGION['east']} "
                          f"N{REGION['north']} S{REGION['south']}{Style.RESET_ALL}")
                    print(f"{Fore.CYAN}{'─'*60}")
                    print(f"  TIJD      ZONE        AFSTAND   DIR  COÖRDINAAT")
                    print(f"{'─'*60}{Style.RESET_ALL}")

                    async for bericht in ws:
                        if self._stop.is_set():
                            break
                        try:
                            decoded = lzw_decode(bericht)
                            data = json.loads(decoded)
                            if "lat" in data and "lon" in data:
                                if self.in_regio(data["lat"], data["lon"]):
                                    await self.verwerk_inslag(data)
                        except (json.JSONDecodeError, Exception):
                            pass

            except websockets.exceptions.ConnectionClosed as e:
                print(f"{Fore.YELLOW}[blitzortung] Gesloten ({e.code}), opnieuw...{Style.RESET_ALL}")
                await asyncio.sleep(3)
            except OSError as e:
                print(f"{Fore.RED}[blitzortung] Verbindingsfout: {e}, volgende server...{Style.RESET_ALL}")
                await asyncio.sleep(5)
            except Exception as e:
                print(f"{Fore.RED}[blitzortung] Fout: {e}{Style.RESET_ALL}")
                await asyncio.sleep(5)

    async def batch_flush(self):
        """Stuurt gebatchte inslagen elke 500ms als één JSON-array naar clients."""
        while not self._stop.is_set():
            await asyncio.sleep(0.5)
            if not self._batch or not self.verbonden_clients:
                self._batch.clear()
                continue
            bericht = json.dumps({"type": "batch", "strikes": self._batch})
            self._batch = []
            weggestuurd = set()
            for client in self.verbonden_clients:
                try:
                    await client.send(bericht)
                except Exception:
                    weggestuurd.add(client)
            self.verbonden_clients -= weggestuurd

    async def eta_broadcast(self):
        """Berekent elke minuut ETA, stuurt naar clients en optioneel Pushover."""
        import threading
        ETA_PUSH_COOLDOWN = 20 * 60   # max 1x per 20 min via Pushover
        ETA_ZICHTBAAR_KM  = 200       # ETA alleen tonen/sturen binnen 200 km

        while not self._stop.is_set():
            await asyncio.sleep(60)

            eta = bereken_eta(
                self._buffer,
                self.cfg.home_lat,
                self.cfg.home_lon,
                self.cfg.km_kritiek,
            )
            if eta is None:
                continue

            # Buffer opslaan
            self._sla_buffer_op()

            # Sla altijd op (voor _eta_regel in strike-meldingen)
            self._laatste_eta = eta if eta["afstand_km"] <= ETA_ZICHTBAAR_KM else None

            # Buiten 200 km: stuur 'verberg ETA' naar clients en stop
            if eta["afstand_km"] > ETA_ZICHTBAAR_KM:
                bericht_ws = json.dumps({"type": "eta", "verberg": True})
                weggestuurd = set()
                for client in self.verbonden_clients:
                    try:
                        await client.send(bericht_ws)
                    except Exception:
                        weggestuurd.add(client)
                self.verbonden_clients -= weggestuurd
                continue

            # Terminal log
            if eta["nadert"] and eta["eta_s"]:
                minuten = eta["eta_s"] // 60
                print(f"{Fore.YELLOW}[eta] Onweer nadert vanuit {eta['richting']} "
                      f"op {eta['afstand_km']} km · {eta['snelheid_kmh']} km/h "
                      f"· ETA {minuten} min{Style.RESET_ALL}")

            # Pushover ETA-update: alleen binnen 200 km, nadert, cooldown
            nu = time.time()
            if (eta["nadert"]
                    and nu - self._laatste_eta_push >= ETA_PUSH_COOLDOWN):
                self._laatste_eta_push = nu
                titel   = "🌩 Onweer update"
                bericht = (f"Onweer nadert vanuit {eta['richting']}\n"
                           f"Afstand: {eta['afstand_km']} km")
                print(f"{Fore.MAGENTA}[pushover] ETA update: {bericht}{Style.RESET_ALL}")
                threading.Thread(
                    target=pushover_stuur,
                    args=(titel, bericht, 0),
                    daemon=True,
                ).start()

            # Stuur naar WebSocket clients
            if not self.verbonden_clients:
                continue
            bericht_ws = json.dumps(eta)
            weggestuurd = set()
            for client in self.verbonden_clients:
                try:
                    await client.send(bericht_ws)
                except Exception:
                    weggestuurd.add(client)
            self.verbonden_clients -= weggestuurd

    def stop(self):
        self._stop.set()

    async def run(self):
        taken = [self.verbind_blitzortung(), self.eta_broadcast(), self.batch_flush()]
        if self.ws_server:
            taken.append(self.start_ws_server())
            taken.append(self.start_http_server())

        loop = asyncio.get_running_loop()

        def handle_signal():
            print(f"\n{Fore.YELLOW}Stoppen...{Style.RESET_ALL}")
            self.stop()

        for sig in (signal.SIGINT, signal.SIGTERM):
            loop.add_signal_handler(sig, handle_signal)

        results = await asyncio.gather(*taken, return_exceptions=True)
        for r in results:
            if isinstance(r, Exception):
                print(f"{Fore.RED}[fout] {r}{Style.RESET_ALL}")
        self._sla_buffer_op()
        self.stats.print_samenvatting()


def main():
    parser = argparse.ArgumentParser(
        description="Blitzortung onweer verklikker",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Voorbeelden:
  python3 blitzortung.py                      # alleen terminal output
  python3 blitzortung.py --server             # + WebSocket server op poort 8095
  python3 blitzortung.py --lat 52.37 --lon 4.90  # Amsterdam als thuislocatie
  python3 blitzortung.py --kritiek 20         # alert bij < 20 km
        """,
    )
    parser.add_argument("--server", action="store_true",
                        help=f"Start WebSocket server op poort {SERVER_POORT}")
    parser.add_argument("--lat", type=float, default=DEFAULT_HOME_LAT,
                        help=f"Thuislocatie latitude (standaard: {DEFAULT_HOME_LAT})")
    parser.add_argument("--lon", type=float, default=DEFAULT_HOME_LON,
                        help=f"Thuislocatie longitude (standaard: {DEFAULT_HOME_LON})")
    parser.add_argument("--kritiek",   type=int, default=50,
                        help="Kritieke afstand in km (standaard: 50)")
    parser.add_argument("--dichtbij",  type=int, default=100,
                        help="Dichtbij afstand in km (standaard: 100)")
    parser.add_argument("--regionaal", type=int, default=300,
                        help="Regionaal afstand in km (standaard: 300)")
    args = parser.parse_args()

    cfg = Config(
        home_lat=args.lat,
        home_lon=args.lon,
        km_kritiek=args.kritiek,
        km_dichtbij=args.dichtbij,
        km_regionaal=args.regionaal,
    )

    print(f"{Fore.CYAN}{'═'*60}")
    print(f"  BLITZORTUNG ONWEER VERKLIKKER")
    print(f"  Thuislocatie : {cfg.home_lat:.4f}°N, {cfg.home_lon:.4f}°E")
    print(f"  Regio filter : W{REGION['west']} E{REGION['east']} "
          f"N{REGION['north']} S{REGION['south']}")
    print(f"  Zones        : kritiek <{cfg.km_kritiek}km  "
          f"dichtbij <{cfg.km_dichtbij}km  "
          f"regionaal <{cfg.km_regionaal}km")
    if args.server:
        print(f"  WS server    : ws://0.0.0.0:{SERVER_POORT}")
        print(f"  Dashboard    : http://0.0.0.0:{HTTP_POORT}/dashboard.html")
    print(f"{'═'*60}{Style.RESET_ALL}\n")

    client = BlitzortungClient(cfg, ws_server=args.server)
    asyncio.run(client.run())


if __name__ == "__main__":
    main()
