// Live Mapbox basemap for the right-column "DFW · LIVE EXAMPLE" panel.
// The component is decorative — pan/zoom are disabled and gestures fall
// through to the page. The camera is driven entirely by the parent's
// `phase` (intro animation) and `idx` (Curator cycle).
//
// Animation arc:
//   t=0       → wide DFW view (zoom ~9)
//   phase >=2 → flyTo Uptown · 75201 (~zoom 13.5)
//   phase >=4 → idx starts cycling; each idx swaps the overlay package:
//     idx 0  PERSONAS · The Henry          → primary pin + walk-to annotation
//     idx 1  AREA SIGNALS · 75201          → ZIP polygon outline + signal chips
//     idx 2  STITCHING SOURCES · trails    → wider zoom, 3 trail polylines + pins

const { useEffect, useRef, useState } = React;

const MAPBOX_TOKEN = "pk.eyJ1Ijoia2lhbnBseWFuY2UiLCJhIjoiY21vbDFlejh0MGRvcTJ0cTltbTZiNHNvOCJ9.sdqw31mkA9Yvm1chzBTXtQ";

// Brand colors come from the page's :root CSS custom properties. Mapbox
// paint props can't read CSS vars directly, so we resolve `--signal-hex`,
// `--route-hex`, `--rank-hex` once at mount and feed those literals to
// Mapbox layers. HTML markers continue to use var(--*) inline.
function readBrandHex() {
  if (typeof document === "undefined") return { signal: "#d97757", area: "#1f8a85", rank: "#b58d28" };
  const cs = getComputedStyle(document.documentElement);
  const get = (n, fallback) => (cs.getPropertyValue(n).trim() || fallback);
  return {
    signal: get("--signal-hex", "#d97757"),
    area:   get("--route-hex",  "#1f8a85"),
    rank:   get("--rank-hex",   "#b58d28"),
  };
}

// Synchronous read at module load is fine — :root vars are inlined in <style>.
const C = readBrandHex();
const HEX_SIGNAL = C.signal;
const HEX_AREA   = C.area;
const HEX_RANK   = C.rank;

function prefersReducedMotion() {
  return typeof window !== "undefined"
      && window.matchMedia
      && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
}

// ─── Camera waypoints ─────────────────────────────────────────────────
const DFW_WIDE = {
  center: [-96.85, 32.81],
  zoom:   8.7,
  pitch:  0,
  bearing: 0,
};
// Tri-pin DFW view that fits Lakewood + Bishop Arts + Las Colinas
// comfortably, slightly tighter than the intro view.
const TRI_DFW = {
  center: [-96.842, 32.815],
  zoom:   9.6,
  pitch:  0,
  bearing: 0,
};
// Bishop Arts close-up — three brunch spots cluster on a single block of
// Bishop Avenue. Tilt for a touch of drama.
const BISHOP_ARTS = {
  center: [-96.8294, 32.7472],
  zoom:   16.4,
  pitch:  35,
  bearing: -10,
};
// North DFW span — fits Mathews (Plano), Stonewall (East Dallas) and
// Highland Park EL together with margin.
const NORTH_DFW = {
  center: [-96.760, 32.930],
  zoom:   10.1,
  pitch:  0,
  bearing: 0,
};

// Mobile twins of the wider scenes. The mobile map tile is ~360×190 vs
// the desktop right column's ~870×700, so the same zoom level cuts the
// pins and their labels off at the edges. Lower zoom + a centroid-aligned
// center keeps every pin and its label comfortably inside the frame.
// BISHOP_ARTS doesn't need a twin — its three pins cluster within a
// block, and the close-up zoom 16 holds up at any viewport size.
const TRI_DFW_MOBILE = {
  center: [-96.842, 32.820],
  zoom:   8.9,
  pitch:  0,
  bearing: 0,
};
const NORTH_DFW_MOBILE = {
  // Bounding-box center of the three school pins (Mathews to Highland Park
  // span ~23km of latitude; centroid leaves Mathews's label clipped at the
  // top), zoomed wide enough that every pin's label fits with breathing
  // room on a ~190px-tall mobile map tile.
  center: [-96.760, 32.924],
  zoom:   8.0,
  pitch:  0,
  bearing: 0,
};

