// ====================================================================
// lw-section.jsx — Last Wishes, embedded as step 6 of the PO Activation
// onboarding. Exposes useLastWishes(), a hook the main App mounts
// unconditionally. It owns the Last Wishes sub-flow state (recipient →
// theme → prep → studio → review → done) and the recorder, but borrows
// the App's SINGLE persistent Helm rail: it streams its prose into the
// shared conversation via the App's appendProgressive(), logs actions
// via the App's logActivity(), and hands the App the rail title, quick
// replies, custom card, and the stage to render. The right rail and the
// chat history therefore stay continuous from the very first screen.
// ====================================================================
const { useState: useStateLWS, useEffect: useEffectLWS, useRef: useRefLWS,
        useMemo: useMemoLWS, useCallback: useCbLWS } = React;

// Deep brand accents assigned round-robin to real beneficiaries.
const LWS_ACCENTS = ["#9888BF", "#70B6BA", "#D38D54", "#8D9933", "#C9A93C", "#B86A6A"];

function lwsInitials(name) {
  return (name || "").split(" ").filter(Boolean).map(s => s[0]).join("").slice(0, 2).toUpperCase();
}

// A relationship that is an actual person we'd record a video for —
// excludes trusts, estates, charities, and the like.
function lwsIsPerson(rel) {
  return !/trust|estate|charit|foundation|church|fund\b|organization|llc|inc\b/i.test(rel || "");
}

// We only have two stock portraits today (the policy owner's husband and
// herself), so dynamic beneficiaries borrow whichever reads right for
// their gender. Inlined as data URIs (window.HELM_PORTRAITS, from
// portraits.js) so they load instantly on every screen — no network
// request to drop or arrive late. Falls back to the served files if the
// inlined set isn't present (e.g. standalone use).
const LWS_PHOTO = (window.HELM_PORTRAITS && window.HELM_PORTRAITS.m)
  ? { m: window.HELM_PORTRAITS.m, f: window.HELM_PORTRAITS.f }
  : { m: "lw-assets/john.png", f: "lw-assets/suzie.png" };
const LWS_MALE = new Set("john james robert michael william david richard joseph thomas charles christopher daniel matthew anthony mark donald steven paul andrew joshua kenneth kevin brian george timothy ronald jason edward jeffrey ryan jacob gary nicholas eric jonathan stephen larry justin scott brandon frank benjamin samuel gregory alexander patrick jack henry tyler aaron jose adam nathan peter noah liam mason ethan logan lucas oliver ray raymond carlos luis miguel juan pedro marcus dennis jerry".split(" "));
const LWS_FEMALE = new Set("mary patricia jennifer linda elizabeth barbara susan jessica sarah karen nancy lisa betty margaret sandra ashley kimberly emily donna michelle carol amanda dorothy melissa deborah stephanie rebecca sharon laura cynthia kathleen amy angela shirley anna brenda pamela emma nicole helen samantha katherine christine debra rachel catherine carolyn janet ruth maria heather diane julie joyce victoria olivia sophia isabella ava mia abigail grace chloe sofia suzie susie sue ".split(" "));
function lwsGuessGender(first, rel) {
  const r = (rel || "").toLowerCase();
  if (/husband|father|\bson\b|brother|uncle|grandfather|grandpa|nephew|widower|\bmr\b/.test(r)) return "m";
  if (/wife|mother|daughter|sister|aunt|grandmother|grandma|niece|widow|\bmrs\b|\bms\b/.test(r)) return "f";
  const n = (first || "").toLowerCase().replace(/[^a-z]/g, "");
  if (LWS_FEMALE.has(n)) return "f";
  if (LWS_MALE.has(n)) return "m";
  if (/(a|ah|ia|elle|ette|ine)$/.test(n)) return "f";
  if (/(o|us|am)$/.test(n)) return "m";
  let h = 0; for (const ch of n) h = (h * 31 + ch.charCodeAt(0)) >>> 0;
  return h % 2 ? "m" : "f";
}
// Shared so the payout-guidance tabs pick the SAME portrait for a person.
function lwsPhotoFor(name, rel) {
  return LWS_PHOTO[lwsGuessGender((name || "").split(" ")[0], rel)];
}

