// IngestionControl component - Ingestion Control Tab with Gap Filling

// ─── Constants ──────────────────────────────────────────────────────────────
const CHART_W = 490, CHART_H = 160;
const CHART_PAD = { top: 10, right: 72, bottom: 28, left: 40 };
const J_MILES_AXIS_SCALE      = 10000;
const Y_MILES_AXIS_SCALE      = 5000;
const DEFAULT_MAX_J_MILES     = 100000;
const DEFAULT_MAX_Y_MILES     = 50000;
const TOOLTIP_WIDTH_PX        = 514;
const TOOLTIP_Y_OFFSET        = 250;
const TOOLTIP_HOVER_DELAY_MS  = 300;
const WINDOW_EDGE_PAD         = 8;
const COVERAGE_SUCCESS_PCT    = 98;
const COVERAGE_WARNING_PCT    = 90;
const COVERAGE_GOOD_PCT       = 90;
const COVERAGE_FAIR_PCT       = 50;
const DEFAULT_GAP_FILL_BUDGET = 50;
const Z_INGESTION_TOOLTIP     = 9999;
const RELEASE_WINDOW_THRESHOLD_DAYS = 15;

// --- Distribution Chart with independent cabin pricing scales (pure SVG) ---
// releaseDays: optional number — draws a vertical release-date line
// releaseDate: optional string — actual calendar date for the release line label
// todayDate: optional string — today's date for a "Today" line
function DistributionChart({ data, stats, theme, releaseDays, releaseDate, todayDate }) {
  const W = CHART_W, H = CHART_H, PAD = CHART_PAD;
  const chartW = W - PAD.left - PAD.right;
  const chartH = H - PAD.top - PAD.bottom;
  const dist = data || [];
  const maxVal = (stats && stats.max_flights_per_day) || 1;

  const hasJ = dist.some(d => d.avg_j > 0);
  const hasY = dist.some(d => d.avg_y > 0);
  let maxJ = 0, maxY = 0;
  dist.forEach(d => {
    if (d.avg_j > 0 && d.avg_j > maxJ) maxJ = d.avg_j;
    if (d.avg_y > 0 && d.avg_y > maxY) maxY = d.avg_y;
  });
  if (maxJ > 0) maxJ = Math.ceil(maxJ * 1.1 / J_MILES_AXIS_SCALE) * J_MILES_AXIS_SCALE;
  if (maxY > 0) maxY = Math.ceil(maxY * 1.1 / Y_MILES_AXIS_SCALE) * Y_MILES_AXIS_SCALE;
  if (maxJ === 0) maxJ = DEFAULT_MAX_J_MILES;
  if (maxY === 0) maxY = DEFAULT_MAX_Y_MILES;

  const months = [];
  let lastMonth = -1;
  dist.forEach((d, i) => {
    const m = new Date(d.date + "T00:00:00").getMonth();
    if (m !== lastMonth) {
      months.push({ idx: i, label: new Date(d.date + "T00:00:00").toLocaleString("en-US", { month: "short" }) });
      lastMonth = m;
    }
  });

  const barW = dist.length > 0 ? chartW / dist.length : 1;

  const buildLine = (key, max) => {
    const points = [];
    dist.forEach((d, i) => {
      if (d[key] > 0) {
        const x = PAD.left + i * barW + barW / 2;
        const y = PAD.top + chartH - (d[key] / max) * chartH;
        points.push(`${x},${y}`);
      }
    });
    return points.join(" ");
  };

  const jLine = hasJ ? buildLine("avg_j", maxJ) : "";
  const yLine = hasY ? buildLine("avg_y", maxY) : "";

  const fmtMiles = (v) => v >= 1000 ? (v / 1000).toFixed(0) + "k" : v;

  const children = [
    React.createElement("rect", { key: "bg", x: 0, y: 0, width: W, height: H, rx: 6, fill: "#0d0d0d" }),
    React.createElement("rect", { key: "cbg", x: PAD.left, y: PAD.top, width: chartW, height: chartH, fill: "#111" })
  ];

  [0, 0.25, 0.5, 0.75, 1].forEach(frac => {
    const y = PAD.top + chartH - frac * chartH;
    const val = Math.round(frac * maxVal);
    children.push(
      React.createElement("g", { key: "grid-" + frac },
        React.createElement("line", { x1: PAD.left, y1: y, x2: PAD.left + chartW, y2: y, stroke: "#222", strokeWidth: 0.5 }),
        React.createElement("text", { x: PAD.left - 4, y: y + 3, textAnchor: "end", fill: "#555", fontSize: 8, fontFamily: "Arial" }, val >= 1000 ? (val / 1000).toFixed(1) + "k" : val)
      )
    );
  });

  const rAxisX1 = PAD.left + chartW + 4;
  const rAxisX2 = PAD.left + chartW + 36;

  if (hasJ) {
    [0, 0.5, 1].forEach(frac => {
      const y = PAD.top + chartH - frac * chartH;
      children.push(
        React.createElement("text", { key: "rj-" + frac, x: rAxisX1, y: y + 3, textAnchor: "start", fill: "#fbbf24", fontSize: 7, fontFamily: "Arial", opacity: 0.8 }, fmtMiles(Math.round(frac * maxJ)))
      );
    });
  }

  if (hasY) {
    [0, 0.5, 1].forEach(frac => {
      const y = PAD.top + chartH - frac * chartH;
      children.push(
        React.createElement("text", { key: "ry-" + frac, x: rAxisX2, y: y + 3, textAnchor: "start", fill: "#10b981", fontSize: 7, fontFamily: "Arial", opacity: 0.8 }, fmtMiles(Math.round(frac * maxY)))
      );
    });
  }

  dist.forEach((d, i) => {
    if (d.flights > 0) {
      const barH = Math.max(1, (d.flights / maxVal) * chartH);
      const x = PAD.left + i * barW;
      const y = PAD.top + chartH - barH;
      children.push(
        React.createElement("rect", { key: "b" + i, x: x, y: y, width: Math.max(0.8, barW - 0.3), height: barH, fill: theme.accent, opacity: 0.6 })
      );
    }
  });

  if (jLine) {
    children.push(React.createElement("polyline", { key: "jline", points: jLine, fill: "none", stroke: "#fbbf24", strokeWidth: 1.5, strokeLinejoin: "round", opacity: 0.9 }));
  }
  if (yLine) {
    children.push(React.createElement("polyline", { key: "yline", points: yLine, fill: "none", stroke: "#10b981", strokeWidth: 1.5, strokeLinejoin: "round", opacity: 0.9 }));
  }

  months.forEach(m => {
    children.push(
      React.createElement("g", { key: "m-" + m.idx },
        React.createElement("line", { x1: PAD.left + m.idx * barW, y1: PAD.top, x2: PAD.left + m.idx * barW, y2: PAD.top + chartH, stroke: "#333", strokeWidth: 0.5, strokeDasharray: "2,2" }),
        React.createElement("text", { x: PAD.left + m.idx * barW + 2, y: H - 6, fill: "#666", fontSize: 9, fontFamily: "Arial" }, m.label)
      )
    );
  });

  children.push(
    React.createElement("text", { key: "ylab", x: 4, y: PAD.top + chartH / 2, fill: "#555", fontSize: 7, fontFamily: "Arial", transform: `rotate(-90 4 ${PAD.top + chartH / 2})`, textAnchor: "middle" }, "flights/day")
  );

  // --- Vertical "Today" line (index 0 = today) ---
  if (dist.length > 0) {
    const todayX = PAD.left;
    children.push(
      React.createElement("line", { key: "today-line", x1: todayX, y1: PAD.top, x2: todayX, y2: PAD.top + chartH, stroke: "#60a5fa", strokeWidth: 1.5, strokeDasharray: "4,3", opacity: 0.85 }),
      React.createElement("text", { key: "today-label", x: todayX + 3, y: PAD.top + chartH - 4, fill: "#60a5fa", fontSize: 7, fontFamily: "Arial", fontWeight: 600 }, "Today")
    );
  }

  // --- Vertical release-date line (dynamic based on release window) ---
  if (releaseDays && releaseDays > 0 && releaseDays < dist.length) {
    const rlX = PAD.left + releaseDays * barW;
    const rlLabel = releaseDate ? new Date(releaseDate + "T00:00:00").toLocaleDateString("en-US", { month: "short", day: "numeric", year: "2-digit" }) : releaseDays + "d";
    children.push(
      React.createElement("line", { key: "release-line", x1: rlX, y1: PAD.top, x2: rlX, y2: PAD.top + chartH, stroke: "#ef4444", strokeWidth: 1.5, strokeDasharray: "4,3", opacity: 0.9 }),
      React.createElement("text", { key: "release-label", x: rlX + 3, y: PAD.top + 10, fill: "#ef4444", fontSize: 7, fontFamily: "Arial", fontWeight: 600 }, rlLabel + " (" + releaseDays + "d)")
    );
  }

  if (hasJ || hasY) {
    const legendItems = [];
    let lx = 0;
    legendItems.push(React.createElement("rect", { key: "lb", x: lx, y: 0, width: 8, height: 8, fill: theme.accent, opacity: 0.7, rx: 1 }));
    legendItems.push(React.createElement("text", { key: "lt", x: lx + 11, y: 7, fill: "#888", fontSize: 7, fontFamily: "Arial" }, "Flights"));
    lx += 42;
    if (hasJ) {
      legendItems.push(React.createElement("line", { key: "ljl", x1: lx, y1: 4, x2: lx + 10, y2: 4, stroke: "#fbbf24", strokeWidth: 1.5 }));
      legendItems.push(React.createElement("text", { key: "ljt", x: lx + 13, y: 7, fill: "#fbbf24", fontSize: 7, fontFamily: "Arial" }, "Biz Avg"));
      lx += 42;
    }
    if (hasY) {
      legendItems.push(React.createElement("line", { key: "lyl", x1: lx, y1: 4, x2: lx + 10, y2: 4, stroke: "#10b981", strokeWidth: 1.5 }));
      legendItems.push(React.createElement("text", { key: "lyt", x: lx + 13, y: 7, fill: "#10b981", fontSize: 7, fontFamily: "Arial" }, "Econ Avg"));
      lx += 48;
    }
    const legendX = PAD.left + chartW - lx - 4;
    const legendY = PAD.top + chartH - 14;
    children.push(
      React.createElement("rect", { key: "legend-bg", x: legendX - 4, y: legendY - 3, width: lx + 8, height: 14, rx: 3, fill: "#0d0d0d", opacity: 0.85 })
    );
    children.push(
      React.createElement("g", { key: "legend", transform: `translate(${legendX}, ${legendY})` }, ...legendItems)
    );
  }

  return React.createElement("svg", { width: W, height: H, style: { display: "block" } }, ...children);
}