// ─── Scene-indexed overlay packages ───────────────────────────────────
// Each entry corresponds to one of the search scenes the left-column
// types out (SEARCH_SCENES in Landing.jsx, in order):
//   0  "Remote-friendly DFW neighborhood with parks"   →  3 zones
//   1  "Quiet brunch spots, patio, under 30 min from Plano"   →  3 spots
//   2  "Best public elementary schools with parks nearby"   →  3 schools
// The map flies to the matching camera and drops pins on the result
// places at the moment the search panel reveals its results, so the
// hero reads as one coherent demo of the product working end-to-end.
// Coordinates are evocative, not surveyor-grade.
const OVERLAYS = [
  // ─ scene 0 — neighborhoods + nearby trails. Trails are evocative
  // stylizations of well-known DFW corridors near each pin: White Rock
  // Lake (Lakewood), Trinity Skyline (south of Bishop Arts), Mandalay
  // Canal (Las Colinas).
  {
    camera: TRI_DFW,
    cameraMobile: TRI_DFW_MOBILE,
    pins: [
      { id: "lakewood",    lng: -96.745, lat: 32.831, title: "A · Lakewood",     primary: true,  color: HEX_AREA },
      { id: "bishop-arts", lng: -96.829, lat: 32.748, title: "B · Bishop Arts",  primary: false, color: HEX_AREA },
      { id: "las-colinas", lng: -96.953, lat: 32.881, title: "C · Las Colinas",  primary: false, color: HEX_AREA },
    ],
    trails: [
      {
        id: "white-rock", name: "White Rock Lake Trail",
        line: [
          [-96.730, 32.820], [-96.718, 32.832], [-96.716, 32.852],
          [-96.726, 32.866], [-96.742, 32.860], [-96.745, 32.840],
          [-96.738, 32.825], [-96.730, 32.820],
        ],
      },
      {
        id: "trinity-skyline", name: "Trinity Skyline Trail",
        line: [
          [-96.870, 32.732], [-96.848, 32.738], [-96.825, 32.745],
          [-96.802, 32.755], [-96.780, 32.770], [-96.762, 32.782],
        ],
      },
      {
        id: "mandalay", name: "Mandalay Canal Trail",
        line: [
          [-96.945, 32.872], [-96.948, 32.882], [-96.962, 32.890],
          [-96.972, 32.882], [-96.965, 32.870], [-96.952, 32.866],
          [-96.945, 32.872],
        ],
      },
    ],
  },

  // ─ scene 1 — Bishop Arts brunch (Hattie's / Boulevardier / Eno's)
  // The three spots are a few doors apart on Bishop Avenue; coords are
  // stylized so the pins separate visually at zoom 16.
  {
    camera: BISHOP_ARTS,
    pins: [
      { id: "hatties",     lng: -96.8298, lat: 32.7475, title: "A · Hattie's",     primary: true,  color: HEX_SIGNAL },
      { id: "boulevardier",lng: -96.8290, lat: 32.7470, title: "B · Boulevardier", primary: false, color: HEX_SIGNAL },
      { id: "enos",        lng: -96.8294, lat: 32.7466, title: "C · Eno's Pizza",  primary: false, color: HEX_SIGNAL },
    ],
  },

  // ─ scene 2 — schools + nearby parks (the "parks nearby" criterion
  // visualized). One park per school, evocative coords near each pin.
  {
    camera: NORTH_DFW,
    cameraMobile: NORTH_DFW_MOBILE,
    pins: [
      { id: "mathews",   lng: -96.715, lat: 33.027, title: "A · Mathews EL",     primary: true,  color: HEX_RANK },
      { id: "stonewall", lng: -96.748, lat: 32.842, title: "B · Stonewall",      primary: false, color: HEX_RANK },
      { id: "hp-el",     lng: -96.808, lat: 32.821, title: "C · Highland Park",  primary: false, color: HEX_RANK },
    ],
    parks: [
      { id: "bob-woodruff", name: "Bob Woodruff Park", lng: -96.706, lat: 33.024 },
      { id: "lindsley",     name: "Lindsley Park",     lng: -96.756, lat: 32.836 },
      { id: "hp-prather",   name: "Prather Park",      lng: -96.812, lat: 32.829 },
    ],
  },
];