// Map the PO flow's confirmed beneficiary people → Last Wishes recipients.
// { name, relationship, ... }  →  { id, name, firstName, rel, photo, initials, accent }
function lwsBenesFromPeople(people) {
  return (people || [])
    .filter(p => p && p.confirmed && lwsIsPerson(p.relationship))
    .map((p, i) => ({
      id: "lw-" + (p.name || "person").toLowerCase().replace(/[^a-z0-9]+/g, "-") + "-" + i,
      name: p.name,
      firstName: (p.name || "").split(" ")[0] || p.name,
      rel: p.relationship || "Loved one",
      photo: LWS_PHOTO[lwsGuessGender((p.name || "").split(" ")[0], p.relationship)],
      initials: lwsInitials(p.name),
      accent: LWS_ACCENTS[i % LWS_ACCENTS.length],
    }));
}

const LWS_SCREEN_TITLE = {
  intro:  "Last wishes",
  theme:  "Last wishes",
  prep:   (t) => `${t.label} · ${t.order}`,
  studio: (t) => `Recording · ${t.label}`,
  review: (t) => `${t.label} · review`,
  done:   "Last wishes",
};

// Thin top banner carrying the six-step breadcrumb (step 6 active) over
// the Last Wishes brand background, so these screens read as part of the
// same onboarding. Clicking an earlier step jumps back into the main flow.
function LWStepStrip({ onStepClick }) {
  return (
    <div style={{ flexShrink: 0, padding: "13px 32px", background: "var(--helm-dark-purple)",
      borderBottom: "1px solid rgba(254,252,245,.16)" }}>
      <window.StepBreadcrumb steps={window.HELM_STEPS} current={5} onStepClick={onStepClick} tone="light" />
    </div>
  );
}