// --- GapAnalysis ----
function GapAnalysis() {
  const [data, setData] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [expanded, setExpanded] = React.useState({});

  const theme = window.activeTheme || window.theme || {
    bg: "#0a0a0a", card: "#141414", border: "#252525",
    text: "#e0e0e0", textDim: "#888", accent: "#3b82f6",
    success: "#10b981", warning: "#f59e0b", danger: "#ef4444",
  };

  const [, _themeRender] = React.useState(0);
  React.useEffect(() => {
    const h = () => _themeRender(n => n + 1);
    window.addEventListener("theme-change", h);
    return () => window.removeEventListener("theme-change", h);
  }, []);

  React.useEffect(() => {
    (window.authFetch || fetch)(`${API}/api/admin/gaps`)
      .then(r => { if (!r.ok) throw new Error("Not authorized"); return r.json(); })
      .then(d => { setData(d); setLoading(false); })
      .catch(() => setLoading(false));
  }, []);

  if (loading) return React.createElement("div", { style: { padding: 20, textAlign: "center", color: theme.textDim } }, "Scanning for gaps...");

  if (!data || !data.programs || Object.keys(data.programs).length === 0) {
    return React.createElement("div", { style: { padding: 16, background: theme.success + "15", border: `1px solid ${theme.success}40`, borderRadius: 10, marginBottom: 20, color: theme.success, fontSize: 13, fontWeight: 600 } },
      "\u2713 No date gaps detected \u2014 all programs have continuous coverage"
    );
  }

  const summary = data.summary;
  const programs = data.programs;
  const sorted = Object.entries(programs).sort((a, b) => b[1].total_gap_days - a[1].total_gap_days);

  const covColor = (pct) => pct >= COVERAGE_SUCCESS_PCT ? theme.success : pct >= COVERAGE_WARNING_PCT ? theme.warning : theme.danger;

  const GapTimeline = ({ info }) => {
    const rangeStart = new Date(info.range_start + "T00:00:00");
    const rangeEnd = new Date(info.range_end + "T00:00:00");
    const rangeDays = info.range_days;
    if (rangeDays <= 0) return null;
    const dayToX = (dateStr) => {
      const d = new Date(dateStr + "T00:00:00");
      return ((d - rangeStart) / (rangeEnd - rangeStart)) * 100;
    };
    return React.createElement("div", { style: { position: "relative", height: 10, background: theme.success + "30", borderRadius: 4, overflow: "hidden", marginTop: 6 } },
      ...info.gaps.map((g, i) => {
        const left = dayToX(g.start);
        const right = dayToX(g.end);
        const width = Math.max(0.5, right - left);
        return React.createElement("div", { key: i, title: `${g.start} \u2192 ${g.end} (${g.days}d)`, style: { position: "absolute", left: left + "%", width: width + "%", top: 0, bottom: 0, background: theme.danger, opacity: 0.8, borderRadius: 2 } });
      })
    );
  };

  return React.createElement("div", { style: { marginBottom: 24 } },
    React.createElement("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 12 } },
      React.createElement("h3", { style: { fontSize: 16, fontWeight: 700, margin: 0, color: theme.text } }, "\uD83D\uDD0D Gap Analysis"),
      React.createElement("div", { style: { display: "flex", gap: 16, fontSize: 12 } },
        React.createElement("span", { style: { color: theme.danger } }, summary.total_gaps + " gaps"),
        React.createElement("span", { style: { color: theme.warning } }, summary.total_gap_days + " missing days"),
        React.createElement("span", { style: { color: theme.textDim } }, "~" + summary.estimated_api_calls + " calls to fix")
      )
    ),
    React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 6 } },
      ...sorted.map(([source, info]) => {
        const isExpanded = expanded[source];
        const progName = (PROGRAMS[source] || {}).name || source;
        return React.createElement("div", { key: source, style: { background: theme.card, border: `1px solid ${theme.border}`, borderRadius: 8, overflow: "hidden" } },
          React.createElement("div", { onClick: () => setExpanded(prev => ({ ...prev, [source]: !prev[source] })), style: { display: "flex", alignItems: "center", gap: 12, padding: "10px 14px", cursor: "pointer" } },
            React.createElement("span", { style: { fontSize: 10, color: theme.textDim, transition: "transform 0.15s", transform: isExpanded ? "rotate(90deg)" : "none" } }, "\u25B6"),
            React.createElement("span", { style: { fontSize: 13, fontWeight: 600, color: theme.text, width: 160 } }, progName),
            React.createElement("span", { style: { fontSize: 12, color: theme.danger, fontWeight: 600, width: 80 } }, info.gaps.length + " gap" + (info.gaps.length > 1 ? "s" : "")),
            React.createElement("span", { style: { fontSize: 12, color: theme.warning, width: 80 } }, info.total_gap_days + " days"),
            React.createElement("span", { style: { fontSize: 12, color: covColor(info.coverage_pct), width: 70, textAlign: "right" } }, info.coverage_pct + "%"),
            React.createElement("div", { style: { flex: 1, minWidth: 100 } }, React.createElement(GapTimeline, { info }))
          ),
          isExpanded && React.createElement("div", { style: { padding: "0 14px 12px 36px", borderTop: `1px solid ${theme.border}` } },
            React.createElement("div", { style: { fontSize: 11, color: theme.textDim, marginTop: 8, marginBottom: 6 } }, `Range: ${info.range_start} \u2192 ${info.range_end} (${info.range_days} days, ${info.covered_days} covered)`),
            React.createElement("table", { style: { width: "100%", fontSize: 12, borderCollapse: "collapse" } },
              React.createElement("thead", null,
                React.createElement("tr", { style: { color: theme.textDim, textAlign: "left" } },
                  React.createElement("th", { style: { padding: "4px 8px", fontWeight: 500 } }, "Start"),
                  React.createElement("th", { style: { padding: "4px 8px", fontWeight: 500 } }, "End"),
                  React.createElement("th", { style: { padding: "4px 8px", fontWeight: 500, textAlign: "right" } }, "Days")
                )
              ),
              React.createElement("tbody", null,
                ...info.gaps.map((g, i) => React.createElement("tr", { key: i, style: { borderTop: `1px solid ${theme.border}10` } },
                  React.createElement("td", { style: { padding: "4px 8px", color: theme.text } }, g.start),
                  React.createElement("td", { style: { padding: "4px 8px", color: theme.text } }, g.end),
                  React.createElement("td", { style: { padding: "4px 8px", color: theme.danger, fontWeight: 600, textAlign: "right" } }, g.days)
                ))
              )
            )
          )
        );
      })
    )
  );
}

