<script lang="ts">
  /**
   * WLAN-Empfangstracker — listet WLAN-Netze, beim Klick auf ein Netz wechselt
   * die Seite in den Tracker-Modus: laufende RSSI-Anzeige beim Durchlaufen
   * durchs Gebäude. Beim verbundenen Netz live (alle 500 ms), bei Fremd-BSSIDs
   * scan-basiert (~30 s pro Sample, Android-9+-Limit).
   *
   * Stop friert eine `WifiTrackSession` ein und legt sie ins Protokoll.
   */
  import { onMount, onDestroy } from 'svelte';
  import { page } from '$app/stores';
  import { goto } from '$app/navigation';
  import { ChevronDown, RefreshCw, Wifi } from 'lucide-svelte';
  import AppHeader from '$lib/components/AppHeader.svelte';
  import { getProtocol, saveProtocol } from '$lib/db';
  import { addWifiTrackSession, uid } from '$lib/protocols';
  import {
    scanner,
    onWifiSignal,
    type WifiNetwork,
    type WifiSignalEvent,
  } from '$lib/scanner';
  import { sync } from '$lib/sync.svelte';
  import { toast } from '$lib/toast.svelte';
  import type { Protocol, WifiSignalSample, WifiTrackSession } from '$lib/types';

  let protocol = $state<Protocol | null>(null);
  let networks = $state<WifiNetwork[]>([]);
  let connectedBssid = $state<string | null>(null);
  let busy = $state(false);
  let scanning = $state(false);
  let throttled = $state(false);
  let expanded = $state<string | null>(null);

  // Aktive Tracker-Session (null wenn keine läuft)
  let session = $state<WifiTrackSession | null>(null);
  let offSignal: (() => void) | undefined;

  const running = $derived(session?.status === 'running');
  const pastSessions = $derived(
    (protocol?.wifiTrackSessions ?? []).filter((s) => s.status === 'stopped'),
  );

  onMount(async () => {
    const p = await getProtocol($page.params.id ?? '');
    if (!p) {
      toast.show('Protokoll nicht gefunden', 'error');
      goto('/auftraege/');
      return;
    }
    protocol = p;

    // Läuft bereits ein Tracker? → wieder andocken
    const live = p.wifiTrackSessions?.find((s) => s.status === 'running');
    if (live?.runId) {
      try {
        const st = await scanner.getWifiTrackStatus({ runId: live.runId });
        if (st.running) {
          live.samples = st.samples;
          live.mode = st.mode;
          session = live;
          attachSignalListener(live.runId);
          return;
        }
        live.status = 'stopped';
        live.endedAt = Date.now();
        await persist();
      } catch {
        live.status = 'stopped';
        await persist();
      }
    }

    await refreshNetworks();
  });

  onDestroy(() => {
    // Tracker läuft im Plugin (oder im Mock) weiter — nur den Listener lösen
    offSignal?.();
    void persist();
  });

  async function persist(): Promise<void> {
    if (!protocol) return;
    protocol.dirty = true;
    await saveProtocol($state.snapshot(protocol) as Protocol);
    await sync.refreshPending();
  }

  async function refreshNetworks() {
    if (scanning) return;
    scanning = true;
    throttled = false;
    try {
      const triggered = await scanner.startWifiScan().catch(() => ({ triggered: true }));
      if (!triggered.triggered) throttled = true;
      // Kurz warten, dann den frisch gefüllten Cache lesen (im Mock egal)
      await new Promise((r) => setTimeout(r, 800));
      const r = await scanner.wifiScan();
      networks = [...r.networks].sort((a, b) => b.rssi - a.rssi);
      // Aktuell verbundenes Netz aus linkInfo holen
      try {
        const li = await scanner.linkInfo();
        connectedBssid = li.links.find((l) => l.type === 'wifi')?.bssid ?? null;
      } catch {
        connectedBssid = null;
      }
    } catch (e) {
      toast.show(e instanceof Error ? e.message : 'WLAN-Scan fehlgeschlagen', 'error');
    } finally {
      scanning = false;
    }
  }

  function attachSignalListener(runId: string) {
    offSignal?.();
    offSignal = onWifiSignal((e: WifiSignalEvent) => {
      if (e.runId !== runId || !session) return;
      const s: WifiSignalSample = { ts: e.ts, rssi: e.rssi, source: e.source };
      session.samples.push(s);
      // Statistik nachziehen
      const vals = session.samples.map((x) => x.rssi);
      session.min = Math.min(...vals);
      session.max = Math.max(...vals);
      session.avg = Math.round(vals.reduce((a, b) => a + b, 0) / vals.length);
      void persist();
    });
  }

  async function startTrack(n: WifiNetwork) {
    if (!protocol || busy) return;
    busy = true;
    try {
      const { runId, mode } = await scanner.startWifiTrack({
        bssid: n.bssid,
        intervalMs: 500,
      });
      const newSession: WifiTrackSession = {
        id: uid(),
        name: n.ssid || n.bssid,
        startedAt: Date.now(),
        bssid: n.bssid,
        ssid: n.ssid,
        band: n.band,
        channel: n.channel,
        samples: [],
        status: 'running',
        runId,
        mode,
      };
      addWifiTrackSession(protocol, newSession);
      // Den frisch gepushten Eintrag aus dem Array holen (Svelte-Proxy → Reaktivität)
      session = protocol.wifiTrackSessions![protocol.wifiTrackSessions!.length - 1];
      attachSignalListener(runId);
      await persist();
    } catch (e) {
      toast.show(e instanceof Error ? e.message : 'Tracker-Start fehlgeschlagen', 'error');
    } finally {
      busy = false;
    }
  }

  async function stopTrack() {
    if (!session?.runId || busy) return;
    busy = true;
    try {
      const res = await scanner.stopWifiTrack({ runId: session.runId });
      session.samples = res.samples;
      session.min = res.min;
      session.max = res.max;
      session.avg = res.avg;
      session.status = 'stopped';
      session.endedAt = Date.now();
      offSignal?.();
      offSignal = undefined;
      await persist();
      toast.show('Aufzeichnung gespeichert', 'success');
      session = null;
    } catch (e) {
      toast.show(e instanceof Error ? e.message : 'Tracker-Stopp fehlgeschlagen', 'error');
    } finally {
      busy = false;
    }
  }

  /* --- Anzeige-Helfer --- */

  function rssiColor(rssi: number | undefined): string {
    if (rssi == null) return 'text-zinc-500';
    if (rssi >= -55) return 'text-emerald-400';
    if (rssi >= -70) return 'text-amber-400';
    return 'text-red-400';
  }

  function rssiLabel(rssi: number | undefined): string {
    if (rssi == null) return '—';
    if (rssi >= -55) return 'sehr gut';
    if (rssi >= -65) return 'gut';
    if (rssi >= -75) return 'mäßig';
    return 'schwach';
  }

  /** Aktuelles RSSI: letztes Sample der laufenden Session */
  const currentRssi = $derived(
    session && session.samples.length > 0
      ? session.samples[session.samples.length - 1].rssi
      : undefined,
  );

  /** Letzte 60 Samples für die Sparkline */
  const sparklinePoints = $derived.by(() => {
    if (!session || session.samples.length === 0) return '';
    const tail = session.samples.slice(-60);
    const min = Math.min(-85, ...tail.map((s) => s.rssi));
    const max = Math.max(-40, ...tail.map((s) => s.rssi));
    const w = 280;
    const h = 50;
    return tail
      .map((s, i) => {
        const x = (i / Math.max(1, tail.length - 1)) * w;
        const y = h - ((s.rssi - min) / Math.max(1, max - min)) * h;
        return `${x.toFixed(1)},${y.toFixed(1)}`;
      })
      .join(' ');
  });

  function fmtTime(ts: number): string {
    return new Date(ts).toLocaleTimeString('de-DE', {
      hour: '2-digit',
      minute: '2-digit',
    });
  }

  function fmtDateTime(ts: number): string {
    return new Date(ts).toLocaleString('de-DE', {
      day: '2-digit',
      month: '2-digit',
      hour: '2-digit',
      minute: '2-digit',
    });
  }