// ── The hook ────────────────────────────────────────────────────────
function useLastWishes({ active, benes, appendProgressive, logActivity, onStepClick }) {
  const [lwScreen, setLwScreen] = useStateLWS("intro");
  const [beneId, setBeneId]     = useStateLWS(null);
  const [themeId, setThemeId]   = useStateLWS("hope");
  const [beats, setBeats]       = useStateLWS([]);
  const [segClips, setSegClips] = useStateLWS([]);
  const [saved, setSaved]       = useStateLWS({});       // { beneId: ['hope', ...] }
  const [savedVideos, setSavedVideos] = useStateLWS({}); // { beneId: { hope: {beats, segClips} } }
  const [watchVideo, setWatchVideo]   = useStateLWS(null);
  const [example, setExample]   = useStateLWS(null);
  const [studioStart, setStudioStart] = useStateLWS(0);
  const [railBump, setRailBump] = useStateLWS(0);
  const [askOpen, setAskOpen]   = useStateLWS(false); // studio: Helm overlay open?
  const streamedRef = useRefLWS(new Set());
  const rec = window.useRecorder();

  const beneById = useCbLWS((id) => (benes || []).find(b => b.id === id) || null, [benes]);
  const bene  = beneId ? beneById(beneId) : null;
  const theme = window.lwTheme(themeId);
  const suggestions = useMemoLWS(() => (bene ? window.lwPrompts(bene, themeId) : []), [bene, themeId]);
  const savedForBene = (saved[beneId] || []);

  // If the recipient list changes out from under us (rare — e.g. a reset),
  // and the selected recipient no longer exists, fall back to the intro.
  useEffectLWS(() => {
    if (beneId && !beneById(beneId)) { setBeneId(null); setLwScreen("intro"); }
  }, [benes]); // eslint-disable-line react-hooks/exhaustive-deps

  // Stream each screen's intro lines once per context, into the SHARED
  // conversation. Only while the section is active (mounted in the stage).
  useEffectLWS(() => {
    if (!active) return;
    if (lwScreen === "studio") return;  // the studio is a focused recording mode
    const key = `${lwScreen}:${beneId || "-"}:${themeId}`;
    if (streamedRef.current.has(key)) return;
    streamedRef.current.add(key);
    const lines = window.lwScriptFor(lwScreen, bene, theme, savedForBene);
    if (lines && lines.length) {
      const tagged = lines.map(m => ({ ...m, screenTag: "lastWishes" }));
      const id = setTimeout(() => appendProgressive(tagged), 240);
      return () => clearTimeout(id);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [active, lwScreen, beneId, themeId]);

  // keep segClips sized to beats
  useEffectLWS(() => {
    setSegClips((s) => beats.map((_, i) => s[i] || null));
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [beats.length]);

  // landing on review saves the current cut to the inventory
  useEffectLWS(() => {
    if (lwScreen === "review" && beneId && segClips.some(Boolean)) {
      setSavedVideos((s) => ({ ...s, [beneId]: { ...(s[beneId] || {}), [themeId]: { beats, segClips } } }));
      setSaved((s) => { const cur = s[beneId] || []; return cur.includes(themeId) ? s : { ...s, [beneId]: [...cur, themeId] }; });
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [lwScreen, segClips]);

  // ── transitions ───────────────────────────────────────────────────
  const seedBeats = (b, tid) => window.lwPrompts(b, tid).slice(0, 4).map((p, i) => ({ ...p, key: tid + "_" + i }));

  const pickBene = (id) => {
    const b = beneById(id);
    setBeneId(id);
    logActivity(`Recording for ${b ? b.name : "someone"}`, b ? b.rel : undefined, "lastWishes");
    setLwScreen("theme");
  };
  const startTheme = (tid) => {
    setThemeId(tid);
    setBeats(seedBeats(bene, tid));
    setSegClips(new Array(4).fill(null));
    setStudioStart(0);          // fresh video always records from the top
    rec.resetAll();
    logActivity(`Starting: ${window.lwTheme(tid).label}`, window.lwTheme(tid).order, "lastWishes");
    setLwScreen("prep");
  };
  // Single entry point into the studio. ALWAYS sets the starting beat
  // explicitly, so a stale studioStart from a previous session (e.g. a
  // prior "add beat" / "redo beat") can never carry over — that leak was
  // the root cause of the studio opening on the wrong beat and then
  // recording beats out of order. startIdx defaults to 0 = from the top.
  const openStudio = useCbLWS(async (startIdx = 0) => {
    setStudioStart(Math.max(0, startIdx | 0));
    setAskOpen(false);
    if (rec.status === "idle") await rec.requestCamera();
    setLwScreen("studio");
  }, [rec]);
  const enterStudio = useCbLWS(() => openStudio(0), [openStudio]);
  const reorderBeats = (from, to) => {
    setBeats((arr) => { const n = [...arr]; const [m] = n.splice(from, 1); n.splice(to, 0, m); return n; });
    setSegClips((arr) => { const n = [...arr]; const [m] = n.splice(from, 1); n.splice(to, 0, m); return n; });
  };
  const addBeatInReview = (b) => {
    const newIdx = beats.length;          // index the appended beat lands at
    setBeats((arr) => [...arr, b]);
    openStudio(newIdx);                    // record just the new beat
  };
  const deleteBeat = (i) => {
    setBeats((arr) => arr.filter((_, idx) => idx !== i));
    setSegClips((arr) => arr.filter((_, idx) => idx !== i));
  };
  const saveVideo = () => {
    setSaved((s) => ({ ...s, [beneId]: [...new Set([...(s[beneId] || []), themeId])] }));
    setSavedVideos((s) => ({ ...s, [beneId]: { ...(s[beneId] || {}), [themeId]: { beats, segClips } } }));
    logActivity(`Saved: ${theme.label} for ${bene.firstName}`, `${beats.length} beats`, "lastWishes");
    // Acknowledge the save in the shared chat, then return to THIS
    // recipient's video library (the theme screen) — there's no separate
    // "done" screen. Pre-mark the theme intro as streamed so the generic
    // "let's make two videos" intro doesn't replay over the confirmation.
    const nextSaved = [...new Set([...(saved[beneId] || []), themeId])];
    const both = nextSaved.includes("hope") && nextSaved.includes("payout");
    const line = both
      ? `Both videos for ${bene.firstName} are saved — kept safe, and shared only when the time comes.`
      : `Saved. That was the brave part. Whenever you're ready, the ${themeId === "hope" ? "payout guidance" : "message of hope"} is just a few short beats.`;
    streamedRef.current.add(`theme:${beneId}:${themeId}`);
    appendProgressive([{ from: "ai", prose: true, text: line, screenTag: "lastWishes" }]);
    setLwScreen("theme");
  };
  const playVideo = (tid) => { const v = (savedVideos[beneId] || {})[tid]; if (v) setWatchVideo({ ...v, theme: window.lwTheme(tid) }); };
  const editVideo = (tid) => { const v = (savedVideos[beneId] || {})[tid]; if (!v) return; setThemeId(tid); setBeats(v.beats); setSegClips(v.segClips); setLwScreen("review"); };
  const recordOther = () => { startTheme(themeId === "hope" ? "payout" : "hope"); };

  const reset = useCbLWS(() => {
    setLwScreen("intro"); setBeneId(null); setThemeId("hope"); setBeats([]); setSegClips([]);
    setSaved({}); setSavedVideos({}); setWatchVideo(null); setExample(null); setStudioStart(0);
    setAskOpen(false);
    streamedRef.current = new Set();
    rec.resetAll();
  }, [rec]);

  // ── rail config (title / quick replies / custom card) ─────────────
  const titleSrc = LWS_SCREEN_TITLE[lwScreen];
  const title = typeof titleSrc === "function" ? titleSrc(theme) : titleSrc;

  const quickReplies = useMemoLWS(() => {
    if (lwScreen === "prep") return [
      { label: "I'm ready — open the studio", onClick: enterStudio },
      { label: "Show me an example", soft: true, onClick: () => setExample(theme.id === "payout" ? window.LW_EXAMPLES[1] : window.LW_EXAMPLES[0]) },
    ];
    if (lwScreen === "review") return [
      { label: "Save this video", onClick: saveVideo },
      { label: "Re-record a beat", soft: true, onClick: () => openStudio(0) },
    ];
    // The theme screen IS this recipient's video library. Once at least one
    // video is saved, offer the obvious next steps from the rail.
    if (lwScreen === "theme") {
      const both = savedForBene.includes("hope") && savedForBene.includes("payout");
      if (both) return [
        { label: "Record for someone else", onClick: () => { setBeneId(null); setLwScreen("intro"); } },
      ];
      if (savedForBene.length === 1) return [
        { label: `Record ${savedForBene.includes("hope") ? "guidance for the payout" : "the message of hope"}`, onClick: recordOther },
        { label: "Record for someone else", soft: true, onClick: () => { setBeneId(null); setLwScreen("intro"); } },
      ];
      return [];
    }
    return [];
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [lwScreen, theme, savedForBene, themeId, enterStudio, openStudio]);

  const customCard = useMemoLWS(() => {
    if (lwScreen === "prep" && bene) return <window.HowItWorksCard theme={theme} />;
    if (lwScreen === "review") return (
      <window.ReviewModule beats={beats} segClips={segClips} onReorder={reorderBeats}
        suggestions={suggestions} theme={theme} onAddBeat={addBeatInReview}
        onExpand={() => setRailBump(n => n + 1)}
        onRedo={(i) => { rec.retake(true); openStudio(i); }} onDelete={deleteBeat} />
    );
    return null;
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [lwScreen, bene, theme, beats, segClips, suggestions]);

  // ── the stage (left of the rail), wrapped with the step strip ─────
  const noBenes = !benes || benes.length === 0;
  const stage = (
    <div style={{ display: "flex", flexDirection: "column", flex: 1, height: "100%", minHeight: 0 }}>
      {lwScreen !== "studio" && <LWStepStrip onStepClick={onStepClick} />}
      <div style={{ flex: 1, minHeight: 0, position: "relative" }}>
        {noBenes ? (
          <window.WaveBg>
            <div style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column",
              justifyContent: "center", padding: "0 72px" }}>
              <div style={{ maxWidth: 460 }}>
                <h1 style={{ fontFamily: "var(--font-serif)", fontSize: 40, lineHeight: "44px",
                  color: "var(--helm-dark-green)", margin: "0 0 14px" }}>Confirm who you're protecting first.</h1>
                <p className="prose" style={{ color: "var(--helm-dark-green-2)", margin: 0, maxWidth: 420 }}>
                  Personal videos are recorded for the people in your plan. Once your beneficiaries are confirmed,
                  they'll appear here to record for.</p>
              </div>
            </div>
          </window.WaveBg>
        ) : lwScreen === "intro" ? (
          <window.LWIntroStage benes={benes} savedVideos={savedVideos} onPick={pickBene} />
        ) : lwScreen === "theme" ? (
          <window.LWThemeStage bene={bene} savedMap={savedVideos[beneId] || {}} onStart={startTheme}
            onPlay={playVideo} onEdit={editVideo} onBack={() => { setBeneId(null); setLwScreen("intro"); }} />
        ) : lwScreen === "prep" ? (
          <window.LWPrepStage bene={bene} theme={theme} beats={beats} setBeats={setBeats}
            suggestions={suggestions} onEnter={enterStudio}
            onExample={() => setExample(theme.id === "payout" ? window.LW_EXAMPLES[1] : window.LW_EXAMPLES[0])}
            onBack={() => setLwScreen("theme")} />
        ) : lwScreen === "studio" ? (
          <window.StudioScreen bene={bene} theme={theme} beats={beats} segClips={segClips} setSegClips={setSegClips}
            rec={rec} logActivity={(t, m) => logActivity(t, m, "lastWishes")} startAt={studioStart}
            onExit={() => { setStudioStart(0); setLwScreen("prep"); }} onReviewAll={() => setLwScreen("review")} />
        ) : lwScreen === "review" ? (
          <window.LWReviewStage bene={bene} theme={theme} beats={beats} segClips={segClips} onSave={saveVideo} />
        ) : null}
      </div>
    </div>
  );

  // ── modals (example clip + watch-saved-video) ─────────────────────
  const modals = (
    <>
      {example && <window.ExampleClip ex={example} onClose={() => setExample(null)} />}
      {watchVideo && (
        <div onClick={() => setWatchVideo(null)} style={{ position: "fixed", inset: 0, zIndex: 130,
          background: "rgba(12,18,12,.6)", backdropFilter: "blur(8px)", display: "flex",
          alignItems: "center", justifyContent: "center", padding: 40 }}>
          <div onClick={(e) => e.stopPropagation()} style={{ width: 860, maxWidth: "100%", borderRadius: 24,
            overflow: "hidden", background: "#0c160c", boxShadow: "0 24px 80px rgba(0,0,0,.5)" }}>
            <div style={{ position: "relative", aspectRatio: "16 / 10" }}>
              <window.StitchPlayer segments={watchVideo.beats.map((b, i) => ({ label: b.t, clip: watchVideo.segClips[i] }))} poster={window.LW_PO.photo} />
            </div>
            <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "16px 22px",
              background: "#0c160c", color: "rgba(254,252,245,.9)", fontFamily: "var(--font-sans)" }}>
              <span style={{ fontSize: 15, fontWeight: 600 }}>{watchVideo.theme.label} · {bene ? bene.firstName : ""}</span>
              <button onClick={() => setWatchVideo(null)} style={{ background: "none", border: "none",
                color: "rgba(254,252,245,.7)", cursor: "pointer", fontSize: 14, fontWeight: 600, fontFamily: "var(--font-sans)" }}>Close</button>
            </div>
          </div>
        </div>
      )}
    </>
  );

  // free-text handler tuned to the recording moment
  const handleUserSend = useCbLWS(async (text) => {
    return window.HelmAI.lwChatReply(text, bene, theme);
  }, [bene, theme]);

  return { stage, modals, title, quickReplies, customCard, reset, handleUserSend,
           immersive: lwScreen === "studio", askOpen, setAskOpen,
           railBump: railBump + beats.length + (saved[beneId]?.length || 0) };
}

Object.assign(window, { useLastWishes, lwsBenesFromPeople, lwsPhotoFor });