// --- IngestionControl ----
function IngestionControl() {
  // === Tab state (NEW) ===
  const [activeTab, setActiveTab] = React.useState("ingestion");

  // === Ingestion state (from OLD) ===
  const [coverage, setCoverage] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [selected, setSelected] = React.useState({});
  const [maxPages, setMaxPages] = React.useState(0);
  const [triggerStatus, setTriggerStatus] = React.useState(null);
  const [triggering, setTriggering] = React.useState(false);

  // === Chart hover state (from OLD) ===
  const [hoveredSource, setHoveredSource] = React.useState(null);
  const [chartCache, setChartCache] = React.useState({});
  const [chartLoading, setChartLoading] = React.useState(false);
  const [tooltipPos, setTooltipPos] = React.useState({ x: 0, y: 0 });
  const hoverTimerRef = React.useRef(null);

  // === Gap state (NEW) ===
  const [gaps, setGaps] = React.useState({});
  const [gapSummary, setGapSummary] = React.useState({});
  const [gapsLoading, setGapsLoading] = React.useState(true);
  const [selectedGaps, setSelectedGaps] = React.useState({});
  const [maxBudget, setMaxBudget] = React.useState(DEFAULT_GAP_FILL_BUDGET);
  const [gapTriggerStatus, setGapTriggerStatus] = React.useState(null);
  const [gapTriggering, setGapTriggering] = React.useState(false);

  // === Theme (from OLD) ===
  const theme = window.activeTheme || window.theme || {
    bg: "#0a0a0a", card: "#141414", border: "#252525",
    text: "#e0e0e0", textDim: "#888", accent: "#3b82f6",
    success: "#10b981", warning: "#f59e0b", danger: "#ef4444",
    purple: "#8b5cf6"
  };

  // Theme change listener (from OLD)
  const [, _themeRender2] = React.useState(0);
  React.useEffect(() => {
    const h = () => _themeRender2(n => n + 1);
    window.addEventListener("theme-change", h);
    return () => window.removeEventListener("theme-change", h);
  }, []);

  // === Load coverage data (from OLD - uses authFetch) ===
  React.useEffect(() => {
    (window.authFetch || fetch)(`${API}/api/admin/ingestion-coverage`)
      .then(r => { if (!r.ok) throw new Error("Not authorized"); return r.json(); })
      .then(d => {
        setCoverage(d.coverage || []);
        const sel = {};
        (d.coverage || []).forEach(a => { sel[a.source] = a.days_ahead < (a.release_days || 365) - RELEASE_WINDOW_THRESHOLD_DAYS; });
        setSelected(sel);
        setLoading(false);
      })
      .catch(() => setLoading(false));
  }, []);

  // === Load gap data (NEW - uses authFetch) ===
  React.useEffect(() => {
    (window.authFetch || fetch)(`${API}/api/admin/gaps`)
      .then(r => r.json())
      .then(d => {
        setGaps(d.programs || {});
        setGapSummary(d.summary || {});
        const sel = {};
        Object.keys(d.programs || {}).forEach(source => { sel[source] = true; });
        setSelectedGaps(sel);
        setGapsLoading(false);
      })
      .catch(() => setGapsLoading(false));
  }, []);

  // === Chart hover functions (from OLD) ===
  const fetchDistribution = React.useCallback(async (source) => {
    if (chartCache[source]) return;
    setChartLoading(true);
    try {
      const res = await (window.authFetch || fetch)(`${API}/api/admin/ingestion-distribution/${source}`);
      const data = await res.json();
      setChartCache(prev => ({ ...prev, [source]: data }));
    } catch (e) {
      console.error("Distribution fetch failed:", e);
    } finally {
      setChartLoading(false);
    }
  }, [chartCache]);

  const handleCardMouseEnter = React.useCallback((source, event) => {
    clearTimeout(hoverTimerRef.current);
    const rect = event.currentTarget.getBoundingClientRect();
    hoverTimerRef.current = setTimeout(() => {
      const tooltipW = TOOLTIP_WIDTH_PX;
      let x = rect.left + rect.width / 2 - tooltipW / 2;
      let y = rect.top - TOOLTIP_Y_OFFSET;
      if (x < WINDOW_EDGE_PAD) x = WINDOW_EDGE_PAD;
      if (x + tooltipW > window.innerWidth - WINDOW_EDGE_PAD) x = window.innerWidth - tooltipW - WINDOW_EDGE_PAD;
      if (y < WINDOW_EDGE_PAD) y = rect.bottom + WINDOW_EDGE_PAD;
      setTooltipPos({ x, y });
      setHoveredSource(source);
      fetchDistribution(source);
    }, TOOLTIP_HOVER_DELAY_MS);
  }, [fetchDistribution]);

  const handleCardMouseLeave = React.useCallback(() => {
    clearTimeout(hoverTimerRef.current);
    setHoveredSource(null);
  }, []);

  // === Ingestion helpers (from OLD) ===
  const toggleAirline = (source) => { setSelected(prev => ({ ...prev, [source]: !prev[source] })); };
  const selectAll = () => { const sel = {}; coverage.forEach(a => { sel[a.source] = true; }); setSelected(sel); };
  const selectNone = () => { const sel = {}; coverage.forEach(a => { sel[a.source] = false; }); setSelected(sel); };
  const selectedSources = Object.entries(selected).filter(([,v]) => v).map(([k]) => k);

  const triggerIngestion = async () => {
    if (selectedSources.length === 0) { setTriggerStatus({ type: "error", msg: "No airlines selected" }); return; }
    setTriggering(true); setTriggerStatus(null);
    try {
      const res = await (window.authFetch || fetch)(`${API}/api/admin/trigger-ingestion`, {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ sources: selectedSources.join(","), max_pages: maxPages || 0 })
      });
      const data = await res.json();
      if (res.ok) { setTriggerStatus({ type: "success", msg: `Ingestion triggered! ${data.message || ""}` }); }
      else { setTriggerStatus({ type: "error", msg: data.detail || data.error || "Failed to trigger" }); }
    } catch (e) { setTriggerStatus({ type: "error", msg: e.message }); }
    setTriggering(false);
  };

  // === Gap helpers (NEW) ===
  const toggleGapProgram = (source) => { setSelectedGaps(prev => ({ ...prev, [source]: !prev[source] })); };
  const selectAllGaps = () => { const sel = {}; Object.keys(gaps).forEach(source => { sel[source] = true; }); setSelectedGaps(sel); };
  const selectNoGaps = () => { const sel = {}; Object.keys(gaps).forEach(source => { sel[source] = false; }); setSelectedGaps(sel); };
  const selectedGapSources = Object.entries(selectedGaps).filter(([,v]) => v).map(([k]) => k);
  const selectedGapDays = selectedGapSources.reduce((sum, s) => sum + (gaps[s]?.total_gap_days || 0), 0);
  const selectedGapCount = selectedGapSources.reduce((sum, s) => sum + (gaps[s]?.gaps?.length || 0), 0);

  const triggerGapFilling = async () => {
    if (selectedGapSources.length === 0) { setGapTriggerStatus({ type: "error", msg: "No programs selected" }); return; }
    setGapTriggering(true); setGapTriggerStatus(null);
    try {
      const res = await (window.authFetch || fetch)(`${API}/api/admin/trigger-gaps`, {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ sources: selectedGapSources.join(","), max_budget: maxBudget || 50 })
      });
      const data = await res.json();
      if (res.ok) { setGapTriggerStatus({ type: "success", msg: data.message || "Gap filling triggered!" }); }
      else { setGapTriggerStatus({ type: "error", msg: data.detail || data.error || "Failed to trigger" }); }
    } catch (e) { setGapTriggerStatus({ type: "error", msg: e.message }); }
    setGapTriggering(false);
  };

  // === Shared helpers ===
  // Color/bar now relative to per-program release window
  const getDaysColor = (days, relDays) => {
    const pct = relDays > 0 ? (days / relDays) : 0;
    if (pct >= 0.95) return theme.success;
    if (pct >= 0.5) return theme.warning;
    if (pct >= 0.1) return "#f97316";
    return theme.danger;
  };
  const getBarWidth = (days, relDays) => {
    const target = relDays || 365;
    return Math.min(100, Math.max(2, (days / target) * 100));
  };
  const getCoverageColor = (pct) => { if (pct >= 98) return theme.success; if (pct >= 90) return theme.warning; return theme.danger; };
  const formatGapDate = (iso) => { const d = new Date(iso + "T00:00:00"); return (d.getMonth() + 1) + "/" + d.getDate(); };

  if (loading) {
    return React.createElement("div", { style: { padding: 40, textAlign: "center", color: theme.textDim } }, "Loading coverage data...");
  }

  const gapCount = Object.keys(gaps).length;

  // === Tab button helper (NEW) ===
  const tabBtn = (id, label) => React.createElement("button", {
    onClick: () => setActiveTab(id),
    style: { padding: "10px 0", fontSize: 14, fontWeight: activeTab === id ? 600 : 400, background: "none", border: "none", cursor: "pointer", color: activeTab === id ? theme.accent : theme.textDim, borderBottom: activeTab === id ? `2px solid ${theme.accent}` : "2px solid transparent", marginRight: 24 }
  }, label);

  // === Chart tooltip portal (from OLD) ===
  const chartTooltip = hoveredSource && React.createElement("div", {
    style: { position: "fixed", left: tooltipPos.x, top: tooltipPos.y, zIndex: Z_INGESTION_TOOLTIP, background: "#141414", border: "1px solid #333", borderRadius: 10, padding: 12, boxShadow: "0 12px 40px rgba(0,0,0,0.6)", pointerEvents: "none", width: TOOLTIP_WIDTH_PX, transition: "opacity 0.15s", opacity: 1 }
  },
    React.createElement("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 8 } },
      React.createElement("span", { style: { fontSize: 13, fontWeight: 700, color: theme.text } }, (PROGRAMS[hoveredSource] || {}).name || hoveredSource),
      React.createElement("span", { style: { fontSize: 11, color: theme.textDim } }, (chartCache[hoveredSource]?.release_days || 365) + "-day release window")
    ),
    chartLoading && !chartCache[hoveredSource]
      ? React.createElement("div", { style: { height: 140, display: "flex", alignItems: "center", justifyContent: "center", color: theme.textDim, fontSize: 12 } }, React.createElement("span", { style: { animation: "pulse 1s infinite" } }, "Loading..."))
      : chartCache[hoveredSource]
        ? React.createElement(DistributionChart, { data: chartCache[hoveredSource].distribution, stats: chartCache[hoveredSource].stats, theme: theme, releaseDays: chartCache[hoveredSource].release_days, releaseDate: chartCache[hoveredSource].release_date, todayDate: chartCache[hoveredSource].today })
        : null,
    chartCache[hoveredSource] && chartCache[hoveredSource].stats && React.createElement("div", { style: { display: "flex", gap: 12, marginTop: 8, paddingTop: 8, borderTop: "1px solid #252525" } },
      ...(() => {
        const st = chartCache[hoveredSource].stats;
        const relD = st.release_days || 365;
        const items = [
          { label: "Coverage", value: `${st.coverage_pct || 0}%`, color: (st.coverage_pct || 0) >= 90 ? theme.success : (st.coverage_pct || 0) >= 50 ? theme.warning : theme.danger },
          { label: "Days", value: `${st.days_in_release_window || st.days_with_data || 0}/${relD}`, color: theme.text },
          { label: "Gaps", value: `${(st.days_with_data || 0) - (st.days_in_release_window || 0)}d`, color: theme.warning },
          { label: "Flights/day", value: (st.avg_flights_per_day || 0).toLocaleString(), color: theme.accent },
        ];
        if (st.avg_j_miles) items.push({ label: "Avg Biz", value: (st.avg_j_miles).toLocaleString(), color: "#fbbf24" });
        if (st.avg_y_miles) items.push({ label: "Avg Econ", value: (st.avg_y_miles).toLocaleString(), color: "#10b981" });
        return items;
      })().map(s => React.createElement("div", { key: s.label, style: { flex: 1, textAlign: "center" } },
        React.createElement("div", { style: { fontSize: 13, fontWeight: 700, color: s.color } }, s.value),
        React.createElement("div", { style: { fontSize: 9, color: theme.textDim, marginTop: 2 } }, s.label)
      ))
    )
  );

  // ================================================================
  // RENDER
  // ================================================================
  return React.createElement("div", { style: { maxWidth: 1100, margin: "0 auto" } },

    // DB Progress Widget at top (from OLD)
    typeof DBProgressWidget === "function" && React.createElement(ErrorBoundary, null, React.createElement(DBProgressWidget)),

    // Chart tooltip portal (from OLD)
    chartTooltip,

    // --- Tab switcher (NEW) ---
    React.createElement("div", { style: { display: "flex", borderBottom: `1px solid ${theme.border}`, marginBottom: 20 } },
      tabBtn("ingestion", "\uD83D\uDE80 Rolling Ingestion"),
      tabBtn("gaps", `\uD83D\uDD0D Fill Gaps${gapCount > 0 ? ` (${gapCount})` : ""}`)
    ),

    // ================================================================
    // TAB: ROLLING INGESTION
    // ================================================================
    activeTab === "ingestion" && React.createElement(React.Fragment, null,
      React.createElement("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 20 } },
        React.createElement("h2", { style: { fontSize: 20, fontWeight: 700, margin: 0, color: theme.text } }, "\uD83D\uDE80 Ingestion Control"),
        React.createElement("span", { style: { fontSize: 12, color: theme.textDim } }, coverage.length + " programs \u2022 " + selectedSources.length + " selected")
      ),

      // Gap Analysis panel (from OLD)
      React.createElement(GapAnalysis),

      // Control bar
      React.createElement("div", { style: { display: "flex", gap: 16, alignItems: "center", marginBottom: 20, padding: 16, background: theme.card, borderRadius: 10, border: `1px solid ${theme.border}` } },
        React.createElement("div", { style: { display: "flex", gap: 8 } },
          React.createElement("button", { onClick: selectAll, style: { padding: "6px 12px", fontSize: 12, borderRadius: 6, cursor: "pointer", background: "transparent", border: `1px solid ${theme.border}`, color: theme.text } }, "Select All"),
          React.createElement("button", { onClick: selectNone, style: { padding: "6px 12px", fontSize: 12, borderRadius: 6, cursor: "pointer", background: "transparent", border: `1px solid ${theme.border}`, color: theme.text } }, "Select None")
        ),
        React.createElement("div", { style: { width: 1, height: 30, background: theme.border } }),
        React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8 } },
          React.createElement("label", { style: { fontSize: 12, color: theme.textDim, whiteSpace: "nowrap" } }, "Max Pages:"),
          React.createElement("input", { type: "number", min: 0, value: maxPages, onChange: (e) => setMaxPages(parseInt(e.target.value) || 0), placeholder: "0 = unlimited", style: { width: 90, padding: "6px 10px", fontSize: 12, borderRadius: 6, background: theme.bg, border: `1px solid ${theme.border}`, color: theme.text } }),
          React.createElement("span", { style: { fontSize: 11, color: theme.textDim } }, "(0 = unlimited)")
        ),
        React.createElement("div", { style: { flex: 1 } }),
        React.createElement("span", { style: { fontSize: 12, color: maxPages > 0 ? theme.text : theme.textDim } }, maxPages > 0 ? `~${selectedSources.length * maxPages} API calls` : `${selectedSources.length} airlines, unlimited pages`),
        React.createElement("button", { onClick: triggerIngestion, disabled: triggering || selectedSources.length === 0, style: { padding: "8px 20px", fontSize: 13, fontWeight: 600, borderRadius: 8, cursor: triggering ? "wait" : "pointer", border: "none", background: selectedSources.length === 0 ? theme.border : theme.accent, color: selectedSources.length === 0 ? theme.textDim : "#fff" } }, triggering ? "Triggering..." : "\u25B6 Run Ingestion")
      ),

      // Trigger status
      triggerStatus && React.createElement("div", { style: { padding: "10px 16px", marginBottom: 16, borderRadius: 8, fontSize: 13, background: triggerStatus.type === "success" ? "#10b98120" : "#ef444420", color: triggerStatus.type === "success" ? theme.success : theme.danger, border: `1px solid ${triggerStatus.type === "success" ? theme.success + "40" : theme.danger + "40"}` } }, triggerStatus.msg),

      // Airline cards grid (with hover from OLD)
      React.createElement("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(320px, 1fr))", gap: 10 } },
        coverage.map(airline => React.createElement("div", {
          key: airline.source,
          onClick: () => toggleAirline(airline.source),
          onMouseEnter: (e) => handleCardMouseEnter(airline.source, e),
          onMouseLeave: handleCardMouseLeave,
          style: { display: "flex", alignItems: "center", gap: 12, padding: "12px 16px", borderRadius: 10, cursor: "pointer", background: selected[airline.source] ? theme.accent + "15" : theme.card, border: `1px solid ${selected[airline.source] ? theme.accent + "50" : theme.border}`, transition: "all 0.15s" }
        },
          React.createElement("div", { style: { width: 20, height: 20, borderRadius: 4, flexShrink: 0, display: "flex", alignItems: "center", justifyContent: "center", background: selected[airline.source] ? theme.accent : "transparent", border: `2px solid ${selected[airline.source] ? theme.accent : theme.border}` } },
            selected[airline.source] && React.createElement("span", { style: { color: "#fff", fontSize: 12, fontWeight: 700 } }, "\u2713")
          ),
          React.createElement("div", { style: { flex: 1, minWidth: 0 } },
            React.createElement("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 4 } },
              React.createElement("span", { style: { fontSize: 13, fontWeight: 600, color: theme.text } }, (PROGRAMS[airline.source] || {}).name || airline.source),
              React.createElement("span", { style: { fontSize: 11, color: getDaysColor(airline.days_ahead, airline.release_days), fontWeight: 600 } }, airline.days_ahead >= 0 ? airline.days_ahead + "d / " + (airline.release_days || 365) + "d" : Math.abs(airline.days_ahead) + "d expired")
            ),
            React.createElement("div", { style: { height: 4, borderRadius: 2, background: theme.border, marginBottom: 4 } },
              React.createElement("div", { style: { height: "100%", borderRadius: 2, width: getBarWidth(airline.days_ahead, airline.release_days) + "%", background: getDaysColor(airline.days_ahead, airline.release_days) } })
            ),
            React.createElement("div", { style: { display: "flex", gap: 8, fontSize: 11, color: theme.textDim, flexWrap: "wrap" } },
              React.createElement("span", null, (airline.date_count || 0) + "d cov"),
              airline.gap_days > 0 && React.createElement("span", { style: { color: theme.warning } }, airline.gap_days + " gaps"),
              React.createElement("span", null, airline.rows.toLocaleString() + " rows"),
              airline.complete && React.createElement("span", { style: { color: theme.success } }, "\u2713 Complete")
            )
          )
        ))
      ),

      // Footer
      React.createElement("div", { style: { marginTop: 20, padding: 16, background: theme.card, borderRadius: 10, border: `1px solid ${theme.border}`, display: "flex", justifyContent: "space-between", fontSize: 12, color: theme.textDim } },
        React.createElement("span", null, "Total: " + coverage.reduce((s, a) => s + a.rows, 0).toLocaleString() + " rows across " + coverage.length + " programs"),
        React.createElement("span", null, "Full release window: " + coverage.filter(a => a.complete).length + " / " + coverage.length + " programs"),
        React.createElement("span", null, "Total gaps: " + coverage.reduce((s, a) => s + (a.gap_days || 0), 0) + " days")
      )
    ),

    // ================================================================
    // TAB: FILL GAPS (NEW)
    // ================================================================
    activeTab === "gaps" && React.createElement(React.Fragment, null,
      gapsLoading
        ? React.createElement("div", { style: { padding: 40, textAlign: "center", color: theme.textDim } }, "Loading gap analysis...")
        : gapCount === 0
          ? React.createElement("div", { style: { padding: 40, textAlign: "center", color: theme.success, background: theme.card, borderRadius: 10, border: `1px solid ${theme.border}` } },
              React.createElement("div", { style: { fontSize: 32, marginBottom: 8 } }, "\u2705"),
              React.createElement("div", { style: { fontSize: 16, fontWeight: 600 } }, "No gaps detected!"),
              React.createElement("div", { style: { fontSize: 13, color: theme.textDim, marginTop: 4 } }, "All programs have continuous date coverage.")
            )
          : React.createElement(React.Fragment, null,
              // Summary bar
              React.createElement("div", { style: { display: "flex", gap: 24, padding: 16, marginBottom: 20, background: theme.card, borderRadius: 10, border: `1px solid ${theme.border}` } },
                React.createElement("div", null,
                  React.createElement("div", { style: { fontSize: 11, color: theme.textDim, marginBottom: 2 } }, "Programs"),
                  React.createElement("div", { style: { fontSize: 18, fontWeight: 700, color: theme.text } }, gapSummary.programs_with_gaps || 0)
                ),
                React.createElement("div", null,
                  React.createElement("div", { style: { fontSize: 11, color: theme.textDim, marginBottom: 2 } }, "Total Gaps"),
                  React.createElement("div", { style: { fontSize: 18, fontWeight: 700, color: theme.warning } }, gapSummary.total_gaps || 0)
                ),
                React.createElement("div", null,
                  React.createElement("div", { style: { fontSize: 11, color: theme.textDim, marginBottom: 2 } }, "Gap Days"),
                  React.createElement("div", { style: { fontSize: 18, fontWeight: 700, color: theme.danger } }, gapSummary.total_gap_days || 0)
                ),
                React.createElement("div", null,
                  React.createElement("div", { style: { fontSize: 11, color: theme.textDim, marginBottom: 2 } }, "Est. API Calls"),
                  React.createElement("div", { style: { fontSize: 18, fontWeight: 700, color: theme.purple } }, "~" + (gapSummary.estimated_api_calls || 0))
                )
              ),

              // Control bar
              React.createElement("div", { style: { display: "flex", gap: 16, alignItems: "center", marginBottom: 20, padding: 16, background: theme.card, borderRadius: 10, border: `1px solid ${theme.border}` } },
                React.createElement("div", { style: { display: "flex", gap: 8 } },
                  React.createElement("button", { onClick: selectAllGaps, style: { padding: "6px 12px", fontSize: 12, borderRadius: 6, cursor: "pointer", background: "transparent", border: `1px solid ${theme.border}`, color: theme.text } }, "Select All"),
                  React.createElement("button", { onClick: selectNoGaps, style: { padding: "6px 12px", fontSize: 12, borderRadius: 6, cursor: "pointer", background: "transparent", border: `1px solid ${theme.border}`, color: theme.text } }, "Select None")
                ),
                React.createElement("div", { style: { width: 1, height: 30, background: theme.border } }),
                React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8 } },
                  React.createElement("label", { style: { fontSize: 12, color: theme.textDim, whiteSpace: "nowrap" } }, "Max Budget:"),
                  React.createElement("input", { type: "number", min: 10, max: 900, value: maxBudget, onChange: (e) => setMaxBudget(parseInt(e.target.value) || 50), style: { width: 80, padding: "6px 10px", fontSize: 12, borderRadius: 6, background: theme.bg, border: `1px solid ${theme.border}`, color: theme.text } }),
                  React.createElement("span", { style: { fontSize: 11, color: theme.textDim } }, "API calls")
                ),
                React.createElement("div", { style: { flex: 1 } }),
                React.createElement("span", { style: { fontSize: 12, color: theme.text } }, `${selectedGapSources.length} programs, ${selectedGapCount} gaps, ${selectedGapDays} days`),
                React.createElement("button", { onClick: triggerGapFilling, disabled: gapTriggering || selectedGapSources.length === 0, style: { padding: "8px 20px", fontSize: 13, fontWeight: 600, borderRadius: 8, cursor: gapTriggering ? "wait" : "pointer", border: "none", background: selectedGapSources.length === 0 ? theme.border : theme.purple, color: selectedGapSources.length === 0 ? theme.textDim : "#fff" } }, gapTriggering ? "Triggering..." : "\uD83D\uDD0D Fill Gaps")
              ),

              // Gap trigger status
              gapTriggerStatus && React.createElement("div", { style: { padding: "10px 16px", marginBottom: 16, borderRadius: 8, fontSize: 13, background: gapTriggerStatus.type === "success" ? "#10b98120" : "#ef444420", color: gapTriggerStatus.type === "success" ? theme.success : theme.danger, border: `1px solid ${gapTriggerStatus.type === "success" ? theme.success + "40" : theme.danger + "40"}` } }, gapTriggerStatus.msg),

              // Gap program cards grid
              React.createElement("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(340px, 1fr))", gap: 10 } },
                Object.entries(gaps).sort((a, b) => b[1].total_gap_days - a[1].total_gap_days).map(([source, info]) => React.createElement("div", {
                  key: source,
                  onClick: () => toggleGapProgram(source),
                  style: { display: "flex", alignItems: "flex-start", gap: 12, padding: "12px 16px", borderRadius: 10, cursor: "pointer", background: selectedGaps[source] ? theme.purple + "15" : theme.card, border: `1px solid ${selectedGaps[source] ? theme.purple + "50" : theme.border}`, transition: "all 0.15s" }
                },
                  React.createElement("div", { style: { width: 20, height: 20, borderRadius: 4, flexShrink: 0, marginTop: 2, display: "flex", alignItems: "center", justifyContent: "center", background: selectedGaps[source] ? theme.purple : "transparent", border: `2px solid ${selectedGaps[source] ? theme.purple : theme.border}` } },
                    selectedGaps[source] && React.createElement("span", { style: { color: "#fff", fontSize: 12, fontWeight: 700 } }, "\u2713")
                  ),
                  React.createElement("div", { style: { flex: 1, minWidth: 0 } },
                    React.createElement("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 4 } },
                      React.createElement("span", { style: { fontSize: 13, fontWeight: 600, color: theme.text } }, (PROGRAMS[source] || {}).name || source),
                      React.createElement("span", { style: { fontSize: 11, color: getCoverageColor(info.coverage_pct), fontWeight: 600 } }, info.coverage_pct + "% covered")
                    ),
                    React.createElement("div", { style: { height: 4, borderRadius: 2, background: theme.border, marginBottom: 6 } },
                      React.createElement("div", { style: { height: "100%", borderRadius: 2, width: info.coverage_pct + "%", background: getCoverageColor(info.coverage_pct) } })
                    ),
                    React.createElement("div", { style: { display: "flex", gap: 12, fontSize: 11, color: theme.textDim, marginBottom: 4 } },
                      React.createElement("span", { style: { color: theme.warning } }, info.gaps.length + (info.gaps.length === 1 ? " gap" : " gaps")),
                      React.createElement("span", { style: { color: theme.danger } }, info.total_gap_days + " days missing"),
                      React.createElement("span", null, info.range_start + " \u2192 " + info.range_end)
                    ),
                    React.createElement("div", { style: { fontSize: 11, color: theme.textDim } },
                      info.gaps.slice(0, 3).map((g, i) => React.createElement("span", { key: i, style: { display: "inline-block", marginRight: 8, marginTop: 2, padding: "2px 6px", borderRadius: 4, background: theme.bg, border: `1px solid ${theme.border}` } }, formatGapDate(g.start) + "\u2013" + formatGapDate(g.end) + " (" + g.days + "d)")),
                      info.gaps.length > 3 && React.createElement("span", { style: { fontSize: 10, color: theme.textDim } }, " +" + (info.gaps.length - 3) + " more")
                    )
                  )
                ))
              ),

              // Gap footer
              React.createElement("div", { style: { marginTop: 20, padding: 16, background: theme.card, borderRadius: 10, border: `1px solid ${theme.border}`, display: "flex", justifyContent: "space-between", fontSize: 12, color: theme.textDim } },
                React.createElement("span", null, selectedGapSources.length + " of " + gapCount + " programs selected"),
                React.createElement("span", null, "Selected: " + selectedGapCount + " gaps, " + selectedGapDays + " gap days")
              )
            )
    )
  );
}
