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

  export let activities: ActivitySummary[] = [];
  export let base: string = '/';

  let mapEl: HTMLDivElement;
  let map: any;
  let sourceReady = false;
  let selectedId: string | null = null;

  const TILE_STYLE = 'https://tiles.openfreemap.org/styles/positron';

  function bboxFromCoords(coords: [number, number][]): [[number, number], [number, number]] {
    const lons = coords.map(([, lon]) => lon);
    const lats = coords.map(([lat]) => lat);
    return [[Math.min(...lons), Math.min(...lats)], [Math.max(...lons), Math.max(...lats)]];
  }

  function toGeoJSON(acts: ActivitySummary[]) {
    return {
      type: 'FeatureCollection' as const,
      features: acts
        .filter(a => a.preview_coords?.length)
        .map(a => ({
          type: 'Feature' as const,
          geometry: {
            type: 'LineString' as const,
            coordinates: a.preview_coords!.map(([lat, lon]) => [lon, lat]),
          },
          properties: { id: a.id, color: sportColor(a.sport) },
        })),
    };
  }

  // Update GeoJSON source whenever activities or sourceReady changes
  $: if (sourceReady && activities) {
    (map?.getSource('activities') as any)?.setData(toGeoJSON(activities));
  }

  // Update paint whenever selectedId changes (after map layers exist)
  $: if (map?.getLayer('act-line')) {
    const sel = selectedId ?? '';
    map.setPaintProperty('act-line', 'line-width',
      ['case', ['==', ['get', 'id'], sel], 4, 2]);
    map.setPaintProperty('act-line', 'line-opacity',
      ['case', ['==', ['get', 'id'], sel], 1.0, 0.55]);
  }

  async function selectActivity(id: string) {
    selectedId = selectedId === id ? null : id;
    if (!selectedId) return;
    const act = activities.find(a => a.id === selectedId);
    if (act?.preview_coords?.length && map) {
      map.fitBounds(bboxFromCoords(act.preview_coords), { padding: 60, maxZoom: 15, duration: 500 });
    }
    await tick();
    document.getElementById(`fmv-${selectedId}`)?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
  }

  onMount(() => {
    map = new maplibregl.Map({
      container: mapEl,
      style: TILE_STYLE,
      center: [0, 30],
      zoom: 2,
      attributionControl: false,
    });
    map.addControl(new maplibregl.NavigationControl({ showCompass: false }), 'top-right');
    map.addControl(new maplibregl.AttributionControl({ compact: true }), 'bottom-right');

    map.on('load', () => {
      map.addSource('activities', {
        type: 'geojson',
        data: { type: 'FeatureCollection', features: [] },
      });

      // Wide transparent hit area for easier clicking
      map.addLayer({ id: 'act-hit', type: 'line', source: 'activities',
        paint: { 'line-color': 'transparent', 'line-width': 14 } });

      map.addLayer({ id: 'act-line', type: 'line', source: 'activities',
        layout: { 'line-cap': 'round', 'line-join': 'round' },
        paint: {
          'line-color': ['get', 'color'],
          'line-width': 2,
          'line-opacity': 0.55,
        },
      });

      map.on('click', 'act-hit', (e: any) => {
        const id = e.features?.[0]?.properties?.id;
        if (id) selectActivity(id);
      });
      map.on('mouseenter', 'act-hit', () => { map.getCanvas().style.cursor = 'pointer'; });
      map.on('mouseleave', 'act-hit', () => { map.getCanvas().style.cursor = ''; });

      sourceReady = true;

      // Fit to all loaded tracks on initial render
      const geo = toGeoJSON(activities);
      if (geo.features.length > 0) {
        const allCoords = geo.features.flatMap(f => (f.geometry as any).coordinates as [number, number][]);
        const lons = allCoords.map(c => c[0]);
        const lats = allCoords.map(c => c[1]);
        map.fitBounds(
          [[Math.min(...lons), Math.min(...lats)], [Math.max(...lons), Math.max(...lats)]],
          { padding: 40, maxZoom: 14, duration: 0 }
        );
      }
    });
  });

  onDestroy(() => { map?.remove(); });
</script>

<div class="rounded-xl border border-zinc-800 overflow-hidden">
  <!-- Map -->
  <div style="height: 420px;" class="relative">
    <div bind:this={mapEl} class="w-full h-full"></div>
  </div>

  <!-- Activity list -->
  <div class="border-t border-zinc-800 max-h-[360px] overflow-y-auto">
    {#if activities.length === 0}
      <p class="text-zinc-500 text-sm text-center py-8">No activities to display.</p>
    {:else}
      <div class="divide-y divide-zinc-800/60">
        {#each activities as a (a.id)}
          <div
            id="fmv-{a.id}"
            class="px-4 py-3 cursor-pointer transition-colors hover:bg-zinc-800/40"
            class:bg-zinc-800={selectedId === a.id}
            on:click={() => selectActivity(a.id)}
            role="button"
            tabindex="0"
            on:keydown={e => e.key === 'Enter' && selectActivity(a.id)}
          >
            <!-- Summary row -->
            <div class="flex items-center justify-between gap-3">
              <div class="flex items-center gap-2 min-w-0">
                <span class="shrink-0 text-base">{sportIcon(a.sport)}</span>
                <div class="min-w-0">
                  <a
                    href={a.detail_url ? `${base}activity/${a.id}/` : `${base}activity/local/?id=${a.id}`}
                    class="text-sm font-medium text-white hover:text-[--accent] transition-colors truncate block"
                    on:click|stopPropagation
                  >{a.title}</a>
                  <p class="text-xs text-zinc-500">
                    {formatDate(a.started_at)}{#if a.handle} · @{a.handle}{/if}
                  </p>
                </div>
              </div>
              <div class="flex gap-3 text-xs shrink-0">
                {#if a.distance_m != null}
                  <span class="text-white">{formatDistance(a.distance_m)}</span>
                {/if}
                {#if a.elevation_gain_m != null}
                  <span class="text-zinc-400">↑{formatElevation(a.elevation_gain_m)}</span>
                {/if}
              </div>
            </div>

            <!-- Expanded stats when selected -->
            {#if selectedId === a.id}
              <div class="mt-2.5 pl-8 grid grid-cols-2 sm:grid-cols-4 gap-x-6 gap-y-1 text-xs">
                {#if a.moving_time_s != null}
                  <div><span class="text-white font-medium">{formatDuration(a.moving_time_s)}</span> <span class="text-zinc-500">time</span></div>
                {/if}
                {#if a.avg_speed_kmh != null}
                  <div><span class="text-white font-medium">{a.avg_speed_kmh.toFixed(1)} km/h</span> <span class="text-zinc-500">avg speed</span></div>
                {/if}
                {#if a.avg_hr_bpm != null}
                  <div><span class="text-white font-medium">{a.avg_hr_bpm} bpm</span> <span class="text-zinc-500">avg HR</span></div>
                {/if}
                {#if a.avg_power_w != null}
                  <div><span class="text-white font-medium">{a.avg_power_w} W</span> <span class="text-zinc-500">avg power</span></div>
                {/if}
              </div>
            {/if}
          </div>
        {/each}
      </div>
    {/if}
  </div>
</div>
