/* live-routing.jsx — makes the route REAL.
   Fetches street-following bicycle geometry from the keyless FOSSGIS OSRM bike router for the
   actual A→B, so distance + bike-ETA are computed from live routes instead of baked coordinates.

   To preserve the design's 3-route compare we ask OSRM for GENUINE alternatives on the real bike
   graph (alternatives=3). These ride the actual network — including park paths and inner streets —
   so the route options are real instead of forced perpendicular detours. If OSRM returns fewer
   than 3 distinct corridors (common for short urban trips) we enrich the pool with a couple of
   via-waypoint corridors (still real OSRM bike geometry) so buildRoute() always has a pool to
   choose Fast / Safe / Scenic from.

   buildRoute() then SCORES each corridor against the seed safety layer (safety-data.jsx) and
   assigns roles: Fast = shortest · Safe = least hazard exposure · Scenic = longest.

   Falls back to baked geometry (window.VR_SRC) if all calls fail, so it never breaks. */

const OSRM_BIKE = 'https://routing.openstreetmap.de/routed-bike/route/v1/driving';

// A waypoint offset perpendicular to the start→dest line, `meters` to one side, at the midpoint.
function offsetVia(start, dest, meters) {
  const mid = [(start[0] + dest[0]) / 2, (start[1] + dest[1]) / 2];
  const dLat = dest[0] - start[0], dLng = dest[1] - start[1];
  const L = Math.hypot(dLat, dLng) || 1e-6;
  const pLat = -dLng / L, pLng = dLat / L;            // unit perpendicular
  const mPerLat = 111320, mPerLng = 111320 * Math.cos(mid[0] * Math.PI / 180);
  return [mid[0] + pLat * meters / mPerLat, mid[1] + pLng * meters / mPerLng];
}

// One OSRM request. With `alternatives` it asks for up to N extra routes and returns them ALL
// (each as { coords, steps }); without it, just the single best route.
// OSRM wants lng,lat; geojson geometry returns [lng,lat] → flip back to our [lat,lng].
// steps=true gives REAL turn-by-turn maneuvers; vrCompileSteps (nav-engine.jsx) turns the legs
// into our engine step shape (BG text + icon).
async function osrmRoutes(points, alternatives) {
  const coordStr = points.map((p) => `${p[1]},${p[0]}`).join(';');
  const altParam = alternatives ? `&alternatives=${alternatives}` : '';
  const url = `${OSRM_BIKE}/${coordStr}?overview=full&geometries=geojson&steps=true&annotations=distance${altParam}`;
  const res = await fetch(url);
  if (!res.ok) throw new Error('osrm http ' + res.status);
  const j = await res.json();
  if (j.code !== 'Ok' || !j.routes || !j.routes.length) throw new Error('osrm code ' + j.code);
  return j.routes.map((route) => ({
    coords: route.geometry.coordinates.map(([lng, lat]) => [lat, lng]),
    steps: window.vrCompileSteps ? window.vrCompileSteps(route.legs) : [],
  }));
}

// drop near-identical corridors so the 3-route compare doesn't show the same path three times.
// signature = a handful of evenly-sampled points rounded to ~11 m, joined.
function routeSig(coords) {
  const n = coords.length; if (!n) return '';
  const out = [];
  for (let k = 0; k <= 8; k++) {
    const p = coords[Math.min(n - 1, Math.round(k / 8 * (n - 1)))];
    out.push(p[0].toFixed(4) + ',' + p[1].toFixed(4));
  }
  return out.join('|');
}
function dedupeRoutes(pool) {
  const seen = new Set(); const out = [];
  for (const r of pool) {
    if (!r || !r.coords || r.coords.length < 2) continue;
    const sig = routeSig(r.coords);
    if (seen.has(sig)) continue;
    seen.add(sig); out.push(r);
  }
  return out;
}

window.fetchLiveRoutes = async function (start, dest) {
  try {
    // Primary: genuine alternatives on the real bike graph (includes park paths + inner streets).
    let pool = [];
    try { pool = await osrmRoutes([start, dest], 3); } catch (e) { pool = []; }
    pool = dedupeRoutes(pool);

    // Bias toward the official VELOSOFIZE bike network: when a lane forms a sensible corridor,
    // route O→pO→pD→D so OSRM follows the lane instead of detouring around it (the "take the velo
    // road, not around the park" ask). Each biased corridor is still real OSRM bike geometry.
    const vias = window.vrBikelaneVia ? window.vrBikelaneVia(start, dest, 2) : [];
    if (vias.length) {
      const biased = await Promise.all(vias.map(([pO, pD]) => osrmRoutes([start, pO, pD, dest]).then((r) => r[0]).catch(() => null)));
      pool = dedupeRoutes(pool.concat(biased.filter(Boolean)));
    }

    // Last resort only: if we STILL have a single corridor, add one gentle off-axis alternative so
    // there's something to compare. (No wide perpendicular detours — those produced silly loops.)
    if (pool.length < 2) {
      const gentle = await Promise.all([
        osrmRoutes([start, offsetVia(start, dest, 350), dest]).then((r) => r[0]).catch(() => null),
        osrmRoutes([start, offsetVia(start, dest, -350), dest]).then((r) => r[0]).catch(() => null),
      ]);
      pool = dedupeRoutes(pool.concat(gentle.filter(Boolean)));
    }

    if (!pool.length) throw new Error('all OSRM calls failed');
    console.info(`[veloroute] live routing OK — ${pool.length} corridor(s); ${vias.length} bike-network biased`);
    return pool;   // each is { coords, steps }
  } catch (e) {
    console.warn('[veloroute] live routing failed, using baked geometry:', e.message);
    return null;
  }
};
