// Progression — a draggable 2x2 board per sub-team. Cards (project + company count)
// move across: My Court/Lists (white) → Outbound/Lists (yellow) → My Court/Mailings
// (orange) → Outbound/Mailings (green). The longer a card sits in a quadrant, the more
// vibrant its color. Cards seed from R.A.M. Research Goals; shared per sub-team board.
(function () {
  const { useState, useEffect, useMemo, useCallback } = React;
  const F = () => window.VAULT_FIRM || {};
  const BLUE = "#196EA7", INK = "#1A2733", INK2 = "#5A6B7B", MUTE = "#93A1B0", LINE = "#E4E9F0";

  // quadrant key = `${col}:${row}`. Base hue per quadrant; intensity by age (days).
  const QUAD = {
    "court:lists":     { name: "Active Research",    base: "#FFFFFF", deep: "#E9ECEF", ink: "#1A2733" }, // white→grey
    "outbound:lists":  { name: "List Sent",          base: "#FFF4C2", deep: "#F2C744", ink: "#5A4A12" }, // yellow
    "court:mailings":  { name: "Backlogged Mailings",base: "#FFE0B8", deep: "#EE9B3A", ink: "#6B3A12" }, // orange
    "outbound:mailings":{name: "Outbound Mailings",  base: "#CDE7C4", deep: "#4CAF50", ink: "#1E4620" }, // green
  };
  // blend base→deep by age: 0 days = base, capped at ~21 days = deep
  function hexBlend(a, b, t) {
    const pa = [parseInt(a.slice(1, 3), 16), parseInt(a.slice(3, 5), 16), parseInt(a.slice(5, 7), 16)];
    const pb = [parseInt(b.slice(1, 3), 16), parseInt(b.slice(3, 5), 16), parseInt(b.slice(5, 7), 16)];
    const r = pa.map((c, i) => Math.round(c + (pb[i] - c) * t));
    return "#" + r.map(c => c.toString(16).padStart(2, "0")).join("");
  }
  function ageDays(enteredAt) {
    if (!enteredAt) return 0;
    return Math.max(0, (Date.now() - new Date(enteredAt).getTime()) / 86400000);
  }
  function cardColor(card) {
    const q = QUAD[`${card.col}:${card.row}`] || QUAD["court:lists"];
    return { bg: hexBlend(q.base, q.deep, 0.28), ink: q.ink, days: 0 };
  }

  function fmtWeekLabel(s) {
    if (!s) return "";
    const d = new Date(s + "T00:00:00");
    return isNaN(d) ? s : d.toLocaleDateString("en-US", { month: "short", day: "numeric" });
  }

  function ProgressionScreen({ user }) {
    const [boards, setBoards] = useState([]);     // [{planId, label, members}]
    const [activeBoard, setActiveBoard] = useState(null);
    const [cards, setCards] = useState([]);
    const [loading, setLoading] = useState(true);
    const [dragId, setDragId] = useState(null);
    const [weekStart, setWeekStart] = useState(() => mondayOf(new Date()));

    const moduleHandle = useMemo(() => {
      // Resolve the viewer's MODULE handle (the MD's id), not the viewer's own id.
      // The old version passed the viewer's pid as the module, which only worked
      // for the MD himself — everyone else got zero plans/boards (and the + Add
      // composer failed with no planId). 2026-07-08.
      const F2 = window.VAULT_FIRM || {};
      let pid = (user && (user.personId || user.id)) || "";
      pid = String(pid).replace(/^p_/, "").replace(/_gmail$/, "");
      const p = (F2.PEOPLE_BY_ID || {})[pid];
      let st = p && p.subteam;                                              // "st-bscott" for branch members
      if (!st) { const led = (F2.SUBTEAMS || []).find(t => t.lead === pid); st = led && led.id; }  // MDs lead their branch
      return st ? st.replace(/^st-/, "") : (pid || "bscott");
    }, [user]);

    // discover the sub-team boards from research plans
    useEffect(() => {
      let alive = true;
      (async () => {
        setLoading(true);
        try {
          const plans = await window.VaultAPI.listResearchPlans(moduleHandle);
          const bs = (plans || []).map(p => ({ planId: p.id, label: p.label || "Sub-team", members: p.ownerIds || [] }));
          if (!alive) return;
          setBoards(bs);
          setActiveBoard(bs.length ? bs[0].planId : null);
        } catch (e) { if (alive) setBoards([]); }
        setLoading(false);
      })();
      return () => { alive = false; };
    }, [moduleHandle]);

    // Cards are now a live view of the team's research_goals for the selected week,
    // bucketed into quadrants by section + sent-flag. No separate progression_cards.
    //   research  -> Active Research (court:lists)
    //   approval  -> Lists Sent      (outbound:lists)
    //   mailings  -> Backlogged (court:mailings) when unsent / Outbound (outbound:mailings) when sent
    const loadCards = useCallback(async (planId, wk) => {
      if (!planId || !wk) { setCards([]); return; }
      const goals = await window.VaultAPI.listResearchGoals(moduleHandle, wk).catch(() => []);
      // Stage-gated funnel. A company advances only when its current stage is checked:
      //   Research (unchecked) → Active Research
      //   research done → Approval (unchecked) → Lists Sent
      //   approval done → Mailings (unsent) → Backlogged; Mailings (sent) → Outbound
      // A company sits at its EARLIEST present-and-unchecked stage; if every present stage
      // is checked, it rests at its furthest present stage. One card per company.
      const groups = new Map();
      (goals || []).forEach(g => {
        if (g.planId !== planId) return;
        if (g.section !== "research" && g.section !== "approval" && g.section !== "mailings") return;
        const name = (g.projectName || "").trim();
        if (!name) return;
        const key = name.toLowerCase();
        let grp = groups.get(key);
        if (!grp) { grp = { name, research: [], approval: [], mailings: [] }; groups.set(key, grp); }
        grp[g.section].push(g);
      });
      const cards = [];
      groups.forEach(grp => {
        const present = s => grp[s].length > 0;
        const allDone = s => grp[s].length > 0 && grp[s].every(r => !!r.done);
        let quad;
        if (present("research") && !allDone("research")) quad = "court:lists";
        else if (present("approval") && !allDone("approval")) quad = "outbound:lists";
        else if (present("mailings")) quad = allDone("mailings") ? "outbound:mailings" : "court:mailings";
        else if (present("approval")) quad = "outbound:lists";
        else if (present("research")) quad = "court:lists";
        else return;
        const [col, row] = quad.split(":");
        const placedSec = row === "mailings" ? "mailings" : (col === "court" ? "research" : "approval");
        const rep = grp[placedSec][0] || grp.research[0] || grp.approval[0] || grp.mailings[0];
        cards.push({ id: rep.id, goalId: rep.id, project: grp.name, col, row,
          companyCount: rep && rep.targetCount != null ? rep.targetCount : "",
          researchIds: grp.research.map(r => r.id),
          approvalIds: grp.approval.map(r => r.id),
          mailingIds: grp.mailings.map(r => r.id) });
      });
      setCards(cards);
    }, [moduleHandle]);

    useEffect(() => { if (activeBoard) loadCards(activeBoard, weekStart); }, [activeBoard, weekStart, loadCards]);

    function mondayOf(date) {
      const d = (date instanceof Date) ? new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())) : new Date(date + "T00:00:00Z");
      const day = (d.getUTCDay() + 6) % 7;
      d.setUTCDate(d.getUTCDate() - day); return d.toISOString().slice(0, 10);
    }
    function shiftWeek(weekStartStr, deltaWeeks) {
      const d = new Date(weekStartStr + "T00:00:00Z"); d.setUTCDate(d.getUTCDate() + deltaWeeks * 7);
      return d.toISOString().slice(0, 10);
    }

    const [dragOverQuad, setDragOverQuad] = useState(null);

    // Stage order: Active Research(0) → Lists Sent(1) → Backlogged(2) → Outbound(3).
    // Dragging one stage flips the boundary checkbox:
    //   0↔1 research done · 1↔2 approval (list sent) · 2↔3 mailing sent
    function stageIndex(col, row) {
      if (row === "lists") return col === "court" ? 0 : 1;
      return col === "court" ? 2 : 3;
    }
    async function dropInto(col, row) {
      if (dragId == null) return;
      const card = cards.find(c => c.id === dragId);
      setDragId(null); setDragOverQuad(null);
      if (!card) return;
      const from = stageIndex(card.col, card.row);
      const to = stageIndex(col, row);
      if (from === to) return;
      if (Math.abs(from - to) !== 1) { window.VaultUI.toast("info", "Move one stage at a time."); return; }
      // the section whose checkbox this boundary toggles
      const boundary = Math.min(from, to); // 0 research · 1 approval · 2 mailing
      const boxIds = boundary === 0 ? card.researchIds : boundary === 1 ? card.approvalIds : card.mailingIds;
      // the section the card must have a row in to actually land at the target
      const targetIds = to === 0 ? card.researchIds : to === 1 ? card.approvalIds : card.mailingIds;
      if (!targetIds || targetIds.length === 0) {
        window.VaultUI.toast("info", "This company has no row for that stage — add it on Weekly Goals first.");
        return;
      }
      const advancing = to > from;
      const patch = boundary === 2
        ? { done: advancing, doneDate: advancing ? new Date().toISOString().slice(0, 10) : null }
        : { done: advancing };
      setCards(cs => cs.map(c => c.id === card.id ? { ...c, col, row } : c)); // optimistic
      try { await Promise.all((boxIds || []).map(id => window.VaultAPI.updateResearchGoal(id, patch))); } catch (e) {}
      loadCards(activeBoard, weekStart);
    }
    async function setCount(card, val) {
      const n = parseInt(String(val).replace(/[^0-9]/g, ""), 10) || 0;
      setCards(cs => cs.map(c => c.id === card.id ? { ...c, companyCount: n } : c));
      try { await window.VaultAPI.updateProgressionCard(card.id, { companyCount: n }); } catch (e) {}
    }
    // Per-quadrant add — creates a real Weekly Goals row on this board's plan,
    // in the section whose stage-gating lands the company in the clicked
    // quadrant. Replaces the retired progression_cards add-modal: adding here
    // and adding on the Weekly Goals screen are now the same act.
    const [addingQuad, setAddingQuad] = useState(null); // "col:row" | null
    const QUAD_NEW_ROW = {
      "court:lists":       { section: "research", done: false },
      "outbound:lists":    { section: "approval", done: false },
      "court:mailings":    { section: "mailings", done: false },
      "outbound:mailings": { section: "mailings", done: true  },
    };
    async function addFromQuad(key, name, count) {
      name = (name || "").trim();
      if (!name || !activeBoard) return;
      const spec = QUAD_NEW_ROW[key];
      if (!spec) return;
      const existing = await window.VaultAPI.listResearchGoals(moduleHandle, weekStart).catch(() => []);
      const rank = existing.filter(g => g.planId === activeBoard && g.section === spec.section).length + 1;
      await window.VaultAPI.createResearchGoal({
        module: moduleHandle, weekStart: weekStart, section: spec.section, rank: rank,
        planId: activeBoard, projectName: name,
        targetCount: count !== "" && count != null ? (parseInt(String(count).replace(/[^0-9]/g, ""), 10) || null) : null,
        done: spec.done,
      });
      setAddingQuad(null);
      loadCards(activeBoard, weekStart);
    }
    async function removeCard(card) {
      setCards(cs => cs.filter(c => c.id !== card.id));
      try { await window.VaultAPI.deleteProgressionCard(card.id); } catch (e) {}
    }

    const quadrant = (col, row) => cards.filter(c => c.col === col && c.row === row);
    const mlabel = { fontSize: 11, fontWeight: 600, color: MUTE, textTransform: "uppercase", letterSpacing: ".04em", marginBottom: 6 };
    const minp = { width: "100%", padding: "9px 11px", borderRadius: 8, border: `1px solid ${LINE}`, fontSize: 13, color: INK, background: "#fff", boxSizing: "border-box" };

    function Cell({ col, row }) {
      const q = QUAD[`${col}:${row}`];
      const items = quadrant(col, row);
      const key = `${col}:${row}`;
      const isTarget = dragOverQuad === key && dragId != null;
      return (
        <div
          onDragOver={e => { e.preventDefault(); if (dragOverQuad !== key) setDragOverQuad(key); }}
          onDragLeave={e => { if (e.currentTarget === e.target) setDragOverQuad(cur => cur === key ? null : cur); }}
          onDrop={() => dropInto(col, row)}
          style={{ flex: 1, minHeight: 200, border: isTarget ? `2px dashed ${BLUE}` : `1px solid ${LINE}`, borderRadius: 12, background: isTarget ? "#EAF3FB" : "#FAFBFC", padding: 12, display: "flex", flexDirection: "column", gap: 8, transition: "background .12s, border-color .12s" }}>
          <div style={{ display: "flex", alignItems: "center", marginBottom: 2 }}>
            <div style={{ fontSize: 11, fontWeight: 600, color: MUTE, textTransform: "uppercase", letterSpacing: ".04em" }}>{q.name} · {items.length}</div>
            <button onClick={() => setAddingQuad(a => a === key ? null : key)} title="Add a Weekly Goals row in this stage"
              style={{ marginLeft: "auto", border: "none", background: "transparent", color: BLUE, fontSize: 12, fontWeight: 700, cursor: "pointer", padding: "0 2px" }}>{addingQuad === key ? "×" : "+ Add"}</button>
          </div>
          {addingQuad === key && (
            <QuadComposer withCount={key === "court:lists"} line={LINE} ink={INK} blue={BLUE}
              onSubmit={(name, count) => addFromQuad(key, name, count)} onCancel={() => setAddingQuad(null)}/>
          )}
          {items.map(card => {
            const cc = cardColor(card);
            const dragging = dragId === card.id;
            return (
              <div key={card.id} draggable onDragStart={() => setDragId(card.id)} onDragEnd={() => { setDragId(null); setDragOverQuad(null); }}
                style={{ background: cc.bg, color: cc.ink, border: `1px solid rgba(0,0,0,.12)`, borderRadius: 10, padding: "9px 11px", cursor: "grab", boxShadow: "0 1px 2px rgba(0,0,0,.06)", opacity: dragging ? 0.4 : 1, transition: "opacity .12s" }}>
                <div style={{ fontSize: 13.5, fontWeight: 600, lineHeight: 1.2 }}>{card.project}</div>
                {card.companyCount !== "" && card.companyCount != null && (
                  <div style={{ display: "flex", alignItems: "center", gap: 6, marginTop: 6 }}>
                    <span style={{ fontSize: 12, fontWeight: 600 }}>List {card.companyCount}</span>
                  </div>
                )}
              </div>
            );
          })}
          {items.length === 0 && <div style={{ fontSize: 12, color: MUTE, fontStyle: "italic", paddingTop: 8 }}>{isTarget ? "Drop here" : "—"}</div>}
        </div>
      );
    }

    return (
      <div style={{ maxWidth: 1120, margin: "0 auto" }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 16, flexWrap: "wrap", gap: 10 }}>
          {/* board picker — Director sees all; members see theirs */}
          <div style={{ display: "inline-flex", gap: 2, background: "#EEF2F6", borderRadius: 10, padding: 3, flexWrap: "wrap" }}>
            {boards.map(b => (
              <button key={b.planId} onClick={() => setActiveBoard(b.planId)} style={{ padding: "7px 14px", borderRadius: 8, border: "none", cursor: "pointer", fontSize: 13, fontWeight: 500, background: activeBoard === b.planId ? "#fff" : "transparent", color: activeBoard === b.planId ? BLUE : INK2, boxShadow: activeBoard === b.planId ? "0 1px 2px rgba(0,0,0,.06)" : "none" }}>{b.label}</button>
            ))}
          </div>
          <div style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
            <button onClick={() => setWeekStart(w => shiftWeek(w, -1))} title="Previous week"
              style={{ width: 30, height: 30, borderRadius: 8, border: `1px solid ${LINE}`, background: "#fff", color: INK2, cursor: "pointer", fontSize: 15, lineHeight: 1 }}>‹</button>
            <span style={{ fontSize: 13, fontWeight: 600, color: INK, minWidth: 96, textAlign: "center" }}>Week of {fmtWeekLabel(weekStart)}</span>
            <button onClick={() => setWeekStart(w => shiftWeek(w, 1))} title="Next week"
              style={{ width: 30, height: 30, borderRadius: 8, border: `1px solid ${LINE}`, background: "#fff", color: INK2, cursor: "pointer", fontSize: 15, lineHeight: 1 }}>›</button>
            <button onClick={() => setWeekStart(mondayOf(new Date()))}
              style={{ height: 30, padding: "0 12px", borderRadius: 8, border: `1px solid ${LINE}`, background: "#fff", color: INK2, cursor: "pointer", fontSize: 12, fontWeight: 500 }}>This week</button>
            <button onClick={() => loadCards(activeBoard, weekStart)} title="Reload from Weekly Goals"
              style={{ height: 30, padding: "0 12px", borderRadius: 8, border: `1px solid ${LINE}`, background: "#fff", color: BLUE, cursor: "pointer", fontSize: 12, fontWeight: 600 }}>Refresh</button>
          </div>
        </div>

        {loading ? <div style={{ color: MUTE, padding: 40, textAlign: "center" }}>Loading…</div> : (
          <div>
            {/* column headers */}
            <div style={{ display: "flex", gap: 12, marginBottom: 8, paddingLeft: 76 }}>
              <div style={{ flex: 1, textAlign: "center", fontSize: 13, fontWeight: 700, color: INK }}>My Court</div>
              <div style={{ flex: 1, textAlign: "center", fontSize: 13, fontWeight: 700, color: INK }}>Outbound</div>
            </div>
            {/* Lists row */}
            <div style={{ display: "flex", gap: 12, marginBottom: 12, alignItems: "stretch" }}>
              <div style={{ width: 64, display: "flex", alignItems: "center", justifyContent: "center", fontSize: 13, fontWeight: 700, color: INK, writingMode: "vertical-rl", transform: "rotate(180deg)" }}>Lists</div>
              <Cell col="court" row="lists" /><Cell col="outbound" row="lists" />
            </div>
            {/* Mailings row */}
            <div style={{ display: "flex", gap: 12, alignItems: "stretch" }}>
              <div style={{ width: 64, display: "flex", alignItems: "center", justifyContent: "center", fontSize: 13, fontWeight: 700, color: INK, writingMode: "vertical-rl", transform: "rotate(180deg)" }}>Mailings</div>
              <Cell col="court" row="mailings" /><Cell col="outbound" row="mailings" />
            </div>
            <p style={{ fontSize: 11.5, color: MUTE, marginTop: 16, lineHeight: 1.5 }}>
              This board mirrors the team's Weekly Goals and syncs both ways. Drag a card one stage left/right to advance or pull it back — it checks the matching box on Weekly Goals (research done → list sent → mailing sent). A company needs a row in the next stage's section to move into it; use + Add in a quadrant (or the Weekly Goals screen) to create one.
            </p>
          </div>
        )}

      </div>
    );
  }

  // Hoisted to module scope with internal state: the quadrant Cell is an
  // inline component, so parent-held input state would remount-and-blur on
  // every keystroke. Internal state keeps typing local and focus stable.
  function QuadComposer({ withCount, onSubmit, onCancel, line, ink, blue }) {
    const [name, setName] = useState("");
    const [count, setCount] = useState("");
    const inp = { padding: "7px 9px", borderRadius: 8, border: `1px solid ${line}`, fontSize: 12.5, color: ink, background: "#fff", boxSizing: "border-box" };
    const keys = e => { if (e.key === "Enter" && name.trim()) onSubmit(name, count); if (e.key === "Escape") onCancel(); };
    return (
      <div style={{ display: "flex", gap: 6, alignItems: "center" }}>
        <input autoFocus value={name} onChange={e => setName(e.target.value)} onKeyDown={keys}
          placeholder="Company / project" style={{ ...inp, flex: 1, minWidth: 0 }}/>
        {withCount && (
          <input value={count} onChange={e => setCount(e.target.value)} onKeyDown={keys}
            placeholder="# targets" style={{ ...inp, width: 74 }}/>
        )}
        <button onClick={() => onSubmit(name, count)} disabled={!name.trim()}
          style={{ border: "none", borderRadius: 8, background: blue, color: "#fff", fontSize: 12, fontWeight: 700, padding: "7px 12px", cursor: name.trim() ? "pointer" : "default", opacity: name.trim() ? 1 : 0.5 }}>Add</button>
      </div>
    );
  }

  window.ProgressionScreen = ProgressionScreen;
})();