function StreetsMap({ phase, idx = 0, mobile = false }) {
  const containerRef = useRef(null);
  const mapRef = useRef(null);
  const markersRef = useRef([]);
  const [ready, setReady] = useState(false);

  // Mount Mapbox once
  useEffect(() => {
    if (typeof mapboxgl === "undefined" || !containerRef.current) return;
    mapboxgl.accessToken = MAPBOX_TOKEN;
    const map = new mapboxgl.Map({
      container: containerRef.current,
      style: "mapbox://styles/mapbox/light-v11",
      center: DFW_WIDE.center,
      zoom:   DFW_WIDE.zoom,
      pitch:  DFW_WIDE.pitch,
      bearing: DFW_WIDE.bearing,
      // Attribution + logo land bottom-right; my status pill owns bottom-left.
      attributionControl: { compact: true },
      logoPosition: "bottom-right",
      interactive: false,    // decorative — page owns the gestures
      cooperativeGestures: false,
    });
    mapRef.current = map;

    map.on("load", () => {
      // Atlas paint pass — nudge Mapbox light-v11 toward the page palette
      // so the basemap reads as a continuation of Atlas Cream paper, not
      // a foreign tile. Land becomes paper, water tints toward warm-gray
      // teal, roads desaturate a notch. Wrapped in try/catch because the
      // exact layer ids in Mapbox's hosted styles can shift between
      // releases — failing soft means the map still loads, just less
      // bespoke.
      const setPaint = (id, prop, val) => {
        if (!map.getLayer(id)) return;
        try { map.setPaintProperty(id, prop, val); } catch (_) {}
      };
      // Land surfaces → Atlas Cream
      ["land", "landuse", "national-park"].forEach((id) => {
        setPaint(id, "background-color", "#f5f1e9");
        setPaint(id, "fill-color",       "#f5f1e9");
      });
      // Water → muted Cartographic Teal at low chroma
      setPaint("water",         "fill-color", "#dde6e4");
      setPaint("waterway",      "line-color", "#cdd9d6");
      // Roads → tinted gray (was bright white). The motorway casing is the
      // most visible network on a tinted basemap, so soften it most.
      setPaint("road-motorway-trunk",     "line-color", "#e8dfce");
      setPaint("road-motorway",           "line-color", "#efe6d4");
      setPaint("road-primary",            "line-color", "#ece4d2");
      setPaint("road-secondary-tertiary", "line-color", "#e8e0cd");
      setPaint("road-street",             "line-color", "#e6dec9");
      // Building fills → quiet Cream Layer
      setPaint("building", "fill-color", "#ece6d8");
      setPaint("building", "fill-opacity", 0.5);
      // Place labels: warm ink, slightly translucent so labels sit IN the
      // paper rather than ON it.
      ["settlement-major-label", "settlement-minor-label", "settlement-subdivision-label"].forEach((id) => {
        setPaint(id, "text-color", "#2b2620");
        setPaint(id, "text-halo-color", "rgba(245,241,233,0.85)");
      });

      // Empty sources we'll mutate in response to idx changes
      map.addSource("polygon-src",       { type: "geojson", data: emptyFC() });
      map.addSource("trails-src",        { type: "geojson", data: emptyFC() });
      map.addSource("trail-labels-src",  { type: "geojson", data: emptyFC() });
      map.addSource("annotation-src",    { type: "geojson", data: emptyFC() });
      map.addSource("parks-src",         { type: "geojson", data: emptyFC() });

      map.addLayer({
        id: "polygon-fill", type: "fill", source: "polygon-src",
        paint: { "fill-color": HEX_AREA, "fill-opacity": 0.07 },
      });
      map.addLayer({
        id: "polygon-outline", type: "line", source: "polygon-src",
        paint: {
          "line-color":     HEX_AREA,
          "line-width":     2,
          "line-dasharray": [2.5, 1.5],
          "line-opacity":   0.9,
        },
      });

      // Trails — context lines for "neighborhoods near trails." Quieter
      // than the curator-style trail render: thin, dashed, ash-ink, so
      // the primary neighborhood pins stay loudest.
      map.addLayer({
        id: "trails-line", type: "line", source: "trails-src",
        layout: { "line-cap": "round", "line-join": "round" },
        paint:  {
          "line-color":     "#6b6358",                // Ash Ink
          "line-width":     2.2,
          "line-dasharray": [1.5, 1.5],
          "line-opacity":   0.6,
        },
      });
      // Trail labels — rendered from a separate Point-feature source so
      // placement is reliable. (line-center on the LineString source
      // auto-hides when the rendered line isn't long enough to fit the
      // text, which was the case here at zoom 9.6.)
      map.addLayer({
        id: "trails-label", type: "symbol", source: "trail-labels-src",
        layout: {
          "text-field":            ["get", "name"],
          "text-font":             ["DIN Pro Medium", "Arial Unicode MS Regular"],
          "text-size":             10,
          "text-letter-spacing":   0.08,
          "text-anchor":           "top",
          "text-offset":           [0, 0.6],
          "text-allow-overlap":    true,
          "text-ignore-placement": true,
        },
        paint: {
          "text-color":      "#2b2620",               // Smoke Ink
          "text-halo-color": "rgba(245,241,233,0.92)",
          "text-halo-width": 1.6,
          "text-opacity":    0.95,
        },
      });

      // Parks — context dots for "schools with parks nearby." Soft
      // ash-ink filled circles with name labels above. They sit
      // visually behind the school pins (drawn before the HTML markers,
      // which always render above WebGL layers).
      map.addLayer({
        id: "parks-circle", type: "circle", source: "parks-src",
        paint: {
          "circle-radius":         8,
          "circle-color":          "#6b6358",         // Ash Ink
          "circle-opacity":        0.32,
          "circle-stroke-width":   1,
          "circle-stroke-color":   "#6b6358",
          "circle-stroke-opacity": 0.55,
        },
      });
      map.addLayer({
        id: "parks-label", type: "symbol", source: "parks-src",
        layout: {
          "text-field":       ["get", "name"],
          "text-font":        ["DIN Pro Medium", "Arial Unicode MS Regular"],
          "text-size":        10,
          "text-anchor":      "top",
          "text-offset":      [0, 0.9],
          "text-letter-spacing": 0.04,
          "text-allow-overlap": false,
        },
        paint: {
          "text-color":      "#2b2620",               // Smoke Ink
          "text-halo-color": "rgba(245,241,233,0.92)",
          "text-halo-width": 1.5,
          "text-opacity":    0.85,
        },
      });

      map.addLayer({
        id: "annotation-line", type: "line", source: "annotation-src",
        paint: {
          "line-color":     HEX_SIGNAL,
          "line-width":     1.6,
          "line-dasharray": [2, 2.5],
          "line-opacity":   0.7,
        },
      });

      setReady(true);
    });

    return () => {
      markersRef.current.forEach((m) => m.remove());
      markersRef.current = [];
      map.remove();
    };
  }, []);

  // Camera flight — drive from phase + idx. Honors prefers-reduced-motion:
  // when the user opts out of motion, snap with `jumpTo` instead of flying.
  useEffect(() => {
    if (!ready) return;
    const map = mapRef.current;
    if (!map) return;

    if (phase < 2) {
      // Hold the wide view while the search/results animation runs
      return;
    }
    const overlay = OVERLAYS[idx] || {};
    const cam = (mobile && overlay.cameraMobile) || overlay.camera || UPTOWN;
    if (prefersReducedMotion()) {
      map.jumpTo({
        center:  cam.center,
        zoom:    cam.zoom,
        pitch:   cam.pitch,
        bearing: cam.bearing,
      });
      return;
    }
    map.flyTo({
      center:  cam.center,
      zoom:    cam.zoom,
      pitch:   cam.pitch,
      bearing: cam.bearing,
      duration: 2200,
      curve:    1.4,
      essential: true,
    });
  }, [phase, idx, ready, mobile]);

  // Overlay swap — clear + re-add markers and source data each idx
  useEffect(() => {
    if (!ready || phase < 4) return;
    const map = mapRef.current;
    if (!map) return;
    const overlay = OVERLAYS[idx] || {};

    // Clear existing markers
    markersRef.current.forEach((m) => m.remove());
    markersRef.current = [];

    // HTML pins (primary places + secondary references)
    (overlay.pins || []).forEach((p) => {
      const el = makePinEl(p);
      const m = new mapboxgl.Marker({ element: el, anchor: "bottom" })
        .setLngLat([p.lng, p.lat])
        .addTo(map);
      markersRef.current.push(m);
    });

    // HTML chips (area signals)
    (overlay.chips || []).forEach((c) => {
      const el = makeChipEl(c);
      const m = new mapboxgl.Marker({ element: el, anchor: "center" })
        .setLngLat([c.lng, c.lat])
        .addTo(map);
      markersRef.current.push(m);
    });

    // Polygon (75201 outline)
    setSourceData(map, "polygon-src",
      overlay.polygon
        ? [{ type: "Feature", geometry: { type: "Polygon", coordinates: [overlay.polygon] } }]
        : [],
    );

    // Trail polylines (scene 0 — context for "near trails")
    setSourceData(map, "trails-src",
      (overlay.trails || []).map((t) => ({
        type: "Feature",
        properties: { name: t.name },
        geometry: { type: "LineString", coordinates: t.line },
      })),
    );

    // Trail labels — Point feature at each trail's centroid (mean of
    // vertices) so the symbol layer can place a single label reliably.
    setSourceData(map, "trail-labels-src",
      (overlay.trails || []).map((t) => {
        const n = t.line.length;
        const cx = t.line.reduce((s, c) => s + c[0], 0) / n;
        const cy = t.line.reduce((s, c) => s + c[1], 0) / n;
        return {
          type: "Feature",
          properties: { name: t.name },
          geometry: { type: "Point", coordinates: [cx, cy] },
        };
      }),
    );

    // Park dots (scene 2 — context for "with parks nearby")
    setSourceData(map, "parks-src",
      (overlay.parks || []).map((p) => ({
        type: "Feature",
        properties: { name: p.name },
        geometry: { type: "Point", coordinates: [p.lng, p.lat] },
      })),
    );

    // Walk-to annotation line
    setSourceData(map, "annotation-src",
      overlay.annotation
        ? [{ type: "Feature", geometry: { type: "LineString",
            coordinates: [overlay.annotation.from, overlay.annotation.to] } }]
        : [],
    );
  }, [idx, ready, phase]);

  return (
    <div style={{ position: "absolute", inset: 0 }}>
      <div ref={containerRef} style={{ position: "absolute", inset: 0 }}/>

      {/* Warm wash + vignette — kept from the SVG version for cohesion */}
      <div style={{
        position: "absolute", inset: 0, pointerEvents: "none",
        background: "linear-gradient(180deg, rgba(245,241,233,0.18) 0%, rgba(245,241,233,0.04) 50%, rgba(245,241,233,0.22) 100%)",
      }}/>
      <div style={{
        position: "absolute", inset: 0, pointerEvents: "none",
        background: "radial-gradient(ellipse at center, transparent 55%, rgba(20,17,13,0.18) 100%)",
      }}/>
    </div>
  );
}

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

