/* Flight Planner — drone mission planning (Area / Waypoint)
   Based on POC: map-draw. Integrated into SkyOps MIC workflow.

   Modes:
     - AREA: polygon of interest + lawnmower/boustrophedon generation
     - WAYPOINT: manual point-to-point route + per-waypoint camera actions

   The "map" is a hand-drawn SVG view of a real plot (Évora Plana PV),
   not Mapbox — this is a design prototype. Coordinate math uses a fake
   metric grid (1 unit = 1 m) anchored to the AOI bounding box.
*/

const { useState: useStateFP, useEffect: useEffectFP, useMemo: useMemoFP, useRef: useRefFP, useCallback: useCallbackFP } = React;

/* =================================================================
   DATA — site grounding
   The AOI corresponds to the Évora Plana PV plant (same as SitePage).
   Coordinate system: a synthetic Cartesian grid in METERS anchored to
   the SVG viewBox (0..VIEW_W, 0..VIEW_H). 1 unit = 1 m.
================================================================= */
const VIEW_W = 1000, VIEW_H = 560;

// Plant polygon (hull of the PV arrays) — roughly rectangular with chamfers
const AOI_POLYGON = [
  [70, 90], [520, 70], [740, 115], [900, 160],
  [910, 430], [780, 490], [300, 500], [85, 470], [55, 260]
];

// PV array rectangles (rows of panels) — visual context
const PV_ARRAYS = [
  { x: 120, y: 120, w: 170, h: 28, id: "A01" },
  { x: 120, y: 155, w: 170, h: 28, id: "A02" },
  { x: 120, y: 190, w: 170, h: 28, id: "A03" },
  { x: 120, y: 225, w: 170, h: 28, id: "A04" },
  { x: 120, y: 260, w: 170, h: 28, id: "A05" },
  { x: 120, y: 295, w: 170, h: 28, id: "A06" },

  { x: 330, y: 120, w: 210, h: 28, id: "B01" },
  { x: 330, y: 155, w: 210, h: 28, id: "B02" },
  { x: 330, y: 190, w: 210, h: 28, id: "B03" },
  { x: 330, y: 225, w: 210, h: 28, id: "B04" },
  { x: 330, y: 260, w: 210, h: 28, id: "B05" },
  { x: 330, y: 295, w: 210, h: 28, id: "B06" },
  { x: 330, y: 330, w: 210, h: 28, id: "B07" },
  { x: 330, y: 365, w: 210, h: 28, id: "B08" },

  { x: 580, y: 140, w: 180, h: 28, id: "C01" },
  { x: 580, y: 175, w: 180, h: 28, id: "C02" },
  { x: 580, y: 210, w: 180, h: 28, id: "C03" },
  { x: 580, y: 245, w: 180, h: 28, id: "C04" },
  { x: 580, y: 280, w: 180, h: 28, id: "C05" },
  { x: 580, y: 315, w: 180, h: 28, id: "C06" },

  { x: 780, y: 200, w: 120, h: 28, id: "D01" },
  { x: 780, y: 235, w: 120, h: 28, id: "D02" },
  { x: 780, y: 270, w: 120, h: 28, id: "D03" },
  { x: 780, y: 305, w: 120, h: 28, id: "D04" },
  { x: 780, y: 340, w: 120, h: 28, id: "D05" },

  { x: 150, y: 370, w: 150, h: 28, id: "E01" },
  { x: 150, y: 405, w: 150, h: 28, id: "E02" },
  { x: 150, y: 440, w: 150, h: 28, id: "E03" },
];

// Priority zones (inherited from the last MIC) — feed AOI editor
const PRIORITY_ZONES = [
  { id: "Z1", name: "Inverter 3 — hot-cells", rect: [120, 110, 175, 120], color: "amber" },
  { id: "Z2", name: "Combiner 4.2 — offset strings", rect: [585, 280, 175, 50], color: "amber" },
  { id: "Z3", name: "Re-fly gap · MIC-2026-01", rect: [770, 190, 135, 80], color: "red", hatch: true },
];

// Inherited home/takeoff reference from the last cycle
const TAKEOFF_DEFAULT = [960, 520];

