// Gantt + Calendar — editable ledger, click-to-detail, internal lanes (Team/HR + Hiring)
//
// Live (mgmt / md) lanes come from Monday via /api/roadmap. Internal lanes are
// local-only scaffolding kept in localStorage so users can track non-client
// work without polluting the Monday board. Bars start empty — users add real
// items via the "+ Add" buttons.
const INITIAL_LANES = [
  { id:"l-team",     name:"Team & HR",       segment:"int", bars:[] },
  { id:"l-hiring",   name:"Hiring pipeline", segment:"int", bars:[] },
  { id:"l-internal", name:"Operations",      segment:"int", bars:[] },
];

// Status taxonomy mirrors the Monday "Operations Roadmap" board.
// Legacy keys (in-progress/planned/etc.) are kept so the local-only internal lanes still render.
const STATUS_COLORS = {
  // Monday labels
  "In Progress":      "var(--teal)",
  "Scheduled":        "var(--accent)",
  "Needs Scheduling": "var(--gold)",
  "Completed":        "var(--green)",
  "On Hold":          "var(--ink-muted)",
  "cancelled":        "var(--red)",
  // Legacy / internal-only
  "in-progress": "var(--teal)",
  "planned": "var(--accent)",
  "upcoming": "var(--plum)",
  "event": "var(--gold)",
  "milestone": "var(--gold)",
  "overdue": "var(--red)",
};
const MONDAY_STATUSES = ["Scheduled", "In Progress", "Needs Scheduling", "On Hold", "Completed", "cancelled"];

// Zoom presets — `days` = visible window length, `pastDays` = how far back today sits inside it.
// Origin (today−10) stays fixed; only the viewport changes.
const ZOOM_PRESETS = {
  week:    { days: 7,   pastDays: 1,  label: "Week"    },
  month:   { days: 30,  pastDays: 4,  label: "Month"   },
  quarter: { days: 90,  pastDays: 10, label: "Quarter" },
  year:    { days: 365, pastDays: 30, label: "Year"    },
};
const ORIGIN_PAST_DAYS = 10; // matches roadmapOrigin() in live-data.jsx
const GANTT_DAYS = 90;
const TODAY_OFFSET = 10;

// Internal-only lanes that don't sync to Monday (Team & HR, Hiring, Operations).
// Filtered by segment === "int".
const LOCAL_INTERNAL_LANES = INITIAL_LANES.filter(l => l.segment === "int");