</script>

{#if protocol}
  <AppHeader
    title={running ? 'Empfangstracker' : 'WLAN-Empfangstracker'}
    subtitle={running ? (session?.ssid ?? '') : protocol.label}
    back
  />

  <div class="flex-1 overflow-y-auto p-3">
    {#if running && session}
      <!-- Tracker-Detail-Ansicht -->
      <div class="rounded-lg bg-zinc-900 p-4">
        <div class="flex items-center justify-between">
          <span class="text-xs text-zinc-500">
            {session.mode === 'connected'
              ? 'Live (verbundenes Netz, 0,5 s)'
              : 'Scan-Modus (~30 s · Android-Limit)'}
          </span>
          <span class="text-xs text-zinc-500">Kanal {session.channel} · {session.band}</span>
        </div>

        <div class="mt-2 flex items-baseline gap-2">
          <span class="text-5xl font-bold {rssiColor(currentRssi)} tabular-nums">
            {currentRssi ?? '—'}
          </span>
          <span class="text-lg text-zinc-400">dBm</span>
          <span class="text-sm {rssiColor(currentRssi)}">{rssiLabel(currentRssi)}</span>
        </div>

        <!-- Sparkline der letzten 60 Samples -->
        {#if sparklinePoints}
          <svg viewBox="0 0 280 50" class="mt-2 w-full" preserveAspectRatio="none">
            <polyline
              points={sparklinePoints}
              fill="none"
              stroke="currentColor"
              stroke-width="1.5"
              class={rssiColor(currentRssi)}
            />
          </svg>
        {/if}

        <div class="mt-2 grid grid-cols-3 gap-2 text-xs">
          <div>
            <div class="text-[10px] uppercase tracking-wide text-zinc-500">Min</div>
            <div class={rssiColor(session.min)}>{session.min ?? '—'} dBm</div>
          </div>
          <div>
            <div class="text-[10px] uppercase tracking-wide text-zinc-500">Ø</div>
            <div class={rssiColor(session.avg)}>{session.avg ?? '—'} dBm</div>
          </div>
          <div>
            <div class="text-[10px] uppercase tracking-wide text-zinc-500">Max</div>
            <div class={rssiColor(session.max)}>{session.max ?? '—'} dBm</div>
          </div>
        </div>

        <p class="mt-2 text-[11px] text-zinc-500">
          {session.samples.length} Samples · seit {fmtTime(session.startedAt)}
        </p>

        <button
          class="mt-3 w-full rounded-lg bg-red-600 py-2 text-sm font-semibold text-white active:bg-red-700 disabled:opacity-50"
          onclick={stopTrack}
          disabled={busy}
        >
          Aufzeichnung beenden
        </button>
      </div>
    {:else}
      <!-- Listen-Ansicht (Netzwahl) -->
      <div class="flex items-center gap-2">
        <h2 class="flex-1 text-sm font-semibold text-zinc-300">WLAN auswählen</h2>
        <button
          class="flex items-center gap-1 rounded bg-zinc-800 px-2 py-1.5 text-xs text-zinc-300 active:bg-zinc-700 disabled:opacity-50"
          onclick={refreshNetworks}
          disabled={scanning}
        >
          <RefreshCw size={14} class={scanning ? 'animate-spin' : ''} />
          Scan
        </button>
      </div>

      {#if throttled}
        <p class="mt-1 text-[11px] leading-tight text-amber-400">
          Android-Scan-Throttling — letztes Cache-Ergebnis wird gezeigt. In 2 Min nochmal.
        </p>
      {/if}

      {#if networks.length === 0}
        <p class="mt-3 text-sm text-zinc-500">Noch keine Netze — bitte Scan starten.</p>
      {:else}
        <div class="mt-2 flex flex-col gap-1">
          {#each networks as n (n.bssid)}
            {@const isConnected = n.bssid === connectedBssid}
            <button
              class="flex items-center gap-2 rounded-lg bg-zinc-800 px-2.5 py-2 text-left active:bg-zinc-700 disabled:opacity-50"
              onclick={() => startTrack(n)}
              disabled={busy}
            >
              <Wifi size={16} class={rssiColor(n.rssi)} />
              <div class="min-w-0 flex-1">
                <div class="truncate text-sm font-medium">{n.ssid}</div>
                <div class="text-[11px] text-zinc-500">
                  Kanal {n.channel} · {n.band}
                  {#if isConnected}
                    · <span class="text-emerald-400">verbunden — Live-Tracking</span>
                  {/if}
                </div>
              </div>
              <span class="shrink-0 text-xs {rssiColor(n.rssi)} tabular-nums">{n.rssi} dBm</span>
            </button>
          {/each}
        </div>
        <p class="mt-2 text-[11px] leading-tight text-zinc-500">
          Tippe ein Netz an — der Tracker zeigt das Signal live, während du durchs Gebäude
          gehst. Verbundenes Netz: alle 0,5 s. Fremdes Netz: alle ~30 s (Android-Limit).
        </p>
      {/if}
    {/if}

    <!-- Frühere Tracker-Sessions -->
    {#if !running && pastSessions.length > 0}
      <h2 class="mb-1 mt-4 text-sm font-semibold text-zinc-300">Frühere Aufzeichnungen</h2>
      {#each pastSessions as s (s.id)}
        <div class="mb-1.5 rounded-lg bg-zinc-900">
          <button
            class="flex w-full items-center justify-between gap-2 p-2.5 text-left"
            onclick={() => (expanded = expanded === s.id ? null : s.id)}
          >
            <div class="min-w-0">
              <div class="truncate text-sm font-medium">{s.name}</div>
              <div class="text-[11px] text-zinc-500">
                {s.samples.length} Samples · Ø {s.avg ?? '—'} dBm ·
                {fmtDateTime(s.startedAt)}
              </div>
            </div>
            <ChevronDown
              size={16}
              class="shrink-0 text-zinc-500 {expanded === s.id ? 'rotate-180' : ''}"
            />
          </button>
          {#if expanded === s.id}
            <div class="border-t border-zinc-800 p-2.5 text-xs">
              <div class="grid grid-cols-3 gap-2">
                <div>
                  <div class="text-[10px] uppercase tracking-wide text-zinc-500">Min</div>
                  <div class={rssiColor(s.min)}>{s.min ?? '—'} dBm</div>
                </div>
                <div>
                  <div class="text-[10px] uppercase tracking-wide text-zinc-500">Ø</div>
                  <div class={rssiColor(s.avg)}>{s.avg ?? '—'} dBm</div>
                </div>
                <div>
                  <div class="text-[10px] uppercase tracking-wide text-zinc-500">Max</div>
                  <div class={rssiColor(s.max)}>{s.max ?? '—'} dBm</div>
                </div>
              </div>
              <p class="mt-1 text-[11px] text-zinc-500">
                BSSID {s.bssid} · Kanal {s.channel} · {s.band}
              </p>
            </div>
          {/if}
        </div>
      {/each}
    {/if}
  </div>
{:else}
  <div class="flex min-h-screen items-center justify-center text-zinc-500">Lädt …</div>
{/if}