// Seed waypoint route (for quick demo / Waypoint mode default)
const SEED_WAYPOINTS = [
  { lat: 38.5731, lng: -7.9154, xy: [120, 130], alt: null, actions: [] },
  { lat: 38.5733, lng: -7.9137, xy: [290, 130], alt: null, actions: [{ id:"a1", type:"take_photo_fixed", params:{ angle:-60 }}] },
  { lat: 38.5734, lng: -7.9117, xy: [540, 130], alt: 80,   actions: [{ id:"a2", type:"hover", params:{ durationSec: 8 }},{ id:"a3", type:"take_photo", params:{}}] },
  { lat: 38.5728, lng: -7.9100, xy: [760, 210], alt: null, actions: [{ id:"a4", type:"start_timed_interval", params:{ intervalSec: 3 }}] },
  { lat: 38.5720, lng: -7.9101, xy: [780, 340], alt: null, actions: [] },
  { lat: 38.5721, lng: -7.9120, xy: [580, 340], alt: null, actions: [{ id:"a5", type:"end_interval", params:{} }] },
  { lat: 38.5724, lng: -7.9142, xy: [330, 350], alt: null, actions: [{ id:"a6", type:"gimbal_tilt", params:{ angle: -45 }},{ id:"a7", type:"take_photo", params:{} }] },
  { lat: 38.5727, lng: -7.9154, xy: [150, 400], alt: null, actions: [] },
];

// Default area polygon (the full AOI)
const SEED_AREA = [[95, 110], [895, 95], [895, 470], [95, 460]];

/* =================================================================
   GEOMETRY HELPERS
================================================================= */
const DEG = Math.PI / 180;

function centroid(pts) {
  const n = pts.length;
  let cx = 0, cy = 0;
  for (const [x, y] of pts) { cx += x; cy += y; }
  return [cx / n, cy / n];
}

function rotatePoint([x, y], [cx, cy], rad) {
  const dx = x - cx, dy = y - cy;
  return [cx + dx * Math.cos(rad) - dy * Math.sin(rad),
          cy + dx * Math.sin(rad) + dy * Math.cos(rad)];
}

function polygonArea(pts) {
  let s = 0;
  for (let i = 0; i < pts.length; i++) {
    const [x1, y1] = pts[i];
    const [x2, y2] = pts[(i + 1) % pts.length];
    s += x1 * y2 - x2 * y1;
  }
  return Math.abs(s / 2);
}

function polygonBBox(pts) {
  let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
  for (const [x, y] of pts) {
    if (x < x0) x0 = x; if (y < y0) y0 = y;
    if (x > x1) x1 = x; if (y > y1) y1 = y;
  }
  return [x0, y0, x1, y1];
}

function pointInPoly([x, y], poly) {
  let inside = false;
  for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
    const [xi, yi] = poly[i], [xj, yj] = poly[j];
    const hit = ((yi > y) !== (yj > y)) &&
      (x < ((xj - xi) * (y - yi)) / (yj - yi + 1e-12) + xi);
    if (hit) inside = !inside;
  }
  return inside;
}

function dist(a, b) {
  const dx = a[0] - b[0], dy = a[1] - b[1];
  return Math.sqrt(dx * dx + dy * dy);
}