function setSourceData(map, id, features) {
  const src = map.getSource(id);
  if (src) src.setData({ type: "FeatureCollection", features });
}

function makePinEl({ title, primary, color }) {
  const el = document.createElement("div");
  el.style.cssText = "display:flex;flex-direction:column;align-items:center;pointer-events:none;transform:translateY(0);";
  el.innerHTML = `
    <div style="
      padding: 4px 10px; border-radius: 999px;
      background: ${primary ? color : "rgba(245,241,233,0.96)"};
      color: ${primary ? "#fdf8ed" : "#14110d"};
      border: 1px solid ${primary ? "transparent" : "rgba(20,17,13,0.16)"};
      font: 600 11.5px 'Geist', system-ui;
      letter-spacing: -0.01em;
      box-shadow: 0 4px 12px -3px rgba(20,17,13,0.28);
      white-space: nowrap; margin-bottom: 4px;
      backdrop-filter: blur(6px);
    ">${escape(title)}</div>
    <div style="
      width: 14px; height: 14px;
      border-radius: 50% 50% 50% 3px;
      background: ${color};
      transform: rotate(-45deg);
      box-shadow: 0 2px 6px -1px rgba(20,17,13,0.35);
      border: 2px solid #fdf8ed;
    "></div>
  `;
  return el;
}

function makeChipEl({ label, value }) {
  const el = document.createElement("div");
  el.style.cssText = "pointer-events:none;";
  el.innerHTML = `
    <div style="
      padding: 5px 9px; border-radius: 8px;
      background: rgba(245,241,233,0.96);
      backdrop-filter: blur(6px);
      border: 1px solid rgba(31,138,133,0.45);
      box-shadow: 0 4px 10px -3px rgba(20,17,13,0.22);
      font: 500 10.5px 'Geist', system-ui;
      letter-spacing: -0.005em;
      color: #2b2620;
      white-space: nowrap;
      display: flex; align-items: center; gap: 6px;
    ">
      <span style="
        font-family: 'Geist Mono', ui-monospace, monospace;
        font-size: 8.5px;
        color: #1f8a85;
        font-weight: 600;
        letter-spacing: 0.12em;
      ">${escape(label)}</span>
      <span style="font-weight: 600;">${escape(value)}</span>
    </div>
  `;
  return el;
}

function escape(s) {
  return String(s).replace(/[&<>"']/g, (c) => ({
    "&": "&amp;", "<": "&lt;", ">": "&gt;", "\"": "&quot;", "'": "&#39;",
  }[c]));
}

window.StreetsMap = StreetsMap;
