<script lang="ts">
  import { onMount, onDestroy } from 'svelte';
  import maplibregl from 'maplibre-gl';
  import 'maplibre-gl/dist/maplibre-gl.css';
  import type { ActivitySummary } from '../lib/types';
  import { loadIndexPaged, loadActivity, loadTimeseries } from '../lib/dataloader';
  import { formatDistance, formatDate, sportIcon } from '../lib/format';

  export let base: string = '/';

  // ── Activity picker ───────────────────────────────────────────────────────
  let activities: ActivitySummary[] = [];
  let pendingShards: string[] = [];
  let loadingActivities = true;
  let loadingMore = false;
  let searchQuery = '';
  let selectedActivity: ActivitySummary | null = null;

  // ── Track data ────────────────────────────────────────────────────────────
  let gpsPoints: [number, number][] = [];   // [lat, lon]
  let elevations: (number | null)[] = [];   // parallel to gpsPoints
  let startIdx = 0;
  let endIdx = 0;
  let loadingTrack = false;
  let trackError = '';

  // ── Segment form ──────────────────────────────────────────────────────────
  let segName = '';
  let segSport = '';
  let saving = false;
  let saveError = '';
  let justSaved = false;
  let lastSavedName = '';

  // ── Map ───────────────────────────────────────────────────────────────────
  let mapEl: HTMLDivElement;
  let map: any;
  let mapReady = false;
  let refSegments: { id: string; name: string; polyline: [number, number][] }[] = [];
  let refPopup: any = null;

  const TILE_STYLE = 'https://tiles.openfreemap.org/styles/positron';
  const DIM_COLOR  = '#71717a';
  const SEL_COLOR  = '#3b82f6';
  const REF_GRADIENT = ['interpolate', ['linear'], ['line-progress'], 0, '#22c55e', 1, '#ef4444'];

  // ── Derived ───────────────────────────────────────────────────────────────
  $: filteredActivities = activities.filter(a =>
    a.preview_coords != null && (
      !searchQuery ||
      (a.title ?? '').toLowerCase().includes(searchQuery.toLowerCase()) ||
      formatDate(a.started_at).toLowerCase().includes(searchQuery.toLowerCase())
    )
  );

  $: maxIdx = Math.max(0, gpsPoints.length - 1);
  $: startPct = maxIdx > 0 ? (startIdx / maxIdx) * 100 : 0;
  $: endPct   = maxIdx > 0 ? (endIdx   / maxIdx) * 100 : 100;
  $: selectedPolyline = gpsPoints.slice(startIdx, endIdx + 1);
  $: selectedDistance = polylineDistance(selectedPolyline);
  $: canSave = segName.trim().length > 0 && selectedPolyline.length >= 2 && selectedDistance >= 500 && !saving;

  // ── Init ──────────────────────────────────────────────────────────────────
  onMount(async () => {
    const params = new URLSearchParams(window.location.search);
    const preId = params.get('activity');
    try {
      const { index, pendingShards: ps } = await loadIndexPaged(base);
      activities = index.activities;
      pendingShards = ps;
      if (preId) {
        const found = activities.find(a => a.id === preId);
        if (found) await selectActivity(found);
      }
    } catch { /* ignore */ }
    loadingActivities = false;
  });

  onDestroy(() => map?.remove());

  // ── Activity selection ────────────────────────────────────────────────────
  async function selectActivity(a: ActivitySummary) {
    selectedActivity = a;
    segSport = a.sport ?? '';
    loadingTrack = true;
    trackError = '';
    gpsPoints = [];
    mapReady = false;
    map?.remove();

    try {
      const detail = await loadActivity(a.id, a.detail_url ?? '', base);
      if (!detail?.timeseries_url) { trackError = 'No GPS data for this activity.'; loadingTrack = false; return; }
      const ts = await loadTimeseries(detail.timeseries_url, a.detail_url ?? '', base);
      if (!ts) { trackError = 'Could not load GPS track.'; loadingTrack = false; return; }

      const pts: [number, number][] = [];
      const elevs: (number | null)[] = [];
      const lats = ts.lat ?? [], lons = ts.lon ?? [], tsElevs: (number | null)[] = ts.elevation_m ?? [];
      for (let i = 0; i < lats.length; i++) {
        if (lats[i] != null && lons[i] != null) {
          pts.push([lats[i]!, lons[i]!]);
          const e = tsElevs[i];
          elevs.push(typeof e === 'number' ? e : null);
        }
      }
      if (pts.length < 2) { trackError = 'Not enough GPS points.'; loadingTrack = false; return; }

      gpsPoints = pts;
      elevations = elevs;
      startIdx = 0;
      endIdx = pts.length - 1;
      loadingTrack = false;

      // Fetch existing segments in this bbox as reference (fire-and-forget)
      refSegments = [];
      const lonMin = Math.min(...pts.map(p => p[1]));
      const latMin = Math.min(...pts.map(p => p[0]));
      const lonMax = Math.max(...pts.map(p => p[1]));
      const latMax = Math.max(...pts.map(p => p[0]));
      fetch(`/api/segments?bbox=${lonMin.toFixed(5)},${latMin.toFixed(5)},${lonMax.toFixed(5)},${latMax.toFixed(5)}`, { credentials: 'include' })
        .then(r => r.ok ? r.json() : [])
        .then(segs => { refSegments = segs; })
        .catch(() => {});

      // Map init happens after the DOM updates (bind:this needs the element)
      setTimeout(initMap, 0);
    } catch {
      trackError = 'Failed to load track.';
      loadingTrack = false;
    }
  }

  async function loadMoreActivities() {
    if (!pendingShards.length || loadingMore) return;
    loadingMore = true;
    try {
      const url = pendingShards[0];
      pendingShards = pendingShards.slice(1);
      const r = await fetch(url);
      const shard = await r.json();
      const shardBase = url.substring(0, url.lastIndexOf('/') + 1);
      const newActs = (shard.activities ?? []).map((a: ActivitySummary) => ({
        ...a,
        detail_url: a.detail_url?.startsWith('http') ? a.detail_url : shardBase + a.detail_url,
        track_url:  a.track_url?.startsWith('http')  ? a.track_url  : shardBase + a.track_url,
      }));
      activities = [...activities, ...newActs];
    } catch { /* ignore */ }
    loadingMore = false;
  }

  // ── Map ───────────────────────────────────────────────────────────────────
  function toLine(pts: [number, number][]) {
    return {
      type: 'Feature' as const,
      geometry: { type: 'LineString' as const, coordinates: pts.map(([lat, lon]) => [lon, lat]) },
    };
  }

  function toRefCollection(segs: typeof refSegments) {
    return {
      type: 'FeatureCollection' as const,
      features: segs.map(s => ({
        type: 'Feature' as const,
        geometry: { type: 'LineString' as const, coordinates: s.polyline.map(([lat, lon]: [number, number]) => [lon, lat]) },
        properties: { id: s.id, name: s.name },
      })),
    };
  }

  function initMap() {
    if (!mapEl || !gpsPoints.length) return;
    const lats = gpsPoints.map(p => p[0]);
    const lons = gpsPoints.map(p => p[1]);

    map = new maplibregl.Map({
      container: mapEl,
      style: TILE_STYLE,
      bounds: [[Math.min(...lons), Math.min(...lats)], [Math.max(...lons), Math.max(...lats)]],
      fitBoundsOptions: { padding: 40 },
      attributionControl: false,
    });
    map.addControl(new maplibregl.AttributionControl({ compact: true }), 'bottom-right');

    map.on('load', () => {
      // Reference segments — rendered below the activity track
      map.addSource('refs', {
        type: 'geojson',
        lineMetrics: true,
        data: toRefCollection(refSegments),
      });
      map.addLayer({ id: 'ref-line', type: 'line', source: 'refs',
        layout: { 'line-cap': 'round', 'line-join': 'round' },
        paint: {
          'line-gradient': REF_GRADIENT,
          'line-width': 6,
          'line-opacity': 0.65,
        },
      });

      map.on('mouseenter', 'ref-line', (e: any) => {
        map.getCanvas().style.cursor = 'pointer';
        const name = e.features?.[0]?.properties?.name;
        if (name) {
          refPopup = new maplibregl.Popup({ closeButton: false, closeOnClick: false, offset: 8 })
            .setLngLat(e.lngLat)
            .setText(name)
            .addTo(map);
        }
      });
      map.on('mouseleave', 'ref-line', () => {
        map.getCanvas().style.cursor = '';
        refPopup?.remove();
        refPopup = null;
      });

      map.addSource('full',     { type: 'geojson', data: toLine(gpsPoints) });
      map.addSource('selected', { type: 'geojson', data: toLine(selectedPolyline) });

      map.addLayer({ id: 'full-line', type: 'line', source: 'full',
        layout: { 'line-cap': 'round', 'line-join': 'round' },
        paint: { 'line-color': DIM_COLOR, 'line-width': 2.5 } });

      map.addLayer({ id: 'sel-line', type: 'line', source: 'selected',
        layout: { 'line-cap': 'round', 'line-join': 'round' },
        paint: { 'line-color': SEL_COLOR, 'line-width': 4.5 } });

      mapReady = true;
    });
  }

  $: if (mapReady && map?.getSource('selected')) {
    map.getSource('selected').setData(toLine(selectedPolyline));
  }

  // Update ref layer when segments load after the map is already ready
  $: if (mapReady && map?.getSource('refs')) {
    map.getSource('refs').setData(toRefCollection(refSegments));
  }

  // ── Slider guards ─────────────────────────────────────────────────────────
  function onStartInput() { if (startIdx >= endIdx) startIdx = Math.max(0, endIdx - 1); }
  function onEndInput()   { if (endIdx <= startIdx) endIdx = Math.min(maxIdx, startIdx + 1); }

  // ── Elevation chart ───────────────────────────────────────────────────────
  const ECHART_W = 600;
  const ECHART_H = 80;
  const ECHART_PAD = 4;  // px margin at top

  $: chartPad = Math.max(1, Math.floor(maxIdx * 0.04));
  $: viewLo = Math.max(0, startIdx - chartPad);
  $: viewHi = Math.min(maxIdx, endIdx + chartPad);

  $: elevStats = (() => {
    const visible: number[] = [];
    for (let i = viewLo; i <= viewHi; i++) {
      const e = elevations[i];
      if (e != null) visible.push(e);
    }
    if (!visible.length) return { min: 0, max: 0, range: 1, hasData: false };
    let min = visible[0], max = visible[0];
    for (const v of visible) { if (v < min) min = v; if (v > max) max = v; }
    return { min, max, range: Math.max(1, max - min), hasData: true };
  })();

  function buildElevPaths(
    elevs: (number | null)[],
    si: number, ei: number,
    vLo: number, vHi: number,
    stats: { min: number; max: number; range: number; hasData: boolean },
  ): { dim: string; sel: string; selLine: string } {
    if (!stats.hasData || !elevs.length) return { dim: '', sel: '', selLine: '' };
    const span = Math.max(1, vHi - vLo);
    const usableH = ECHART_H - ECHART_PAD;

    function area(lo: number, hi: number): { area: string; line: string } {
      let firstX = -1, lastX = -1;
      const linePts: string[] = [];
      for (let i = lo; i <= hi; i++) {
        const e = elevs[i];
        if (e == null) continue;
        const x = ((i - vLo) / span) * ECHART_W;
        const y = ECHART_PAD + usableH * (1 - (e - stats.min) / stats.range);
        if (firstX < 0) firstX = x;
        lastX = x;
        linePts.push(`L${x.toFixed(1)},${y.toFixed(1)}`);
      }
      if (!linePts.length) return { area: '', line: '' };
      const areaPath = `M${firstX.toFixed(1)},${ECHART_H} ${linePts.join(' ')} L${lastX.toFixed(1)},${ECHART_H} Z`;
      const linePath = `M${linePts[0].slice(1)} ${linePts.slice(1).join(' ')}`;
      return { area: areaPath, line: linePath };
    }

    const dimResult = area(vLo, vHi);
    const selResult = area(Math.max(si, vLo), Math.min(ei, vHi));
    return { dim: dimResult.area, sel: selResult.area, selLine: selResult.line };
  }

  $: elevPaths = buildElevPaths(elevations, startIdx, endIdx, viewLo, viewHi, elevStats);

  // ── Save ──────────────────────────────────────────────────────────────────
  async function save() {
    if (!canSave) return;
    saving = true; saveError = '';
    try {
      const r = await fetch('/api/segments', {
        method: 'POST', credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          name: segName.trim(),
          sport: segSport || null,
          polyline: selectedPolyline,
          distance_m: selectedDistance,
        }),
      });
      const d = await r.json();
      if (r.ok) {
        lastSavedName = segName.trim();
        justSaved = true;
        saving = false;
      } else {
        saveError = d.detail ?? 'Failed to save segment';
        saving = false;
      }
    } catch {
      saveError = 'Could not reach server';
      saving = false;
    }
  }

  function addAnother() {
    justSaved = false;
    segName = '';
    segSport = selectedActivity?.sport ?? '';
    startIdx = 0;
    endIdx = maxIdx;
    saveError = '';
  }

  // ── Helpers ───────────────────────────────────────────────────────────────
  function polylineDistance(pts: [number, number][]): number {
    let d = 0;
    for (let i = 1; i < pts.length; i++) {
      const [lat1, lon1] = pts[i-1], [lat2, lon2] = pts[i];
      const R = 6371000;
      const φ1 = lat1 * Math.PI/180, φ2 = lat2 * Math.PI/180;
      const dφ = (lat2-lat1)*Math.PI/180, dλ = (lon2-lon1)*Math.PI/180;
      const a = Math.sin(dφ/2)**2 + Math.cos(φ1)*Math.cos(φ2)*Math.sin(dλ/2)**2;
      d += 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    }
    return d;
  }