/* ---- Lawnmower (boustrophedon) — simplified port of src/lib/lawnmower.js ----
   1. Rotate poly by -angle around its centroid
   2. Scan-line at each spacing along X, find Y intersections with rotated poly edges
   3. Pair intersections, create passes; alternate direction (boustrophedon)
   4. Optional bezier corner rounding between passes
   5. Rotate all points back by +angle
*/
function lawnmower(poly, spacing, angleDeg, { curved = true, fillGrid = false } = {}) {
  if (!poly || poly.length < 3 || spacing <= 0) return { path: [], waypoints: [] };

  const c = centroid(poly);
  const rad = (angleDeg || 0) * DEG;

  const build = (ang) => {
    const r = ang * DEG;
    // Rotate to scan-line friendly frame (passes become vertical)
    const rp = poly.map(p => rotatePoint(p, c, -r));
    const [x0, y0, x1, y1] = polygonBBox(rp);

    const passes = [];
    let flip = false;
    for (let x = x0 + spacing / 2; x < x1; x += spacing) {
      // Find Y intersections at this X with poly edges
      const ys = [];
      for (let i = 0; i < rp.length; i++) {
        const [ax, ay] = rp[i];
        const [bx, by] = rp[(i + 1) % rp.length];
        if ((ax <= x && bx > x) || (bx <= x && ax > x)) {
          const t = (x - ax) / (bx - ax);
          ys.push(ay + t * (by - ay));
        }
      }
      ys.sort((a, b) => a - b);
      // Pair up (enter / exit) — odd counts = tangent; skip
      for (let k = 0; k + 1 < ys.length; k += 2) {
        const y0p = ys[k], y1p = ys[k + 1];
        const strip = flip ? [[x, y1p], [x, y0p]] : [[x, y0p], [x, y1p]];
        passes.push(strip);
      }
      flip = !flip;
    }

    // Flatten with bezier transitions
    const path = [];
    const waypoints = [];
    for (let i = 0; i < passes.length; i++) {
      const [a, b] = passes[i];
      waypoints.push(a, b);
      if (i === 0) {
        path.push(a, b);
      } else {
        const last = path[path.length - 1];
        if (curved) {
          // Cubic bezier with 8 samples
          const midX = (last[0] + a[0]) / 2;
          const ctrl1 = [last[0] + (midX - last[0]) * 0.6, last[1]];
          const ctrl2 = [a[0] - (a[0] - midX) * 0.6, a[1]];
          for (let t = 0; t <= 1; t += 1 / 8) {
            const mt = 1 - t;
            const px = mt*mt*mt*last[0] + 3*mt*mt*t*ctrl1[0] + 3*mt*t*t*ctrl2[0] + t*t*t*a[0];
            const py = mt*mt*mt*last[1] + 3*mt*mt*t*ctrl1[1] + 3*mt*t*t*ctrl2[1] + t*t*t*a[1];
            path.push([px, py]);
          }
        } else {
          path.push(a);
        }
        path.push(b);
      }
    }

    // Rotate back
    return {
      path: path.map(p => rotatePoint(p, c, r)),
      waypoints: waypoints.map(p => rotatePoint(p, c, r)),
    };
  };

  const first = build(angleDeg);
  if (!fillGrid) return first;
  const second = build(angleDeg + 90);
  return {
    path: [...first.path, ...second.path],
    waypoints: [...first.waypoints, ...second.waypoints],
  };
}

/* =================================================================
   ACTIONS — waypoint action catalog
================================================================= */
const ACTION_CATALOG = [
  { group: "Camera", items: [
    { type: "take_photo",        label: "Take photo",           defaults: {} },
    { type: "take_photo_fixed",  label: "Take photo (angle)",   defaults: { angle: -45 } },
    { type: "set_fov",           label: "Set FOV",              defaults: { fov: 84 } },
    { type: "camera_zoom",       label: "Camera zoom",          defaults: { level: 1 } },
  ]},
  { group: "Recording", items: [
    { type: "start_recording",   label: "Start recording",      defaults: {} },
    { type: "stop_recording",    label: "Stop recording",       defaults: {} },
  ]},
  { group: "Interval", items: [
    { type: "start_timed_interval",    label: "Start timed interval",    defaults: { intervalSec: 5 } },
    { type: "start_distance_interval", label: "Start distance interval", defaults: { intervalM: 50 } },
    { type: "end_interval",            label: "End interval",            defaults: {} },
  ]},
  { group: "Flight", items: [
    { type: "hover",             label: "Hover",              defaults: { durationSec: 5 } },
    { type: "aircraft_yaw",      label: "Aircraft yaw",       defaults: { angle: 0, relative: true } },
    { type: "gimbal_tilt",       label: "Gimbal tilt",        defaults: { angle: -45 } },
  ]},
];

const ACTION_LABEL = {
  take_photo: "Take photo",
  take_photo_fixed: "Take photo @ {angle}°",
  set_fov: "Set FOV {fov}°",
  camera_zoom: "Zoom {level}x",
  start_recording: "Start recording",
  stop_recording: "Stop recording",
  start_timed_interval: "Timed interval {intervalSec}s",
  start_distance_interval: "Distance interval {intervalM}m",
  end_interval: "End interval",
  hover: "Hover {durationSec}s",
  aircraft_yaw: "Yaw {angle}°",
  gimbal_tilt: "Gimbal {angle}°",
};

function actionSummary(a) {
  const tpl = ACTION_LABEL[a.type] || a.type;
  return tpl.replace(/\{(\w+)\}/g, (_, k) => a.params?.[k] ?? "–");
}