function GanttCalendar(){
  const { data: roadmap, origin, refresh } = window.useRoadmap ? window.useRoadmap() : { data:null, origin:new Date(), refresh:()=>{} };

  // Local-only internal lanes (Team & HR / Hiring / Ops) live in component state so they're editable.
  // Persist to localStorage so deletions/renames/added bars survive reloads.
  //
  // v1 → v2 migration (May 2026): v1 was seeded with hardcoded mockup bars
  // ("Performance reviews — Q2 cycle", "Annual offsite — Galilee", etc., ids b30..b52).
  // v2 starts empty. Existing users get migrated: their user-added bars
  // (timestamp-style ids like "b1716200000000") are kept; the seeded mockups are dropped.
  const LOCAL_STORAGE_KEY = "mcc.gantt.localInternal.v2";
  const LEGACY_KEY = "mcc.gantt.localInternal.v1";
  const [localInternal, setLocalInternal] = React.useState(() => {
    try {
      const cached = localStorage.getItem(LOCAL_STORAGE_KEY);
      if (cached !== null) return JSON.parse(cached);
      const legacy = localStorage.getItem(LEGACY_KEY);
      if (legacy !== null) {
        const parsed = JSON.parse(legacy);
        const cleaned = parsed.map(l => ({
          ...l,
          bars: (l.bars || []).filter(b => /^b\d{10,}$/.test(String(b.id || ""))),
        }));
        localStorage.removeItem(LEGACY_KEY);
        return cleaned;
      }
    } catch {}
    return JSON.parse(JSON.stringify(LOCAL_INTERNAL_LANES));
  });
  React.useEffect(() => {
    try { localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(localInternal)); } catch {}
  }, [localInternal]);

  // Loading state: API hasn't responded yet. Render an empty Gantt with a
  // "Loading roadmap…" placeholder instead of flashing mockup data.
  const isLoading = !roadmap;
  const lanes = React.useMemo(() => {
    if (isLoading) return [];
    return [...roadmap.lanes, ...localInternal];
  }, [roadmap, localInternal, isLoading]);

  // Helper: edit a single lane in localInternal state.
  const mutateLocal = (laneId, fn) => {
    setLocalInternal(prev => prev.map(l => l.id === laneId ? fn(l) : l).filter(Boolean));
  };
  const isLocalLane = (laneId) => localInternal.some(l => l.id === laneId);

  const [mode, setMode] = React.useState("gantt");
  const [filter, setFilter] = React.useState("all");
  const [zoom, setZoom] = React.useState("week");
  const isMobile = window.useMediaQuery ? window.useMediaQuery("(max-width: 720px)") : false;
  const [editing, setEditing] = React.useState(null); // { laneId, barId } or { laneId:"NEW" }
  const [editingLane, setEditingLane] = React.useState(null); // laneId for rename / segment change
  const [showAddLane, setShowAddLane] = React.useState(false);

  // Viewport derived from zoom. Origin = today − ORIGIN_PAST_DAYS, bars carry offsets from that origin.
  // viewStart = bar-offset of the leftmost visible day; today sits at offset ORIGIN_PAST_DAYS from origin.
  // MUST come before laneHasVisibleBar — that filter reads viewStart/viewDays.
  const preset = ZOOM_PRESETS[zoom] || ZOOM_PRESETS.quarter;
  const viewDays = preset.days;
  const viewStart = ORIGIN_PAST_DAYS - preset.pastDays;
  const todayOffsetInView = preset.pastDays;
  const dayWidth = 100 / viewDays;

  const segmentFiltered = lanes.filter(l => filter === "all" ? true : l.segment === filter);

  // A lane is "in view" if at least one non-ghost bar overlaps the current zoom window.
  const laneHasVisibleBar = (lane) => lane.bars.some(b => {
    if (b._ghost) return false;
    const end = b.start + (b.len || 1) - 1;
    return end >= viewStart && b.start <= viewStart + viewDays - 1;
  });
  const filtered = mode === "gantt"
    ? segmentFiltered.filter(laneHasVisibleBar)
    : segmentFiltered;
  const hiddenLaneCount = mode === "gantt" ? segmentFiltered.length - filtered.length : 0;

  // Position helpers: bars store offsets from origin; we shift by viewStart to position in viewport.
  const barLeft  = (start) => ((start - viewStart) * dayWidth) + "%";
  const barWidth = (len)   => (len * dayWidth) + "%";

  // ===== Sub-track packing (parallel rows within a lane) =====
  // Each bar's "footprint" includes the visible label space, so labels don't visually collide.
  // Milestones get a fixed label-buffer based on zoom; narrow multi-day bars (too short
  // to hold their label inline) also need that buffer added beyond the bar's right edge.
  const MILESTONE_PAD = { week: 2, month: 6, quarter: 14, year: 50 };
  const padDaysForZoom = MILESTONE_PAD[zoom] || 14;
  const NARROW_PCT = 8; // bars narrower than 8% of viewport render with an external label pill
  const SUB_TRACK_PX = 30;

  const isMilestoneBar = (b) => b.status === "milestone" || (b.len != null && b.len <= 1);
  const isNarrowBar = (b) => !isMilestoneBar(b) && (b.len * dayWidth) < NARROW_PCT;
  const footprintLen = (b) => {
    if (isMilestoneBar(b)) return padDaysForZoom;
    if (isNarrowBar(b))    return (b.len || 1) + padDaysForZoom;
    return b.len || 1;
  };

  // Greedy row-packing: returns bars annotated with _row (0..N). Lowest possible row wins.
  const packRows = (bars) => {
    const sorted = [...bars].sort((a, b) => a.start - b.start);
    const rows = []; // each row's running lastEnd offset
    return sorted.map(b => {
      const fpEnd = b.start + footprintLen(b);
      let row = rows.findIndex(r => r.lastEnd <= b.start);
      if (row === -1) { row = rows.length; rows.push({ lastEnd: fpEnd }); }
      else            { rows[row].lastEnd = fpEnd; }
      return { ...b, _row: row };
    });
  };

  // Date helpers: turn a bar offset into a human-readable label.
  const fmtDay = (offset) => {
    const dt = new Date(origin);
    dt.setDate(dt.getDate() + offset);
    return dt.toLocaleDateString("en-US", { month: "short", day: "numeric" }) + " · " +
           dt.toLocaleDateString("en-US", { weekday: "short" });
  };
  const fmtRange = (start, len) => {
    if ((len || 1) <= 1) return fmtDay(start);
    const endDt = new Date(origin); endDt.setDate(endDt.getDate() + start + (len || 1) - 1);
    const endStr = endDt.toLocaleDateString("en-US", { month: "short", day: "numeric" });
    return fmtDay(start) + " → " + endStr;
  };

  // Month labels — one segment per month inside the current viewport.
  const monthName = ["JAN","FEB","MAR","APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC"];
  const monthLabels = (() => {
    const labels = [];
    const winStart = new Date(origin); winStart.setDate(winStart.getDate() + viewStart);
    const winEnd   = new Date(winStart); winEnd.setDate(winStart.getDate() + viewDays - 1);
    let segStart = new Date(winStart);
    while (segStart <= winEnd) {
      const lastDay = new Date(segStart.getFullYear(), segStart.getMonth() + 1, 0);
      const segEnd  = lastDay > winEnd ? winEnd : lastDay;
      const days    = Math.round((segEnd - segStart) / 86400000) + 1;
      labels.push({ label: monthName[segStart.getMonth()], days });
      segStart = new Date(segStart.getFullYear(), segStart.getMonth() + 1, 1);
    }
    return labels;
  })();

  // Live (Monday) bars have all-digit IDs; local internal bars are like "b30".
  const isLiveId = id => /^\d+$/.test(String(id));
  const isoOf = (offset) => window.roadmapHelpers ? window.roadmapHelpers.isoFromOffset(offset, origin) : null;

  // Translate the modal's {start, len} into the API's {start, end} ISO pair.
  const offsetsToIsoRange = (start, len) => ({
    start: isoOf(start),
    end:   isoOf(start + Math.max(0, len - 1)),
  });

  const updateBar = async (laneId, barId, patch) => {
    if (isLocalLane(laneId)) {
      mutateLocal(laneId, l => ({ ...l, bars: l.bars.map(b => b.id === barId ? { ...b, ...patch } : b) }));
      return;
    }
    if (isLiveId(barId)) {
      const { start: isoStart, end: isoEnd } = offsetsToIsoRange(patch.start, patch.len);
      try {
        await window.roadmapApi.update(barId, {
          title:        patch.title,
          status:       patch.status,
          note:         patch.note,
          type:         patch.type,
          mdsAssigned:  patch.mdsAssigned,
          frontDesk:    patch.frontDesk,
          calendly:     patch.calendly,
          start:        isoStart,
          end:          isoEnd,
        });
        refresh();
      } catch (e) { console.error(e); alert("Save failed: " + e.message); }
    }
  };

  const addBar = async (laneId, bar) => {
    const lane = lanes.find(l => l.id === laneId);
    if (!lane) return;
    if (isLocalLane(laneId)) {
      mutateLocal(laneId, l => ({ ...l, bars: [...l.bars, { ...bar, id: "b" + Date.now() }] }));
      return;
    }
    // Live lane → call Monday
    const { start, end } = offsetsToIsoRange(bar.start, bar.len);
    try {
      await window.roadmapApi.create({
        title:        bar.title,
        lane:         lane.name,
        status:       bar.status,
        note:         bar.note,
        type:         bar.type,
        mdsAssigned:  bar.mdsAssigned,
        frontDesk:    bar.frontDesk,
        calendly:     bar.calendly,
        start, end,
      });
      refresh();
    } catch (e) { console.error(e); alert("Create failed: " + e.message); }
  };

  const deleteBar = async (laneId, barId) => {
    if (isLocalLane(laneId)) {
      mutateLocal(laneId, l => ({ ...l, bars: l.bars.filter(b => b.id !== barId) }));
      return;
    }
    if (isLiveId(barId)) {
      try {
        await window.roadmapApi.remove(barId);
        refresh();
      } catch (e) { console.error(e); alert("Delete failed: " + e.message); }
    }
  };

  // Lane operations — local lanes are fully editable; live (Monday) lanes are read-only at the lane level.
  const updateLane = (laneId, patch) => {
    if (isLocalLane(laneId)) mutateLocal(laneId, l => ({ ...l, ...patch }));
    else alert("Live lanes come from the Monday board — rename the Company status label there to change this lane.");
  };
  const addLane = (lane) => {
    // New lanes are always added as local-only internal lanes. To create a Monday lane,
    // first add a label on the Monday Operations Roadmap → Company column, then create an item with it.
    setLocalInternal(prev => [...prev, { ...lane, id: "l" + Date.now(), segment: lane.segment || "int", bars: [] }]);
  };
  const deleteLane = (laneId) => {
    if (isLocalLane(laneId)) setLocalInternal(prev => prev.filter(l => l.id !== laneId));
    else alert("Live lanes can't be deleted from the control center — remove the Company status label on Monday.");
  };

  // Click handlers
  const openBar = (laneId, barId) => setEditing({ laneId, barId });
  const openNewBar = (laneId) => setEditing({ laneId, barId:"NEW" });

  // Expose a window-level opener so the ClientDrawer (and anything else) can launch
  // the same bar modal we use here, keyed by lane *name* (case-insensitive).
  React.useEffect(() => {
    window.openBarEditorByLane = (laneName, barId) => {
      const lane = lanes.find(l => (l.name || "").toLowerCase() === (laneName || "").toLowerCase());
      if (!lane) return false;
      setEditing({ laneId: lane.id, barId: barId || "NEW" });
      return true;
    };
    return () => { try { delete window.openBarEditorByLane; } catch {} };
  }, [lanes]);

  // Calendar window — Sunday of last week → +63 days, so today sits in row 2.
  const buildCalendar = () => {
    const cells = [];
    const start = new Date();
    start.setHours(0, 0, 0, 0);
    const dow = start.getDay(); // 0=Sun, 1=Mon, ..., 6=Sat
    start.setDate(start.getDate() - dow - 7); // Sunday of previous week
    for (let i = 0; i < 63; i++) {
      const dt = new Date(start);
      dt.setDate(start.getDate() + i);
      cells.push(dt);
    }
    return cells;
  };
  const cells = buildCalendar();
  const calStart = cells[0];
  const calEnd = cells[cells.length - 1];

  // Resolve a bar's actual start date — live bars carry _startIso; internal bars use offset.
  const barStartDate = (b) => {
    if (b._startIso) {
      const [y, m, d] = b._startIso.split("-").map(Number);
      return new Date(y, m - 1, d);
    }
    if (b.start != null) {
      const dt = new Date(origin);
      dt.setDate(dt.getDate() + b.start);
      return dt;
    }
    return null;
  };

  const calendarEvents = {};
  lanes.forEach(l => {
    if (filter !== "all" && l.segment !== filter) return;
    l.bars.forEach(b => {
      if (b._ghost) return; // No date — skip from calendar
      const dt = barStartDate(b);
      if (!dt) return;
      if (dt < calStart || dt > calEnd) return; // outside window
      const key = dt.getFullYear() + "-" + dt.getMonth() + "-" + dt.getDate();
      if (!calendarEvents[key]) calendarEvents[key] = [];
      calendarEvents[key].push({ laneId: l.id, barId: b.id, company: l.name, title: b.title, color: b.color, segment: l.segment });
    });
  });
  const todayMidnight = (() => { const d = new Date(); d.setHours(0,0,0,0); return d.getTime(); })();

  // Resolve bar/lane being edited
  const editLane = editing ? lanes.find(l => l.id === editing.laneId) : null;
  const editBar = editing && editLane && editing.barId !== "NEW" ? editLane.bars.find(b => b.id === editing.barId) : null;

  return (
    <section className="section gantt-section">
      <div className="section-head">
        <div className="title">
          <span className="eyebrow">90-day timeline · Editable ledger</span>
          <h2>Gantt <em>&</em> calendar</h2>
        </div>
        <div className="gantt-controls">
          <div className="seg">
            <button className={mode==="gantt"?"on":""} onClick={()=>setMode("gantt")}>Gantt</button>
            <button className={mode==="calendar"?"on":""} onClick={()=>setMode("calendar")}>Calendar</button>
          </div>
          {mode === "gantt" && (
            <div className="seg">
              {Object.keys(ZOOM_PRESETS).map(k => (
                <button key={k} className={zoom===k?"on":""} onClick={()=>setZoom(k)}>{ZOOM_PRESETS[k].label}</button>
              ))}
            </div>
          )}
          <div className="seg">
            <button className={filter==="all"?"on":""} onClick={()=>setFilter("all")}>All</button>
            <button className={filter==="md"?"on":""} onClick={()=>setFilter("md")}>MD</button>
            <button className={filter==="mgmt"?"on":""} onClick={()=>setFilter("mgmt")}>Mgmt</button>
            <button className={filter==="int"?"on":""} onClick={()=>setFilter("int")}>Internal</button>
          </div>
          <button className="gantt-add" onClick={()=>setShowAddLane(true)}><Icon.Plus/> New lane</button>
        </div>
      </div>

      {mode === "gantt" && isMobile && (
        <GanttMobileList lanes={segmentFiltered} origin={origin} onOpen={openBar}/>
      )}

      {mode === "gantt" && !isMobile && (
        <div className="gantt v2">
          <div className="gantt-grid">
            <div className="gantt-head">
              <div className="g-lane-label head">
                <span className="g-lane-title">LEDGER</span>
                <span className="g-lane-sub">
                  {filtered.length} lane{filtered.length!==1?"s":""}
                  {hiddenLaneCount > 0 && (
                    <span style={{color:"var(--ink-muted)", marginLeft:6}}> · {hiddenLaneCount} hidden</span>
                  )}
                </span>
              </div>
              <div className="g-track-head">
                {monthLabels.map((m, i) => (
                  <div key={i} className="g-month" style={{ width: (m.days * dayWidth) + "%" }}>
                    <span>{m.label}</span>
                  </div>
                ))}
              </div>
            </div>

            <div className="gantt-body">
              <div className="g-today" style={{ left: `calc(220px + ${todayOffsetInView * dayWidth}%)` }}>
                <span className="g-today-pin">TODAY · {new Date().toLocaleDateString("en-US",{month:"short",day:"numeric"}).toUpperCase()}</span>
              </div>
              {isLoading && (
                <div style={{
                  padding:"72px 32px", textAlign:"center",
                  color:"var(--ink-soft)",
                  borderTop:"1px dashed var(--line)",
                }}>
                  <div className="mono" style={{color:"var(--ink-muted)", marginBottom:10}}>LOADING ROADMAP…</div>
                  <div className="g-skeleton-rows" aria-hidden="true">
                    {[0,1,2].map(i => <div key={i} className="g-skeleton-row" style={{animationDelay: (i*0.15)+"s"}}/>)}
                  </div>
                </div>
              )}
              {!isLoading && filtered.length === 0 && (
                <div style={{
                  padding:"56px 32px", textAlign:"center",
                  color:"var(--ink-soft)",
                  borderTop:"1px dashed var(--line)",
                }}>
                  <div className="mono" style={{color:"var(--ink-muted)", marginBottom:8}}>NO EVENTS IN THIS {ZOOM_PRESETS[zoom].label.toUpperCase()}</div>
                  <h4 style={{marginBottom:6, fontFamily:"'Fraunces','Noto Sans Hebrew',serif", fontSize:22, fontWeight:500}}>
                    Quiet {ZOOM_PRESETS[zoom].label.toLowerCase()}
                  </h4>
                  <p style={{marginBottom:18}}>
                    {hiddenLaneCount > 0
                      ? `${hiddenLaneCount} lane${hiddenLaneCount===1?"":"s"} ${hiddenLaneCount===1?"has":"have"} events outside this window.`
                      : "Nothing scheduled. Try widening the zoom or pick a lane to add to."}
                  </p>
                  <div style={{display:"flex", gap:8, justifyContent:"center", flexWrap:"wrap"}}>
                    {Object.keys(ZOOM_PRESETS).filter(k => k !== zoom).map(k => (
                      <button key={k} onClick={()=>setZoom(k)}
                              style={{
                                padding:"6px 14px", borderRadius:999,
                                border:"1px solid var(--line)", background:"var(--bg-card)",
                                color:"var(--ink)", fontSize:13, cursor:"pointer",
                              }}>
                        Switch to {ZOOM_PRESETS[k].label}
                      </button>
                    ))}
                  </div>
                </div>
              )}
              {filtered.map(lane => {
                const ghostCount = lane.bars.filter(b => b._ghost).length;
                const visibleBars = lane.bars.filter(b => !b._ghost);
                const packed = packRows(visibleBars);
                const rowCount = Math.max(1, packed.reduce((m, b) => Math.max(m, b._row + 1), 1));
                const trackHeight = Math.max(44, rowCount * SUB_TRACK_PX + 4);
                return (
                <div key={lane.id} className="g-lane">
                  <div className="g-lane-label" style={{ minHeight: trackHeight + 12 }}>
                    <span className={"g-lane-tag " + lane.segment}/>
                    <div className="g-lane-name">
                      {editingLane === lane.id ? (
                        <input autoFocus className="lane-edit-input"
                               defaultValue={lane.name}
                               onBlur={e=>{ updateLane(lane.id,{name:e.target.value}); setEditingLane(null); }}
                               onKeyDown={e=>{ if(e.key==='Enter'){ updateLane(lane.id,{name:e.target.value}); setEditingLane(null); } if(e.key==='Escape') setEditingLane(null); }}/>
                      ) : (
                        <b onClick={()=>setEditingLane(lane.id)} title="Click to rename">{lane.name}</b>
                      )}
                      <span className="g-lane-count">
                        {visibleBars.length} item{visibleBars.length!==1?"s":""}
                        {ghostCount > 0 && (
                          <span title="Items without a Monday timeline" style={{color:"var(--ink-muted)", marginLeft:6}}>
                            · {ghostCount} unscheduled
                          </span>
                        )}
                      </span>
                    </div>
                    <button className="g-lane-add" onClick={()=>openNewBar(lane.id)} title="Add task"><Icon.Plus/></button>
                  </div>
                  <div className="g-track" style={{ height: trackHeight }}>
                    <div className="g-track-grid">
                      {monthLabels.map((m, i) => (
                        <div key={i} className="g-month-cell" style={{ width: (m.days * dayWidth) + "%" }}/>
                      ))}
                    </div>
                    {packed.map((b) => {
                      // Skip bars whose entire span is outside the current viewport.
                      if (b.start + (b.len || 1) - 1 < viewStart) return null;
                      if (b.start > viewStart + viewDays - 1) return null;
                      const top = b._row * SUB_TRACK_PX + 2;
                      const height = SUB_TRACK_PX - 6;
                      const milestone = isMilestoneBar(b);
                      const narrow = !milestone && isNarrowBar(b);

                      if (milestone) {
                        return (
                          <div key={b.id} className="g-milestone" title={b.title + " · " + fmtDay(b.start)}
                               style={{ left: barLeft(b.start), top, height, bottom: "auto" }}
                               onClick={()=>openBar(lane.id, b.id)}>
                            <span className="g-milestone-dot" style={{background: b.color, boxShadow: `0 0 0 2px ${b.color}`}}/>
                            <span className="g-milestone-label">
                              {b.title}
                              <span className="g-milestone-date">{fmtDay(b.start)}</span>
                            </span>
                          </div>
                        );
                      }
                      if (narrow) {
                        // Render the bar empty, with the label pill as a sibling just to the right.
                        const extLeft = `calc(${((b.start - viewStart) + b.len) * dayWidth}% + 6px)`;
                        return (
                          <React.Fragment key={b.id}>
                            <div className={"g-bar narrow " + (b.status||"")}
                                 title={b.title + " · " + fmtRange(b.start, b.len)}
                                 style={{
                                   left: barLeft(b.start),
                                   width: barWidth(b.len),
                                   top, height, bottom: "auto",
                                   background: b.color,
                                 }}
                                 onClick={()=>openBar(lane.id, b.id)}/>
                            <div className="g-bar-extlabel"
                                 style={{ left: extLeft, top, height, bottom: "auto" }}
                                 onClick={()=>openBar(lane.id, b.id)}>
                              <span>{b.title}</span>
                              <span className="g-milestone-date">{fmtRange(b.start, b.len)}</span>
                            </div>
                          </React.Fragment>
                        );
                      }
                      // Wide bar — rectangle + a label OVERLAY positioned in the bar's *visible*
                      // portion, so when the bar extends past the viewport on either side the
                      // label stays on screen.
                      const visLeftPct  = Math.max(0,   (b.start - viewStart) * dayWidth);
                      const visRightPct = Math.min(100, (b.start - viewStart + (b.len || 1)) * dayWidth);
                      const visWidthPct = Math.max(0, visRightPct - visLeftPct);
                      return (
                        <React.Fragment key={b.id}>
                          <div className={"g-bar " + (b.status||"")}
                               title={b.title + " · " + fmtRange(b.start, b.len)}
                               style={{
                                 left: barLeft(b.start),
                                 width: barWidth(b.len),
                                 top, height, bottom: "auto",
                                 background: b.color,
                               }}
                               onClick={()=>openBar(lane.id, b.id)}/>
                          <div className="g-bar-overlay"
                               style={{
                                 left: visLeftPct + "%",
                                 width: visWidthPct + "%",
                                 top, height, bottom: "auto",
                               }}>
                            <span className="g-bar-label">{b.title}</span>
                            <span className="g-bar-date">{fmtRange(b.start, b.len)}</span>
                            {b.owner && <span className="g-bar-owner">{b.owner}</span>}
                          </div>
                        </React.Fragment>
                      );
                    })}
                  </div>
                </div>
                );
              })}
            </div>
          </div>

          <div className="gantt-legend">
            <span><span className="sw" style={{background:"var(--teal)"}}/> Active engagement</span>
            <span><span className="sw" style={{background:"var(--accent)"}}/> Deliverable</span>
            <span><span className="sw" style={{background:"var(--plum)"}}/> Planning / build</span>
            <span><span className="sw" style={{background:"var(--gold)"}}/> Event / milestone</span>
            <span><span className="sw" style={{background:"var(--red)"}}/> Overdue</span>
          </div>
        </div>
      )}

      {mode === "calendar" && (
        <div className="calendar">
          <div className="cal-head">
            <h3>
              {calStart.toLocaleDateString("en-US",{month:"long",day:"numeric"})}
              {" – "}
              {calEnd.toLocaleDateString("en-US",{month:"long",day:"numeric",year:"numeric"})}
            </h3>
            <div className="cal-head-meta">{Object.values(calendarEvents).flat().length} events plotted</div>
          </div>
          <div className="cal-grid">
            {["SUN","MON","TUE","WED","THU","FRI","SAT"].map(d=>(
              <div key={d} className="cal-dow">{d}</div>
            ))}
            {cells.map((dt, i) => {
              const key = dt.getFullYear() + "-" + dt.getMonth() + "-" + dt.getDate();
              const evts = calendarEvents[key] || [];
              const isToday = dt.getTime() === todayMidnight;
              const isWeekend = dt.getDay()===5 || dt.getDay()===6; // Fri+Sat (Israeli weekend)
              return (
                <div key={i} className={"cal-cell " + (isToday?"today ":"") + (isWeekend?"weekend":"")}>
                  <div className="cal-day">
                    <span className="cal-num">{dt.getDate()}</span>
                    {dt.getDate()===1 && <span className="cal-mo">{monthName[dt.getMonth()]}</span>}
                  </div>
                  <div className="cal-events">
                    {evts.slice(0,3).map((e, j)=>(
                      <div key={j} className={"cal-evt " + e.segment}
                           style={{ borderLeftColor:e.color }}
                           onClick={()=>openBar(e.laneId, e.barId)}>
                        <b>{e.company}</b>
                        <span>{e.title}</span>
                      </div>
                    ))}
                    {evts.length > 3 && <span className="cal-more">+{evts.length-3} more</span>}
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      )}

      {editing && editLane && (
        <BarDetailModal
          lane={editLane}
          bar={editBar}
          isNew={editing.barId === "NEW"}
          origin={origin}
          onClose={()=>setEditing(null)}
          onSave={(patch)=>{
            if(editing.barId === "NEW") addBar(editing.laneId, patch);
            else updateBar(editing.laneId, editing.barId, patch);
            setEditing(null);
          }}
          onDelete={()=>{
            deleteBar(editing.laneId, editing.barId);
            setEditing(null);
          }}
        />
      )}

      {showAddLane && (
        <NewLaneModal
          onClose={()=>setShowAddLane(false)}
          onSave={(lane)=>{ addLane(lane); setShowAddLane(false); }}
        />
      )}
    </section>
  );
}

// Event type options surfaced in the modal. Monday's existing 5 + the 3 the user added.
// Order matters: most-used first.
const EVENT_TYPES = [
  "Screening tests",
  "Private MD Kick-off",
  "General Kick-off",
  "Task",
  "Workshop / Lecture",
  "Wellness Activity",
  "Fitness Activity",
  "Special Event",
];
const CALENDLY_OPTIONS = ["Exam", "Exams", "Doc1"];

// ===== Detail / edit modal for a bar =====
function BarDetailModal({ lane, bar, isNew, origin, onClose, onSave, onDelete }){
  // Default new-bar status depends on whether the lane syncs to Monday.
  const isLive = lane && lane.segment !== "int";
  const defaultStatus = isLive ? "Scheduled" : "planned";
  const [form, setForm] = React.useState(bar || {
    title:"", start:10, len:7,
    status: defaultStatus, color: STATUS_COLORS[defaultStatus] || "var(--accent)",
    owner:"", note:"",
    type:"", mdsAssigned:"", frontDesk:"", calendly:"",
  });
  React.useEffect(()=>{
    const onKey = (e) => { if(e.key==='Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    return ()=>window.removeEventListener('keydown', onKey);
  }, [onClose]);

  const set = (k, v) => setForm({...form, [k]:v});
  const setStatus = (s) => setForm({...form, status:s, color:STATUS_COLORS[s] || form.color});

  // Convert day offset -> readable date using the dynamic origin.
  const _origin = origin || new Date();
  const dayLabel = (offset) => {
    const dt = new Date(_origin);
    dt.setDate(dt.getDate() + offset);
    return dt.toLocaleDateString("en-US",{month:"short",day:"numeric"});
  };

  const statusOptions = isLive
    ? MONDAY_STATUSES.map(label => ({ k: label, label }))
    : [
        {k:"in-progress", label:"Active"},
        {k:"planned", label:"Planned"},
        {k:"upcoming", label:"Upcoming"},
        {k:"event", label:"Event"},
        {k:"milestone", label:"Milestone"},
        {k:"overdue", label:"Overdue"},
      ];

  return (
    <React.Fragment>
      <div className="modal-scrim on" onClick={onClose}/>
      <div className="modal bar-modal on">
        <div className="bar-modal-head" style={{background:`linear-gradient(135deg, ${form.color}, color-mix(in oklab, ${form.color} 70%, var(--ink)))`}}>
          <div className="bar-modal-eyebrow">
            <span>{lane.segment.toUpperCase()}</span>
            <span>·</span>
            <span>{lane.name}</span>
            {!isNew && <span className="bar-modal-id">#{bar.id}</span>}
          </div>
          <input className="bar-modal-title"
                 value={form.title}
                 placeholder="Untitled task"
                 onChange={e=>set("title", e.target.value)}/>
          <button className="bar-modal-close" onClick={onClose}><Icon.Close/></button>
        </div>

        <div className="bar-modal-body">
          <div className="bf-row">
            <div className="bf-field">
              <label>Status</label>
              <div className="bf-status-pick">
                {statusOptions.map(s => (
                  <button key={s.k}
                          className={"bf-status-chip " + (form.status===s.k?"on":"")}
                          style={form.status===s.k?{background:STATUS_COLORS[s.k], color:"#fff", borderColor:STATUS_COLORS[s.k]}:{}}
                          onClick={()=>setStatus(s.k)}>
                    <span className="dot" style={{background:STATUS_COLORS[s.k]}}/>
                    {s.label}
                  </button>
                ))}
              </div>
            </div>
          </div>

          <div className="bf-row two">
            <div className="bf-field">
              <label>Start</label>
              <div className="bf-date-line">
                <input type="range" min="-30" max="365" value={form.start}
                       onChange={e=>set("start", parseInt(e.target.value))}/>
                <b>{dayLabel(form.start)}</b>
              </div>
            </div>
            <div className="bf-field">
              <label>Duration ({form.len} day{form.len!==1?"s":""})</label>
              <div className="bf-date-line">
                <input type="range" min="1" max="120" value={form.len}
                       onChange={e=>set("len", parseInt(e.target.value))}/>
                <b>→ {dayLabel(form.start + form.len)}</b>
              </div>
            </div>
          </div>

          {isLive && (
            <div className="bf-row">
              <div className="bf-field">
                <label>Event type</label>
                <select value={form.type||""} onChange={e=>set("type", e.target.value)}
                        style={{width:"100%", padding:"8px 10px", borderRadius:8, border:"1px solid var(--line)", background:"var(--bg-card)"}}>
                  <option value="">— pick a type —</option>
                  {EVENT_TYPES.map(t => <option key={t} value={t}>{t}</option>)}
                </select>
              </div>
            </div>
          )}

          <div className="bf-row two">
            <div className="bf-field">
              <label>Team member in charge {isLive && form.owner==="" && <span style={{color:"var(--ink-muted)",fontSize:11}}>· edit on Monday</span>}</label>
              <input value={form.owner||""} placeholder={isLive ? "(set on Monday)" : "e.g. Dr. Yohai"}
                     disabled={isLive}
                     onChange={e=>set("owner", e.target.value)}/>
            </div>
            <div className="bf-field">
              <label>MDs / service providers assigned</label>
              <input value={form.mdsAssigned||""} placeholder="e.g. Dr. Boris + Dr. Mirit; Pilates tutor X"
                     onChange={e=>set("mdsAssigned", e.target.value)}/>
            </div>
          </div>

          <div className="bf-row two">
            <div className="bf-field">
              <label>Front desk employee</label>
              <input value={form.frontDesk||""} placeholder="Name"
                     onChange={e=>set("frontDesk", e.target.value)}/>
            </div>
            <div className="bf-field">
              <label>Calendly account</label>
              <input list="calendly-options" value={form.calendly||""}
                     placeholder="Exam / Exams / Doc1 / …"
                     onChange={e=>set("calendly", e.target.value)}/>
              <datalist id="calendly-options">
                {CALENDLY_OPTIONS.map(c => <option key={c} value={c}/>)}
              </datalist>
            </div>
          </div>

          <div className="bf-row">
            <div className="bf-field">
              <label>Technical details</label>
              <textarea value={form.note||""}
                        placeholder="Equipment, prep, room setup, links, blockers…"
                        onChange={e=>set("note", e.target.value)}
                        rows={4}/>
            </div>
          </div>

          {!isNew && isLive && bar && /^\d+$/.test(String(bar.id)) && (
            <TasksSection parentId={bar.id}/>
          )}

          {!isNew && bar && (
            <div className="bf-activity">
              <h5>Activity</h5>
              <div className="bf-act">
                <span className="bf-act-dot"/>
                <div>
                  <b>Item created</b>
                  <span>By {bar.owner || "—"} · 12 days ago</span>
                </div>
              </div>
              <div className="bf-act">
                <span className="bf-act-dot doing"/>
                <div>
                  <b>Status set to {bar.status}</b>
                  <span>2 days ago</span>
                </div>
              </div>
            </div>
          )}
        </div>

        <div className="bar-modal-foot">
          {!isNew && <button className="btn-danger" onClick={onDelete}><Icon.Trash/> Delete</button>}
          <div className="bar-modal-foot-right">
            <button className="btn-ghost" onClick={onClose}>Cancel</button>
            <button className="btn-primary" onClick={()=>onSave(form)} disabled={!form.title.trim()}>
              {isNew ? "Add to ledger" : "Save changes"}
            </button>
          </div>
        </div>
      </div>
    </React.Fragment>
  );
}

// ===== Tasks (subitems) section for the bar modal =====
const TASK_STATUSES = ["Working on it", "Done", "Stuck"];
const TASK_CATEGORIES = ["📊 מעקב", "📣 תקשורת", "🚛 לוגיסטיקה", "⚡ ביצוע"];
const TASK_PRIORITIES = ["🔴 דחוף", "🟡 גבוה", "⚪ רגיל"];
const TASK_STATUS_COLOR = {
  "Working on it": "var(--gold)",
  "Done":          "var(--green)",
  "Stuck":         "var(--red)",
};

function TasksSection({ parentId }){
  const [state, setState] = React.useState({ loading: true, tasks: [], users: [], err: null });
  const [draftTitle, setDraftTitle] = React.useState("");
  const [busyId, setBusyId] = React.useState(null);

  const load = React.useCallback(async () => {
    try {
      const j = await window.tasksApi.list(parentId);
      setState({ loading: false, tasks: j.tasks || [], users: j.users || [], err: null });
    } catch (e) {
      setState(s => ({ ...s, loading: false, err: e.message }));
    }
  }, [parentId]);

  React.useEffect(() => { load(); }, [load]);

  const usersById = React.useMemo(() => {
    const m = new Map();
    for (const u of state.users) m.set(String(u.id), u);
    return m;
  }, [state.users]);

  // Track each task's last-saved title so we only push title changes on blur if the user actually edited.
  const savedTitlesRef = React.useRef(new Map());
  React.useEffect(() => {
    const m = savedTitlesRef.current;
    for (const t of state.tasks) if (!m.has(t.id)) m.set(t.id, t.title);
  }, [state.tasks]);

  const update = async (taskId, patch) => {
    // Optimistic: update locally then push to Monday.
    setState(s => ({ ...s, tasks: s.tasks.map(t => t.id === taskId ? { ...t, ...patch } : t) }));
    setBusyId(taskId);
    try {
      await window.tasksApi.update(taskId, patch);
      if (patch.title != null) savedTitlesRef.current.set(taskId, patch.title);
    }
    catch (e) { alert("Task update failed: " + e.message); await load(); }
    finally { setBusyId(null); }
  };

  const commitTitle = (taskId, newTitle) => {
    const saved = savedTitlesRef.current.get(taskId);
    if (newTitle === saved) return; // unchanged → don't bother Monday
    update(taskId, { title: newTitle });
  };

  const remove = async (taskId) => {
    if (!confirm("Delete this task?")) return;
    setState(s => ({ ...s, tasks: s.tasks.filter(t => t.id !== taskId) }));
    try { await window.tasksApi.remove(taskId); }
    catch (e) { alert("Task delete failed: " + e.message); await load(); }
  };

  const create = async () => {
    const title = draftTitle.trim();
    if (!title) return;
    setDraftTitle("");
    try {
      await window.tasksApi.create({ parent: parentId, title, status: "Working on it", priority: "⚪ רגיל" });
      await load();
    } catch (e) { alert("Task create failed: " + e.message); }
  };

  if (state.loading) {
    return (
      <div className="bf-tasks">
        <h5>Tasks <span className="bf-tasks-count">loading…</span></h5>
      </div>
    );
  }
  if (state.err) {
    return (
      <div className="bf-tasks">
        <h5>Tasks</h5>
        <div className="bf-tasks-empty">Couldn't load tasks: {state.err}</div>
      </div>
    );
  }

  const done = state.tasks.filter(t => t.status === "Done").length;
  const total = state.tasks.length;

  return (
    <div className="bf-tasks">
      <h5>
        Tasks
        {total > 0 && <span className="bf-tasks-count">{done}/{total} done</span>}
      </h5>
      {total === 0 && <div className="bf-tasks-empty">No tasks yet. Add one below.</div>}
      {state.tasks.map(t => {
        const owner = t.ownerIds && t.ownerIds[0] ? usersById.get(t.ownerIds[0]) : null;
        return (
          <div key={t.id} className={"bf-task " + (t.status === "Done" ? "done " : "") + (busyId === t.id ? "busy" : "")}>
            <input className="bf-task-title"
                   value={t.title}
                   onChange={e=>setState(s=>({...s, tasks: s.tasks.map(x=>x.id===t.id?{...x,title:e.target.value}:x)}))}
                   onBlur={e => commitTitle(t.id, e.target.value)}
                   onKeyDown={e => { if (e.key === "Enter") e.currentTarget.blur(); }}/>
            <select className="bf-task-status"
                    value={t.status || ""}
                    style={{borderColor: TASK_STATUS_COLOR[t.status] || "var(--line)"}}
                    onChange={e => update(t.id, { status: e.target.value })}>
              <option value="">—</option>
              {TASK_STATUSES.map(s => <option key={s} value={s}>{s}</option>)}
            </select>
            <input className="bf-task-date" type="date"
                   value={t.date || ""}
                   onChange={e => update(t.id, { date: e.target.value })}/>
            <select className="bf-task-cat"
                    value={t.category || ""}
                    onChange={e => update(t.id, { category: e.target.value })}>
              <option value="">— category —</option>
              {TASK_CATEGORIES.map(c => <option key={c} value={c}>{c}</option>)}
            </select>
            <select className="bf-task-pri"
                    value={t.priority || ""}
                    onChange={e => update(t.id, { priority: e.target.value })}>
              <option value="">— priority —</option>
              {TASK_PRIORITIES.map(p => <option key={p} value={p}>{p}</option>)}
            </select>
            <select className="bf-task-owner"
                    value={(t.ownerIds && t.ownerIds[0]) || ""}
                    onChange={e => update(t.id, { ownerIds: e.target.value ? [e.target.value] : [] })}
                    title={owner ? owner.name : "Unassigned"}>
              <option value="">— owner —</option>
              {state.users.map(u => <option key={u.id} value={u.id}>{u.name}</option>)}
            </select>
            <button className="bf-task-del" onClick={()=>remove(t.id)} title="Delete">×</button>
          </div>
        );
      })}
      <div className="bf-task-add">
        <input value={draftTitle}
               placeholder="+ Add a task and press Enter"
               onChange={e=>setDraftTitle(e.target.value)}
               onKeyDown={e => { if (e.key === "Enter") create(); }}/>
        <button className="btn-ghost" onClick={create} disabled={!draftTitle.trim()}>Add</button>
      </div>
    </div>
  );
}

// ===== New lane modal =====
function NewLaneModal({ onClose, onSave }){
  const [name, setName] = React.useState("");
  const [segment, setSegment] = React.useState("md");
  React.useEffect(()=>{
    const onKey = (e) => { if(e.key==='Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    return ()=>window.removeEventListener('keydown', onKey);
  }, [onClose]);
  return (
    <React.Fragment>
      <div className="modal-scrim on" onClick={onClose}/>
      <div className="modal lane-modal on">
        <div className="lane-modal-head">
          <h3>New ledger lane</h3>
          <button className="bar-modal-close" onClick={onClose}><Icon.Close/></button>
        </div>
        <div className="bar-modal-body">
          <div className="bf-field">
            <label>Lane name</label>
            <input autoFocus value={name} placeholder="e.g. Acme Corp, Marketing, Compliance"
                   onChange={e=>setName(e.target.value)}/>
          </div>
          <div className="bf-field">
            <label>Segment</label>
            <div className="bf-status-pick">
              {[
                {k:"md", label:"MD Services", color:"var(--accent)"},
                {k:"mgmt", label:"Management", color:"var(--teal)"},
                {k:"int", label:"Internal", color:"var(--plum)"},
              ].map(s => (
                <button key={s.k}
                        className={"bf-status-chip " + (segment===s.k?"on":"")}
                        style={segment===s.k?{background:s.color,color:"#fff",borderColor:s.color}:{}}
                        onClick={()=>setSegment(s.k)}>
                  <span className="dot" style={{background:s.color}}/>
                  {s.label}
                </button>
              ))}
            </div>
          </div>
        </div>
        <div className="bar-modal-foot">
          <div className="bar-modal-foot-right">
            <button className="btn-ghost" onClick={onClose}>Cancel</button>
            <button className="btn-primary" onClick={()=>onSave({name, segment})} disabled={!name.trim()}>Create lane</button>
          </div>
        </div>
      </div>
    </React.Fragment>
  );
}

// ===== Mobile alternative for the Gantt: vertical date-grouped list =====
function GanttMobileList({ lanes, origin, onOpen }){
  const today = new Date();
  today.setHours(0,0,0,0);
  const todayOffset = Math.round((today - origin) / 86400000);

  const allBars = [];
  lanes.forEach(lane => {
    lane.bars.forEach(b => {
      if (b._ghost) return;
      allBars.push({ ...b, _lane: lane });
    });
  });
  allBars.sort((a, b) => a.start - b.start);

  const buckets = {
    overdue: { label: "In flight", items: [] },
    today:   { label: "Today",     items: [] },
    week:    { label: "This week", items: [] },
    month:   { label: "Coming up", items: [] },
    later:   { label: "Later",     items: [] },
  };

  allBars.forEach(b => {
    const start = b.start;
    const end = start + (b.len || 1) - 1;
    if (end < todayOffset) {
      if (b.status === "In Progress" || b.status === "On Hold") buckets.overdue.items.push(b);
      return;
    }
    if (start <= todayOffset && end >= todayOffset) { buckets.today.items.push(b); return; }
    if (start <= todayOffset + 7)  { buckets.week.items.push(b);  return; }
    if (start <= todayOffset + 30) { buckets.month.items.push(b); return; }
    buckets.later.items.push(b);
  });

  const fmtDate = (offset) => {
    const dt = new Date(origin);
    dt.setDate(dt.getDate() + offset);
    return dt.toLocaleDateString("en-US", { month: "short", day: "numeric" });
  };
  const fmtRange = (start, len) => {
    if ((len||1) <= 1) return fmtDate(start);
    return fmtDate(start) + " → " + fmtDate(start + (len||1) - 1);
  };

  const order = ["overdue", "today", "week", "month", "later"];
  const anyItems = order.some(k => buckets[k].items.length);

  return (
    <div className="gantt-mobile">
      {!anyItems && (
        <div className="gm-empty">
          <div className="mono" style={{color:"var(--ink-muted)", marginBottom:8}}>NOTHING SCHEDULED</div>
          <p style={{color:"var(--ink-soft)"}}>No upcoming events to show in this segment.</p>
        </div>
      )}
      {order.map(key => {
        const bucket = buckets[key];
        if (!bucket.items.length) return null;
        return (
          <div key={key} className="gm-group">
            <div className="gm-group-head">
              <span className="gm-group-title">{bucket.label}</span>
              <span className="gm-group-count">{bucket.items.length}</span>
            </div>
            <div className="gm-list">
              {bucket.items.map(b => (
                <button
                  key={b._lane.id + "-" + b.id}
                  className={"gm-bar seg-" + (b._lane.segment || "int")}
                  type="button"
                  onClick={()=>onOpen(b._lane.id, b.id)}
                >
                  <span className="gm-stripe" style={{ background: b.color || "var(--accent)" }}/>
                  <div className="gm-body">
                    <div className="gm-row1">
                      <span className="gm-lane">{b._lane.name}</span>
                      {b.status && <span className="gm-status">{b.status}</span>}
                    </div>
                    <div className="gm-title">{b.title || "Untitled"}</div>
                    <div className="gm-meta">
                      <span className="gm-dates">{fmtRange(b.start, b.len)}</span>
                      {b.owner && <span className="gm-owner"> · {b.owner}</span>}
                    </div>
                  </div>
                </button>
              ))}
            </div>
          </div>
        );
      })}
    </div>
  );
}

Object.assign(window, { GanttCalendar, BarDetailModal, TasksSection, GanttMobileList });
