<script>
  import { onMount, onDestroy } from 'svelte';
  import maplibregl from 'maplibre-gl';
  import ElevationChart from './ElevationChart.svelte';

  let { activityUrl } = $props();

  const API = `${import.meta.env.VITE_PLANNER_API_URL ?? ''}/api`;

  // ── State ──────────────────────────────────────────────────────────────────
  let mapEl;
  let map;
  let waypoints    = $state([]);   // [{lng, lat, marker}]
  let profile      = $state('gravel');
  let route        = $state(null); // GeoJSON Feature from Brouter
  let loading      = $state(false);
  let error        = $state('');

  // Plans
  let plans        = $state([]);
  let plansLoading = $state(false);
  let activePlanId = $state(null);  // id of currently loaded plan (null = unsaved)
  let savePanel    = $state(false); // show save form
  let saveName     = $state('');
  let saving       = $state(false);

  const PROFILES = [
    { id: 'fastbike', label: 'Road' },
    { id: 'gravel',   label: 'Gravel' },
    { id: 'mtb',      label: 'MTB' },
    { id: 'trekking', label: 'Trekking' },
    { id: 'safety',   label: 'Safety' },
  ];

  // ── Tile layers ────────────────────────────────────────────────────────────
  const TILES = {
    cyclosm:   {
      tiles: [
        'https://a.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png',
        'https://b.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png',
        'https://c.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png',
      ],
      attribution: '© <a href="https://www.cyclosm.org">CyclOSM</a> | © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
      label: 'Cycle',
    },
    osm:       {
      tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
      attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
      label: 'OSM',
    },
    topo:      {
      tiles: [
        'https://a.tile.opentopomap.org/{z}/{x}/{y}.png',
        'https://b.tile.opentopomap.org/{z}/{x}/{y}.png',
        'https://c.tile.opentopomap.org/{z}/{x}/{y}.png',
      ],
      attribution: '© <a href="https://opentopomap.org">OpenTopoMap</a> | © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
      label: 'Topo',
    },
    satellite: {
      tiles: ['https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}'],
      attribution: '© <a href="https://www.esri.com">Esri</a> World Imagery',
      label: 'Sat',
    },
  };
  const TILE_ORDER = ['cyclosm', 'osm', 'topo', 'satellite'];

  let tileLayer = $state('cyclosm');

  function setTileLayer(key) {
    tileLayer = key;
    if (!map) return;
    const src = map.getSource('base');
    if (src) src.setTiles(TILES[key].tiles);
  }

  // ── Map init ───────────────────────────────────────────────────────────────
  onMount(() => {
    map = new maplibregl.Map({
      container: mapEl,
      style: {
        version: 8,
        sources: {
          base: {
            type: 'raster',
            tiles: TILES.cyclosm.tiles,
            tileSize: 256,
            attribution: TILES.cyclosm.attribution,
          },
        },
        layers: [{ id: 'base', type: 'raster', source: 'base' }],
      },
      center: [12, 42],
      zoom: 5,
    });

    map.addControl(new maplibregl.NavigationControl(), 'top-right');

    map.on('load', () => {
      map.addSource('route', { type: 'geojson', data: emptyGeoJSON() });
      map.addLayer({
        id: 'route-line',
        type: 'line',
        source: 'route',
        paint: { 'line-color': '#e879a0', 'line-width': 3, 'line-opacity': 0.9 },
      });
    });

    map.on('click', onMapClick);
    loadPlans();
  });

  onDestroy(() => { if (map) map.remove(); });

  // ── Waypoint management ────────────────────────────────────────────────────
  function onMapClick(e) {
    activePlanId = null;
    addWaypoint(e.lngLat.lng, e.lngLat.lat);
  }

  function addWaypoint(lng, lat) {
    const el = document.createElement('div');
    el.className = 'wp-marker';
    el.textContent = waypoints.length + 1;

    const marker = new maplibregl.Marker({ element: el, draggable: true })
      .setLngLat([lng, lat])
      .addTo(map);

    const wp = { lng, lat, marker };
    marker.on('dragend', () => {
      const { lng: newLng, lat: newLat } = marker.getLngLat();
      const i = waypoints.findIndex(w => w.marker === marker);
      if (i >= 0) {
        waypoints[i].lng = newLng;
        waypoints[i].lat = newLat;
      }
      activePlanId = null;
      fetchRoute();
    });

    waypoints = [...waypoints, wp];
    fetchRoute();
  }

  function removeWaypoint(i) {
    waypoints[i].marker.remove();
    waypoints = waypoints.filter((_, idx) => idx !== i);
    waypoints.forEach((wp, idx) => { wp.marker.getElement().textContent = idx + 1; });
    activePlanId = null;
    fetchRoute();
  }

  function closeLoop() {
    if (waypoints.length < 2) return;
    const first = waypoints[0];
    addWaypoint(first.lng, first.lat);
  }

  function clearAll() {
    waypoints.forEach(wp => wp.marker.remove());
    waypoints = [];
    route = null;
    error = '';
    if (map.getSource('route')) map.getSource('route').setData(emptyGeoJSON());
  }

  // ── Routing ────────────────────────────────────────────────────────────────
  let routeTimer;
  function fetchRoute() {
    clearTimeout(routeTimer);
    if (waypoints.length < 2) {
      route = null;
      error = '';
      if (map.getSource('route')) map.getSource('route').setData(emptyGeoJSON());
      return;
    }
    routeTimer = setTimeout(doFetchRoute, 400);
  }

  async function doFetchRoute() {
    loading = true;
    error = '';
    const lonlats = waypoints.map(wp => `${wp.lng.toFixed(6)},${wp.lat.toFixed(6)}`).join('|');
    const url = `https://brouter.de/brouter?lonlats=${lonlats}&profile=${profile}&alternativeidx=0&format=geojson`;
    try {
      const r = await fetch(url);
      if (!r.ok) throw new Error(`Brouter error ${r.status}`);
      const gj = await r.json();
      route = gj.features?.[0] ?? null;
      if (map.getSource('route')) map.getSource('route').setData(route ?? emptyGeoJSON());
    } catch (e) {
      error = e.message ?? 'Routing failed';
      route = null;
    } finally {
      loading = false;
    }
  }

  // Re-route when profile changes
  $effect(() => { profile; fetchRoute(); });

  // ── GPX export ────────────────────────────────────────────────────────────
  function downloadGPX() {
    if (!route) return;
    const coords = route.geometry.coordinates;
    const trkpts = coords.map(([lon, lat, ele]) =>
      `    <trkpt lat="${lat.toFixed(7)}" lon="${lon.toFixed(7)}">${ele != null ? `<ele>${ele.toFixed(1)}</ele>` : ''}</trkpt>`
    ).join('\n');
    const gpx = `<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.1" creator="BincioPlanner" xmlns="http://www.topografix.com/GPX/1/1">
  <trk><name>Planned route</name><trkseg>
${trkpts}
  </trkseg></trk>
</gpx>`;
    const a = document.createElement('a');
    a.href = URL.createObjectURL(new Blob([gpx], { type: 'application/gpx+xml' }));
    a.download = 'route.gpx';
    a.click();
  }

  // ── Plans API ─────────────────────────────────────────────────────────────
  async function loadPlans() {
    plansLoading = true;
    try {
      const r = await fetch(`${API}/plans`, { credentials: 'include' });
      if (r.ok) plans = (await r.json()).plans ?? [];
    } catch {}
    plansLoading = false;
  }

  async function savePlan() {
    if (!saveName.trim() || waypoints.length < 2) return;
    saving = true;
    const body = {
      name:      saveName.trim(),
      waypoints: waypoints.map(({ lng, lat }) => ({ lng, lat })),
      profile,
      geojson:   route ?? undefined,
    };
    try {
      if (activePlanId) {
        await fetch(`${API}/plans/${activePlanId}`, {
          method: 'PUT', credentials: 'include',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(body),
        });
      } else {
        const r = await fetch(`${API}/plans`, {
          method: 'POST', credentials: 'include',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(body),
        });
        if (r.ok) activePlanId = (await r.json()).id;
      }
      savePanel = false;
      await loadPlans();
    } catch {}
    saving = false;
  }

  async function loadPlan(plan) {
    clearAll();
    profile = plan.profile ?? 'gravel';
    activePlanId = plan.id;
    saveName = plan.name;
    // If the full plan (with geojson) is needed, fetch it; list endpoint omits geojson.
    const r = await fetch(`${API}/plans/${plan.id}`, { credentials: 'include' });
    if (!r.ok) return;
    const full = await r.json();
    for (const { lng, lat } of (full.waypoints ?? [])) addWaypoint(lng, lat);
    if (full.geojson) {
      route = full.geojson;
      if (map.getSource('route')) map.getSource('route').setData(route);
    }
  }

  async function deletePlan(plan, e) {
    e.stopPropagation();
    await fetch(`${API}/plans/${plan.id}`, { method: 'DELETE', credentials: 'include' });
    if (activePlanId === plan.id) activePlanId = null;
    await loadPlans();
  }

  // ── Helpers ────────────────────────────────────────────────────────────────
  function emptyGeoJSON() {
    return { type: 'FeatureCollection', features: [] };
  }

  function routeStats() {
    if (!route) return null;
    const p = route.properties ?? {};
    const dist = parseFloat(p['track-length'] ?? 0) / 1000;
    const up   = parseInt(p['filtered ascend'] ?? p.ascend ?? 0);
    return { dist: dist.toFixed(1), up };
  }

  let stats = $derived(routeStats());