/* =================================================================
   MAIN COMPONENT
================================================================= */
function FlightPlannerPage({ go, mic, site, setStage }) {
  site = site || (window.SITES || []).find(s => s.id === localStorage.getItem("skyops_site")) || {};
  const prof = window.siteProfile && site.id ? window.siteProfile(site) : null;
  const [, forceFP] = useStateFP(0);
  const flown = mic && (mic.progress?.flying ?? 0) > 0;
  const commitFlight = () => {
    if (!mic) return go("review");
    mic.progress = { ...(mic.progress || {}), flying: Math.max(0.1, mic.progress?.flying || 0) };
    mic.status = "flying"; mic.startedAt = mic.startedAt || mic.plannedFor || new Date().toISOString().slice(0, 10);
    window.saveMicState && window.saveMicState(mic);
    forceFP(x => x + 1);
    setStage ? setStage("capture") : go("workspace");
  };
  const [mode, setMode] = useStateFP(() => localStorage.getItem("skyops_fp_mode") || "area");
  // Area mode state
  const [areaPoly, setAreaPoly]       = useStateFP(SEED_AREA);
  const [spacing, setSpacing]         = useStateFP(22);          // meters between passes
  const [angle, setAngle]             = useStateFP(0);           // pass direction
  const [altMode, setAltMode]         = useStateFP("AGL");
  const [altitude, setAltitude]       = useStateFP(60);
  const [speed, setSpeed]             = useStateFP(6);
  const [curved, setCurved]           = useStateFP(true);
  const [fillGrid, setFillGrid]       = useStateFP(false);
  const [showPositions, setShowPositions] = useStateFP(true);
  const [showCoverage, setShowCoverage]   = useStateFP(false);
  const [showHeatmap, setShowHeatmap]     = useStateFP(false);
  const [heatmapMode, setHeatmapMode]     = useStateFP("overlap"); // overlap | gsd
  const [gsd, setGsd]                 = useStateFP(3.0);
  const [fov, setFov]                 = useStateFP(84);
  const [photoMode, setPhotoMode]     = useStateFP("ortho"); // ortho | oblique
  const [gimbalAngle, setGimbalAngle] = useStateFP(-60);
  const [frontOverlap, setFrontOverlap] = useStateFP(75);

  // Waypoint mode state
  const [waypoints, setWaypoints]     = useStateFP(SEED_WAYPOINTS);
  const [wpSelected, setWpSelected]   = useStateFP(null);
  const [wpSpeed, setWpSpeed]         = useStateFP(10);
  const [wpAltitude, setWpAltitude]   = useStateFP(100);
  const [wpAltMode, setWpAltMode]     = useStateFP("AGL");
  const [wpPath, setWpPath]           = useStateFP("rounded"); // straight | rounded | curved
  const [wpShowDistances, setWpShowDistances] = useStateFP(false);
  const [droneOverlay, setDroneOverlay] = useStateFP(true);

  // Takeoff
  const [takeoff, setTakeoff]         = useStateFP(TAKEOFF_DEFAULT);
  const [takeoffSafeAlt, setTakeoffSafeAlt] = useStateFP(20);
  const [takeoffMode, setTakeoffMode] = useStateFP("safe"); // safe | direct

  // Map interaction
  const [drawingArea, setDrawingArea] = useStateFP(false);
  const [drawingWp, setDrawingWp]     = useStateFP(false);
  const [draftPoly, setDraftPoly]     = useStateFP([]);
  const [cursor, setCursor]           = useStateFP(null);
  const [settingTakeoff, setSettingTakeoff] = useStateFP(false);

  useEffectFP(() => { localStorage.setItem("skyops_fp_mode", mode); }, [mode]);

  /* Generate lawnmower whenever area/params change */
  const lawn = useMemoFP(() =>
    lawnmower(areaPoly, spacing, angle, { curved, fillGrid }),
    [areaPoly, spacing, angle, curved, fillGrid]);

  /* Metrics */
  const metrics = useMemoFP(() => {
    if (mode === "area") {
      const drawn = polygonArea(areaPoly);
      // Drawn polygon is a demo footprint; scale route stats to the area committed in Plan
      const targetM2 = (mic && mic.area_ha ? mic.area_ha : prof ? prof.area : drawn / 1e4) * 1e4;
      const k = drawn > 0 ? targetM2 / drawn : 1;
      let d = 0;
      for (let i = 1; i < lawn.path.length; i++) d += dist(lawn.path[i-1], lawn.path[i]);
      d *= k;
      const time = d / speed;
      return {
        area: targetM2,
        distance: d,
        time,
        count: Math.round(lawn.waypoints.length * Math.sqrt(k)),
        photos: Math.ceil(d / Math.max(1, 2 * altitude * Math.tan((fov/2) * DEG) * 0.75 * (1 - frontOverlap/100))),
      };
    } else {
      let d = 0;
      for (let i = 1; i < waypoints.length; i++) d += dist(waypoints[i-1].xy, waypoints[i].xy);
      const time = d / wpSpeed;
      const photos = waypoints.reduce((n, w) => n + (w.actions?.filter(a => a.type.startsWith("take_photo")).length || 0), 0);
      return { area: 0, distance: d, time, count: waypoints.length, photos };
    }
  }, [mode, areaPoly, lawn, speed, waypoints, wpSpeed, altitude, fov, frontOverlap, mic && mic.area_ha, prof && prof.area]);

  /* Handlers */
  const onMapClick = (pt) => {
    if (settingTakeoff) { setTakeoff(pt); setSettingTakeoff(false); return; }
    if (drawingArea) {
      setDraftPoly(d => [...d, pt]);
      return;
    }
    if (drawingWp) {
      const newWp = {
        lat: 38.57 + pt[1] * 1e-5, lng: -7.92 + pt[0] * 1e-5,
        xy: pt, alt: null, actions: [],
      };
      setWaypoints(w => [...w, newWp]);
      setWpSelected(waypoints.length);
      return;
    }
    // click in empty space — deselect
    setWpSelected(null);
  };

  const finishAreaDraw = () => {
    if (draftPoly.length >= 3) setAreaPoly(draftPoly);
    setDraftPoly([]);
    setDrawingArea(false);
  };

  const finishWpDraw = () => { setDrawingWp(false); };

  const clearMission = () => {
    if (mode === "area") { setAreaPoly([]); setDraftPoly([]); }
    else { setWaypoints([]); setWpSelected(null); }
  };

  const selectedWp = wpSelected != null ? waypoints[wpSelected] : null;

  const updateWp = (idx, patch) => {
    setWaypoints(ws => ws.map((w, i) => i === idx ? { ...w, ...patch } : w));
  };

  const addActionToWp = (idx, type, defaults) => {
    const id = Math.random().toString(36).slice(2, 10);
    setWaypoints(ws => ws.map((w, i) =>
      i === idx ? { ...w, actions: [...w.actions, { id, type, params: { ...defaults } }] } : w));
  };
  const removeAction = (wpIdx, actId) => {
    setWaypoints(ws => ws.map((w, i) =>
      i === wpIdx ? { ...w, actions: w.actions.filter(a => a.id !== actId) } : w));
  };
  const updateAction = (wpIdx, actId, paramsPatch) => {
    setWaypoints(ws => ws.map((w, i) =>
      i === wpIdx ? { ...w, actions: w.actions.map(a => a.id === actId ? { ...a, params: { ...a.params, ...paramsPatch }} : a) } : w));
  };
  const moveAction = (wpIdx, actId, dir) => {
    setWaypoints(ws => ws.map((w, i) => {
      if (i !== wpIdx) return w;
      const arr = [...w.actions];
      const pos = arr.findIndex(a => a.id === actId);
      if (pos < 0) return w;
      const np = pos + dir;
      if (np < 0 || np >= arr.length) return w;
      [arr[pos], arr[np]] = [arr[np], arr[pos]];
      return { ...w, actions: arr };
    }));
  };
  const removeWp = (idx) => {
    setWaypoints(ws => ws.filter((_, i) => i !== idx));
    setWpSelected(null);
  };

  return (
    <div className="page fp">
      <div className="page-head">
        <div className="title">
          <div className="kicker">{mic ? mic.id : "MIC"} · Collection plan</div>
          <h1>Flight planner</h1>
        </div>
        <div className="actions">
          <button className="btn btn-ghost" onClick={() => go("planner")}>
            <Icon name="chevron_right" style={{ transform: "rotate(180deg)" }}/>Back to MIC setup
          </button>
          <button className="btn btn-secondary" onClick={() => { const c = window.fpCoords(mode === "waypoint" ? null : areaPoly, mode === "waypoint" ? waypoints : null, prof); window.skyDownload(`${mic ? mic.id : "flight"}-aoi.kml`, window.fpKml(`${site.name || "Site"} · ${mic ? mic.id : "AOI"}`, c, altitude), "application/vnd.google-earth.kml+xml"); window.skyToast("AOI exported as KML"); }}><Icon name="download" />Export KML</button>
          <button className="btn btn-secondary" onClick={() => { const c = window.fpCoords(mode === "waypoint" ? null : areaPoly, mode === "waypoint" ? waypoints : null, prof); window.skyDownload(`${mic ? mic.id : "flight"}-waylines.wpml`, window.fpWpml(mic ? mic.id : "flight", c, mode === "waypoint" ? wpAltitude : altitude, mode === "waypoint" ? wpSpeed : speed), "application/xml"); window.skyToast("DJI WPML exported · import in DJI Pilot 2"); }}><Icon name="download" />Export DJI WPML</button>
          {flown ? <button className="btn btn-primary" onClick={() => setStage ? setStage("capture") : go("workspace")}><Icon name="upload" />Go to capture</button>
            : <button className="btn btn-primary" onClick={commitFlight}><Icon name="send" />Send to pilot & start flight</button>}
        </div>
      </div>
      <div className="fp-ctx">
            <div className="fp-ctx-it"><span className="caps">Site</span><b>{site.name}</b><em className="mono">{(site.id || "").toUpperCase()}</em></div>
            <div className="fp-ctx-it"><span className="caps">Zones</span><div className="fp-ctx-z">{(mic && mic.zones ? mic.zones : prof ? prof.zones.map(z => z.id) : []).map((z, i) => { const zz = prof && prof.zones.find(x => x.id === z); return <span key={z} className="mono" style={{ "--zc": `hsl(${zz ? zz.hue : i * 47},70%,62%)` }}>{z}</span>; })}</div></div>
            <div className="fp-ctx-it"><span className="caps">Area</span><b className="mono">{mic && mic.area_ha ? mic.area_ha : prof ? prof.area.toFixed(1) : "—"} ha</b></div>
            {mic && mic.drone && (() => { const [id, ...rest] = mic.drone.split(" · "); return <div className="fp-ctx-it"><span className="caps">Drone</span><b className="mono">{id}</b><em>{rest.join(" · ")}</em></div>; })()}
            {mic && mic.lead && <div className="fp-ctx-it"><span className="caps">Pilot</span><b>{mic.lead}</b></div>}
            {prof && <div className="fp-ctx-it"><span className="caps">Location</span><b className="mono">{prof.lat}, {prof.lng}</b><em className="mono">{prof.crs.split(" · ")[0]}</em></div>}
          </div>

      {/* Mode switcher */}
      <div className="fp-mode-bar">
        <button className={`fp-mode ${mode==="area"?"on":""}`} onClick={() => setMode("area")}>
          <span className="fp-mode-ic" data-mode="area">
            <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="1.8">
              <path d="M3 3h18v18H3z"/><path d="M3 9h18M3 15h18M7 9v6M13 9v6M19 9v6"/>
            </svg>
          </span>
          <div>
            <div className="fp-mode-tt">Area survey</div>
            <div className="fp-mode-st">Polygon · auto lawnmower · photogrammetry</div>
          </div>
        </button>
        <button className={`fp-mode ${mode==="waypoint"?"on":""}`} onClick={() => setMode("waypoint")}>
          <span className="fp-mode-ic" data-mode="waypoint">
            <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="1.8">
              <circle cx="5" cy="7" r="2"/><circle cx="19" cy="17" r="2"/><circle cx="12" cy="5" r="2"/><circle cx="12" cy="19" r="2"/>
              <path d="m7 7 5-2 0 0M14 5l4 10M17 17l-4 2M10 19 7 9"/>
            </svg>
          </span>
          <div>
            <div className="fp-mode-tt">Waypoint route</div>
            <div className="fp-mode-st">Manual points · per-WP actions · DJI WPML</div>
          </div>
        </button>
        <div style={{ flex: 1 }}/>
        {mic && mic.baseline ? <div className="fp-baseline">
          <Icon name="history" size={12}/>
          <span>Inherited from <b className="mono">{mic.baseline}</b></span>
          <span className="pill"><span className="dot"/>{mic.zones ? mic.zones.length + " zones" : "all zones"} · {mic.area_ha || (prof && prof.area.toFixed(1))} ha</span>
        </div> : <div className="fp-baseline"><Icon name="history" size={12}/><span>First cycle · no baseline</span></div>}
      </div>

      {/* 3-col: toolbar · map · sidebar */}
      <div className="fp-grid">
        <FPToolbar
          mode={mode}
          drawingArea={drawingArea} drawingWp={drawingWp} settingTakeoff={settingTakeoff}
          onDrawArea={() => { setDrawingArea(v => !v); setDraftPoly([]); setDrawingWp(false); setSettingTakeoff(false); }}
          onDrawWp={() => { setDrawingWp(v => !v); setDrawingArea(false); setSettingTakeoff(false); }}
          onFinish={() => { mode==="area" ? finishAreaDraw() : finishWpDraw(); }}
          onSetTakeoff={() => { setSettingTakeoff(v => !v); setDrawingArea(false); setDrawingWp(false); }}
          onClear={clearMission}
        />

        <FPMap
          mode={mode}
          areaPoly={areaPoly} lawn={lawn}
          waypoints={waypoints} wpSelected={wpSelected} onSelectWp={setWpSelected}
          takeoff={takeoff}
          drawingArea={drawingArea} drawingWp={drawingWp}
          draftPoly={draftPoly} cursor={cursor} setCursor={setCursor}
          onMapClick={onMapClick}
          onDoubleClick={() => { mode === "area" ? finishAreaDraw() : finishWpDraw(); }}
          showPositions={showPositions} showCoverage={showCoverage} showHeatmap={showHeatmap}
          heatmapMode={heatmapMode} fov={fov} altitude={altitude}
          wpPath={wpPath} wpShowDistances={wpShowDistances}
          droneOverlay={droneOverlay} gimbalAngle={gimbalAngle} photoMode={photoMode}
          settingTakeoff={settingTakeoff}
          metrics={metrics}
        />

        <FPSidebar
          mode={mode} metrics={metrics}
          // area
          spacing={spacing} setSpacing={setSpacing}
          angle={angle} setAngle={setAngle}
          altMode={altMode} setAltMode={setAltMode}
          altitude={altitude} setAltitude={setAltitude}
          speed={speed} setSpeed={setSpeed}
          curved={curved} setCurved={setCurved}
          fillGrid={fillGrid} setFillGrid={setFillGrid}
          showPositions={showPositions} setShowPositions={setShowPositions}
          showCoverage={showCoverage} setShowCoverage={setShowCoverage}
          showHeatmap={showHeatmap} setShowHeatmap={setShowHeatmap}
          heatmapMode={heatmapMode} setHeatmapMode={setHeatmapMode}
          gsd={gsd} setGsd={setGsd}
          fov={fov} setFov={setFov}
          photoMode={photoMode} setPhotoMode={setPhotoMode}
          gimbalAngle={gimbalAngle} setGimbalAngle={setGimbalAngle}
          frontOverlap={frontOverlap} setFrontOverlap={setFrontOverlap}
          // waypoint
          waypoints={waypoints} wpSelected={wpSelected} setWpSelected={setWpSelected}
          updateWp={updateWp}
          removeWp={removeWp}
          addActionToWp={addActionToWp} removeAction={removeAction}
          updateAction={updateAction} moveAction={moveAction}
          wpSpeed={wpSpeed} setWpSpeed={setWpSpeed}
          wpAltitude={wpAltitude} setWpAltitude={setWpAltitude}
          wpAltMode={wpAltMode} setWpAltMode={setWpAltMode}
          wpPath={wpPath} setWpPath={setWpPath}
          wpShowDistances={wpShowDistances} setWpShowDistances={setWpShowDistances}
          droneOverlay={droneOverlay} setDroneOverlay={setDroneOverlay}
          // takeoff
          takeoff={takeoff} setTakeoff={setTakeoff}
          takeoffSafeAlt={takeoffSafeAlt} setTakeoffSafeAlt={setTakeoffSafeAlt}
          takeoffMode={takeoffMode} setTakeoffMode={setTakeoffMode}
        />
      </div>
    </div>
  );
}

window.FlightPlannerPage = FlightPlannerPage;
window.FP_DATA = { VIEW_W, VIEW_H, AOI_POLYGON, PV_ARRAYS, PRIORITY_ZONES };
window.FP_HELPERS = { dist, pointInPoly, lawnmower, polygonArea, centroid, rotatePoint, actionSummary, ACTION_CATALOG, DEG };