</script>

<div class="max-w-3xl mx-auto px-4 py-6">
  <div class="flex items-center gap-3 mb-6">
    <a href="{base}segments/" class="text-zinc-500 hover:text-white transition-colors text-sm">← Segments</a>
    <h1 class="text-xl font-bold text-white">New Segment</h1>
  </div>

  <!-- ── Step 1: Activity picker ─────────────────────────────────────────── -->
  {#if !selectedActivity}
    <p class="text-zinc-400 text-sm mb-4">Pick an activity to crop the segment from.</p>
    <input
      type="text"
      bind:value={searchQuery}
      placeholder="Search by title or date…"
      class="w-full mb-4 px-3 py-2 rounded-lg bg-zinc-800 border border-zinc-700 text-white placeholder-zinc-500 focus:outline-none focus:border-blue-500 text-sm"
    />

    {#if loadingActivities}
      <p class="text-zinc-500 text-sm">Loading activities…</p>
    {:else if filteredActivities.length === 0}
      <p class="text-zinc-500 text-sm">No GPS activities found{searchQuery ? ' matching your search' : ''}.</p>
    {:else}
      <div class="divide-y divide-zinc-800 rounded-xl border border-zinc-800 overflow-hidden">
        {#each filteredActivities.slice(0, 100) as a (a.id)}
          <button
            class="w-full text-left px-4 py-3 hover:bg-zinc-800 transition-colors flex items-center gap-3"
            on:click={() => selectActivity(a)}
          >
            <span class="text-lg shrink-0">{sportIcon(a.sport as any)}</span>
            <div class="min-w-0 flex-1">
              <p class="text-sm text-white truncate">{a.title ?? 'Untitled'}</p>
              <p class="text-xs text-zinc-500">{formatDate(a.started_at)} · {formatDistance(a.distance_m)}</p>
            </div>
          </button>
        {/each}
      </div>

      {#if filteredActivities.length > 100}
        <p class="text-zinc-600 text-xs mt-2">Showing first 100 results — refine your search to narrow down.</p>
      {/if}

      {#if pendingShards.length > 0}
        <button
          class="mt-4 text-sm text-zinc-400 hover:text-white transition-colors"
          on:click={loadMoreActivities}
          disabled={loadingMore}
        >{loadingMore ? 'Loading…' : `Load older activities (${pendingShards.length} more year${pendingShards.length > 1 ? 's' : ''})`}</button>
      {/if}
    {/if}

  <!-- ── Step 2: Crop + form ──────────────────────────────────────────────── -->
  {:else}
    <!-- Activity label + change button -->
    <div class="flex items-center gap-3 mb-4 p-3 rounded-lg bg-zinc-800 border border-zinc-700">
      <span class="text-lg">{sportIcon(selectedActivity.sport as any)}</span>
      <div class="flex-1 min-w-0">
        <p class="text-sm text-white truncate">{selectedActivity.title ?? 'Untitled'}</p>
        <p class="text-xs text-zinc-500">{formatDate(selectedActivity.started_at)}</p>
      </div>
      <button
        class="text-xs text-zinc-400 hover:text-white transition-colors shrink-0"
        on:click={() => { selectedActivity = null; gpsPoints = []; elevations = []; refSegments = []; map?.remove(); mapReady = false; }}
      >Change</button>
    </div>

    {#if loadingTrack}
      <div class="h-64 flex items-center justify-center text-zinc-500 text-sm">Loading track…</div>
    {:else if trackError}
      <div class="h-32 flex items-center justify-center text-red-400 text-sm">{trackError}</div>
    {:else}
      <!-- Map -->
      <div class="rounded-xl overflow-hidden mb-4" style="height: 320px;">
        <div bind:this={mapEl} class="w-full h-full"></div>
      </div>

      <!-- Elevation profile -->
      {#if elevPaths.dim}
        <div class="relative mb-4 rounded-lg overflow-hidden bg-zinc-900 border border-zinc-800" style="height:84px">
          <svg viewBox="0 0 {ECHART_W} {ECHART_H}" preserveAspectRatio="none" class="w-full h-full">
            <path d={elevPaths.dim} fill="#3f3f46" opacity="0.5" />
            <path d={elevPaths.sel} fill="#3b82f6" opacity="0.55" />
            {#if elevPaths.selLine}
              <path d={elevPaths.selLine} fill="none" stroke="#93c5fd" stroke-width="1.5" />
            {/if}
          </svg>
          <div class="absolute inset-0 flex flex-col justify-between pointer-events-none px-2 py-1">
            <span class="text-zinc-500 text-xs leading-none">{Math.round(elevStats.max)}m</span>
            <span class="text-zinc-500 text-xs leading-none">{Math.round(elevStats.min)}m</span>
          </div>
        </div>
      {/if}

      <!-- Dual-range slider -->
      <div class="mb-5">
        <p class="text-xs text-zinc-500 mb-2">Drag the handles to set the segment start and end</p>
        <div class="range-wrap">
          <div class="range-track"></div>
          <div class="range-fill" style="left:{startPct}%; right:{100-endPct}%"></div>
          <input type="range" min={0} max={maxIdx} step={1}
            bind:value={startIdx} on:input={onStartInput} />
          <input type="range" min={0} max={maxIdx} step={1}
            bind:value={endIdx} on:input={onEndInput} />
        </div>
        <p class="text-xs text-zinc-400 mt-2">
          Selected: <span class="text-white font-medium">{formatDistance(selectedDistance)}</span>
          ({selectedPolyline.length} points)
          {#if selectedDistance < 500}
            <span class="text-amber-400 ml-1">— minimum 500 m</span>
          {/if}
        </p>
      </div>

      <!-- Form -->
      <div class="flex gap-3 mb-4">
        <input
          type="text"
          bind:value={segName}
          placeholder="Segment name…"
          maxlength="80"
          class="flex-1 px-3 py-2 rounded-lg bg-zinc-800 border border-zinc-700 text-white placeholder-zinc-500 focus:outline-none focus:border-blue-500 text-sm"
        />
        <select
          bind:value={segSport}
          class="px-3 py-2 rounded-lg bg-zinc-800 border border-zinc-700 text-white focus:outline-none focus:border-blue-500 text-sm"
        >
          <option value="">Any sport</option>
          <option value="cycling">Cycling</option>
          <option value="running">Running</option>
          <option value="skiing">Skiing</option>
          <option value="hiking">Hiking</option>
        </select>
      </div>

      {#if justSaved}
        <div class="rounded-lg bg-zinc-800 border border-zinc-700 px-4 py-3 flex flex-col gap-3">
          <p class="text-sm text-white">
            <span class="text-green-400 font-medium">✓ "{lastSavedName}" saved.</span>
            Add another segment from this activity?
          </p>
          <div class="flex gap-2">
            <button
              on:click={addAnother}
              class="flex-1 py-2 rounded-lg text-sm font-medium bg-blue-600 hover:bg-blue-500 text-white transition-colors"
            >Add another</button>
            <a
              href="{base}segments/"
              class="flex-1 py-2 rounded-lg text-sm font-medium text-center bg-zinc-700 hover:bg-zinc-600 text-white transition-colors"
            >Done</a>
          </div>
        </div>
      {:else}
        {#if saveError}
          <p class="text-red-400 text-sm mb-3">{saveError}</p>
        {/if}

        <button
          on:click={save}
          disabled={!canSave}
          class="w-full py-2.5 rounded-lg text-sm font-medium transition-colors
                 bg-blue-600 hover:bg-blue-500 text-white
                 disabled:opacity-40 disabled:cursor-not-allowed"
        >{saving ? 'Saving…' : 'Save segment'}</button>
      {/if}
    {/if}
  {/if}
</div>

<style>
  .range-wrap {
    position: relative;
    height: 28px;
    display: flex;
    align-items: center;
  }
  .range-track {
    position: absolute;
    left: 0; right: 0;
    height: 4px;
    border-radius: 2px;
    background: #3f3f46;
  }
  .range-fill {
    position: absolute;
    height: 4px;
    border-radius: 2px;
    background: #3b82f6;
    pointer-events: none;
  }
  .range-wrap input[type=range] {
    position: absolute;
    left: 0; right: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    pointer-events: none;
    -webkit-appearance: none;
    appearance: none;
    background: transparent;
  }
  .range-wrap input[type=range]::-webkit-slider-thumb {
    pointer-events: all;
    width: 20px; height: 20px;
    border-radius: 50%;
    background: white;
    border: 2px solid #3b82f6;
    cursor: pointer;
    -webkit-appearance: none;
    box-shadow: 0 1px 4px rgba(0,0,0,0.4);
  }
  .range-wrap input[type=range]::-moz-range-thumb {
    pointer-events: all;
    width: 20px; height: 20px;
    border-radius: 50%;
    background: white;
    border: 2px solid #3b82f6;
    cursor: pointer;
    box-shadow: 0 1px 4px rgba(0,0,0,0.4);
  }
</style>