</script>

<div class="layout">
  <!-- Sidebar -->
  <aside class="sidebar">
    <header class="brand">
      <span class="logo">Bincio<span class="accent">Planner</span></span>
      <a href={activityUrl} class="back-link">← Activity</a>
    </header>

    <!-- Map layer selector -->
    <section class="section">
      <p class="label">Map</p>
      <div class="pills">
        {#each TILE_ORDER as key}
          <button
            class="pill"
            class:active={tileLayer === key}
            onclick={() => setTileLayer(key)}
          >{TILES[key].label}</button>
        {/each}
      </div>
    </section>

    <!-- Profile selector -->
    <section class="section">
      <p class="label">Profile</p>
      <div class="pills">
        {#each PROFILES as p}
          <button
            class="pill"
            class:active={profile === p.id}
            onclick={() => { profile = p.id; }}
          >{p.label}</button>
        {/each}
      </div>
    </section>

    <!-- Waypoints -->
    <section class="section">
      <p class="label">Waypoints <span class="muted">— click map to add</span></p>
      {#if waypoints.length === 0}
        <p class="muted small">No waypoints yet.</p>
      {:else}
        <ul class="wp-list">
          {#each waypoints as wp, i}
            <li>
              <span class="wp-num">{i + 1}</span>
              <span class="wp-coord">{wp.lat.toFixed(4)}, {wp.lng.toFixed(4)}</span>
              <button class="remove-btn" onclick={() => removeWaypoint(i)} title="Remove">×</button>
            </li>
          {/each}
        </ul>
      {/if}
      <div class="wp-actions">
        <button class="btn-secondary" onclick={closeLoop} disabled={waypoints.length < 2}>Close loop</button>
        <button class="btn-secondary danger" onclick={clearAll} disabled={waypoints.length === 0}>Clear all</button>
      </div>
    </section>

    <!-- Route stats -->
    {#if route}
      {@const s = routeStats()}
      <section class="section">
        <p class="label">Route</p>
        <div class="stats">
          <div class="stat"><span class="stat-val">{s.dist} km</span><span class="muted">distance</span></div>
          <div class="stat"><span class="stat-val">↑ {s.up} m</span><span class="muted">elevation</span></div>
        </div>
        <button class="btn-primary" onclick={downloadGPX}>Download GPX</button>
      </section>
    {/if}

    {#if loading}<p class="status">Routing…</p>{/if}
    {#if error}<p class="status error">{error}</p>{/if}

    <!-- Save panel -->
    {#if route && waypoints.length >= 2}
      <section class="section">
        <p class="label">Save route</p>
        {#if savePanel}
          <div class="save-form">
            <input
              class="save-input"
              type="text"
              placeholder="Route name…"
              bind:value={saveName}
              onkeydown={(e) => e.key === 'Enter' && savePlan()}
            />
            <div class="wp-actions">
              <button class="btn-primary" onclick={savePlan} disabled={saving || !saveName.trim()}>
                {saving ? 'Saving…' : (activePlanId ? 'Update' : 'Save')}
              </button>
              <button class="btn-secondary" onclick={() => savePanel = false}>Cancel</button>
            </div>
          </div>
        {:else}
          <button class="btn-secondary full-width" onclick={() => { savePanel = true; }}>
            {activePlanId ? '✎ Update plan' : '+ Save plan'}
          </button>
        {/if}
      </section>
    {/if}

    <!-- Plans list -->
    <section class="section">
      <p class="label">My plans {#if plansLoading}<span class="muted">(loading…)</span>{/if}</p>
      {#if plans.length === 0 && !plansLoading}
        <p class="muted small">No saved plans yet.</p>
      {:else}
        <ul class="plan-list">
          {#each plans as plan}
            <li class="plan-item" class:active={activePlanId === plan.id}>
              <button class="plan-load-btn" onclick={() => loadPlan(plan)}>
                <span class="plan-name">{plan.name}</span>
                <span class="plan-meta">{plan.waypoints?.length ?? '?'} pts · {plan.profile}</span>
              </button>
              <button class="remove-btn" onclick={(e) => deletePlan(plan, e)} title="Delete">×</button>
            </li>
          {/each}
        </ul>
      {/if}
    </section>
  </aside>

  <!-- Map + elevation -->
  <main class="map-area">
    <div class="map-wrap" bind:this={mapEl}></div>
    {#if route}
      <div class="elevation-wrap">
        <ElevationChart {route} />
      </div>
    {/if}
  </main>
</div>

<style>
  .layout {
    display: flex;
    height: 100vh;
    overflow: hidden;
  }

  .sidebar {
    width: 260px;
    flex-shrink: 0;
    background: var(--bg-card);
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    padding: 0 0 1rem;
  }

  .brand {
    padding: 0.875rem 1rem;
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-shrink: 0;
  }
  .logo { font-size: 1rem; font-weight: 700; letter-spacing: -0.02em; color: #fff; }
  .accent { color: var(--accent); }
  .back-link { font-size: 0.75rem; color: var(--text-5); text-decoration: none; }
  .back-link:hover { color: var(--text-4); }

  .section {
    padding: 0.875rem 1rem;
    border-bottom: 1px solid var(--border-sub);
  }

  .label {
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-5);
    margin: 0 0 0.5rem;
  }

  .pills { display: flex; gap: 0.375rem; flex-wrap: wrap; }
  .pill {
    padding: 0.25rem 0.625rem;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: transparent;
    color: var(--text-4);
    font-size: 0.75rem;
    cursor: pointer;
    transition: all 0.15s;
  }
  .pill:hover { border-color: var(--accent); color: var(--accent); }
  .pill.active { background: var(--accent-dim); border-color: var(--accent); color: var(--accent); }

  .wp-list { list-style: none; margin: 0 0 0.5rem; padding: 0; display: flex; flex-direction: column; gap: 0.25rem; }
  .wp-list li { display: flex; align-items: center; gap: 0.375rem; font-size: 0.75rem; }
  .wp-num { width: 1.25rem; height: 1.25rem; border-radius: 50%; background: var(--accent); color: #000; font-size: 0.65rem; font-weight: 700; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
  .wp-coord { flex: 1; color: var(--text-4); font-variant-numeric: tabular-nums; }
  .remove-btn { background: none; border: none; color: var(--text-5); cursor: pointer; font-size: 1rem; line-height: 1; padding: 0 0.125rem; }
  .remove-btn:hover { color: #f87171; }

  .wp-actions { display: flex; gap: 0.5rem; margin-top: 0.5rem; }

  .btn-secondary {
    flex: 1;
    padding: 0.375rem 0.5rem;
    border-radius: 0.375rem;
    border: 1px solid var(--border);
    background: transparent;
    color: var(--text-4);
    font-size: 0.75rem;
    cursor: pointer;
    transition: all 0.15s;
  }
  .btn-secondary:hover:not(:disabled) { border-color: var(--text-4); color: var(--text-primary); }
  .btn-secondary.danger:hover:not(:disabled) { border-color: #f87171; color: #f87171; }
  .btn-secondary:disabled { opacity: 0.4; cursor: default; }

  .btn-primary {
    width: 100%;
    margin-top: 0.625rem;
    padding: 0.5rem;
    border-radius: 0.375rem;
    border: none;
    background: var(--accent);
    color: #000;
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
    transition: opacity 0.15s;
  }
  .btn-primary:hover { opacity: 0.85; }

  .stats { display: flex; gap: 1rem; margin-bottom: 0.25rem; }
  .stat { display: flex; flex-direction: column; }
  .stat-val { font-size: 0.9rem; font-weight: 600; color: var(--text-primary); }

  .status { font-size: 0.75rem; color: var(--text-5); padding: 0.5rem 1rem 0; margin: 0; }
  .status.error { color: #f87171; }

  .muted { color: var(--text-5); }
  .small { font-size: 0.75rem; margin: 0; }

  .map-area { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
  .map-wrap { flex: 1; }
  .elevation-wrap { height: 140px; flex-shrink: 0; background: var(--bg-card); border-top: 1px solid var(--border); }

  .save-form { display: flex; flex-direction: column; gap: 0.5rem; }
  .save-input {
    width: 100%;
    padding: 0.375rem 0.5rem;
    border-radius: 0.375rem;
    border: 1px solid var(--border);
    background: var(--bg-elevated);
    color: var(--text-primary);
    font-size: 0.8rem;
    box-sizing: border-box;
  }
  .save-input:focus { outline: none; border-color: var(--accent); }
  .full-width { width: 100%; }

  .plan-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.25rem; }
  .plan-item {
    display: flex;
    align-items: center;
    border-radius: 0.375rem;
    border: 1px solid transparent;
    position: relative;
  }
  .plan-item:hover { background: var(--bg-elevated); border-color: var(--border); }
  .plan-item.active { background: var(--accent-dim); border-color: var(--accent); }
  .plan-load-btn {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    padding: 0.4rem 0.5rem;
    background: none;
    border: none;
    text-align: left;
    cursor: pointer;
    min-width: 0;
  }
  .plan-name { font-size: 0.8rem; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .plan-meta { font-size: 0.7rem; color: var(--text-5); }

  :global(.wp-marker) {
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 50%;
    background: var(--accent);
    color: #000;
    font-size: 0.65rem;
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: grab;
    border: 2px solid #fff;
    box-shadow: 0 1px 4px rgba(0,0,0,0.5);
  }
</style>
