/* Pre-Sales Agent — New brief. Three toggleable, dead-simple ways to start
   (switch via Tweaks → "New-brief flow"):
     • smart   — one box, paste anything → instant brief
     • invite  — forward / paste a calendar invite, confirm, go
     • meeting — pick from upcoming meetings the agent already sees
   All three converge on MP.addPrep → the Game Day Brief. */

const briefWait = (ms) => new Promise(r => setTimeout(r, ms));

const SAMPLE_INVITE =
`Intro meeting with Northwind Freight
When: Thursday 3:00 – 3:30 PM
Organizer: Dana Whitfield (dana@northwindfreight.com)
Guests: Dana Whitfield (VP Operations), Marcus Reed (Director of IT)
northwindfreight.com

Following up on your email — keen to hear how you cut manual handoffs and whether you integrate with our TMS. We're scoping the Q3 budget now.`;

const SAMPLE_EMAIL =
`From: Dana Whitfield <dana@northwindfreight.com>
Subject: Re: cutting our manual handoffs

Hi — thanks for reaching out. We're a logistics team and the handoffs between our systems are killing us. Can you walk us through how you'd integrate with our TMS? Pricing matters too — we're a ~900-person company. Marcus Reed (Director of IT) will join.`;

// ---- animated research card (reused by every flow's generating state) ----
function BriefResearchCard({ steps }) {
  const [done, setDone] = React.useState(0);
  React.useEffect(() => {
    let i = 0, t;
    const tick = () => { i += 1; setDone(i); if (i < steps.length) t = setTimeout(tick, 600); };
    t = setTimeout(tick, 480);
    return () => clearTimeout(t);
  }, []);
  return (
    <div className="mp-onb__research">
      <div className="mp-eyebrow"><Icon name="Radar" size={14} /> Deep research</div>
      <div className="mp-research">
        {steps.map((s, i) => {
          const isDone = i < done, isActive = i === done;
          return (
            <div key={i} className={`mp-research__step${i <= done ? " is-on" : ""}`}>
              <span className={`mp-research__dot${isDone ? " is-done" : isActive ? " is-active" : ""}`}>
                {isDone ? <Icon name="Check" size={15} strokeWidth={2.5} /> : isActive ? <span className="mp-spinner"></span> : <span className="mp-tinydot"></span>}
              </span>
              <span className="mp-research__label">{s}</span>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ---- live flow selection (kept in sync with Tweaks) ----
function useCreateFlow() {
  const [flow, set] = React.useState(MP.get().createFlow || "smart");
  React.useEffect(() => {
    const on = () => set(MP.get().createFlow || "smart");
    window.addEventListener("mp-flowchange", on);
    return () => window.removeEventListener("mp-flowchange", on);
  }, []);
  return flow;
}

// ---- meeting time helper ----
function meetingTime(iso) {
  const d = new Date(iso), now = new Date();
  const sameDay = d.toDateString() === now.toDateString();
  const tom = new Date(now); tom.setDate(now.getDate() + 1);
  const isTom = d.toDateString() === tom.toDateString();
  const time = d.toLocaleTimeString(undefined, { hour: "numeric", minute: "2-digit" });
  const day = sameDay ? "Today" : isTom ? "Tomorrow" : d.toLocaleDateString(undefined, { weekday: "long" });
  return { day, time, soon: sameDay };
}

// ---- live research card, driven by REAL pipeline stage events ----
// progress = { phase: "researching"|"writing"|"finishing"|"done", sources, model }
// Two real backend stages — research (grounded web search) and synthesis
// (profile + match + write) — map onto a four-line checklist. A step only
// completes when its real stage actually finishes, so the final line keeps
// spinning until the brief is genuinely ready (no premature "done", no hang).
function LiveResearchCard({ prospect, who, progress }) {
  const phase = progress.phase || "researching";
  const sources = progress.sources;
  const researchDone = phase === "writing" || phase === "finishing" || phase === "done";
  const allDone = phase === "done";

  const steps = [
    { label: "Scanning " + (prospect || "the prospect") + " across the web", group: "research" },
    {
      label: "Reading their site, news, funding & hiring"
        + (sources != null && sources > 0 ? "  ·  " + sources + (sources === 1 ? " source" : " sources") : ""),
      group: "research"
    },
    { label: "Profiling " + who + " & matching to your ICP", group: "synthesis" },
    { label: "Writing your Game Day Brief", group: "synthesis" }
  ];
  const statusOf = (g) => {
    if (allDone) return "done";
    if (g === "research") return researchDone ? "done" : "active";
    // synthesis group
    if (phase === "finishing") return "done";
    return researchDone ? "active" : "pending"; // active once research finished
  };

  const headline = allDone ? "Brief ready"
    : researchDone ? "Writing your brief…"
    : "Researching the live web…";

  return (
    <div className="mp-onb__research">
      <div className="mp-eyebrow"><Icon name="Radar" size={14} /> Deep research · {headline}</div>
      <div className="mp-research">
        {steps.map((s, i) => {
          const st = statusOf(s.group);
          const isDone = st === "done", isActive = st === "active";
          return (
            <div key={i} className={`mp-research__step${st !== "pending" ? " is-on" : ""}`}>
              <span className={`mp-research__dot${isDone ? " is-done" : isActive ? " is-active" : ""}`}>
                {isDone ? <Icon name="Check" size={15} strokeWidth={2.5} /> : isActive ? <span className="mp-spinner"></span> : <span className="mp-tinydot"></span>}
              </span>
              <span className="mp-research__label">{s.label}</span>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ---- shared generating view ----
// When `progress` is supplied we render the live, event-driven card; otherwise
// (offline / fallback path) we fall back to the timed checklist.
function GeneratingView({ prospect, named, progress }) {
  const who = named === 1 ? "the stakeholder" : named > 1 ? named + " stakeholders" : "the company";
  const fallbackSteps = [
    "Scanning " + (prospect || "the prospect") + " across the web",
    "Reading their site, news, funding & hiring",
    "Profiling " + who + " — roles & priorities",
    "Matching them to your ICP & what you sell",
    "Writing your Game Day Brief"
  ];
  return (
    <div className="mp-genwrap mp-fade">
      <div className="mp-genwrap__inner">
        <div className="mp-cellflex" style={{ gap: 12, marginBottom: 4 }}>
          <Avatar name={prospect || "?"} color="blue" size={40} soft />
          <div>
            <div style={{ fontWeight: 900, fontSize: "var(--fs-subtitle-1)", color: "var(--fg-1)" }}>{prospect || "Your prospect"}</div>
            <div style={{ fontSize: "var(--fs-body-3)", color: "var(--fg-2)" }}>Building your brief…</div>
          </div>
        </div>
        {progress
          ? <LiveResearchCard prospect={prospect} who={who} progress={progress} />
          : <BriefResearchCard steps={fallbackSteps} />}
      </div>
    </div>
  );
}

// What the top bar counts down: the credits the platform reported, or the unknown state when it
// could not be read — never a 0 Edge chose (design doc §1.3). No monthly allowance is counted:
// none exists (Part 3; "0 of 0 briefs left" was the seat-model leak #504 §E / pitchiq #24 closed).
function briefsLeftLabel() {
  const credits = MP.packBalance();
  if (credits == null) return CREDITS_UNKNOWN.text;
  return `${credits} credit${credits === 1 ? "" : "s"} left`;
}

// ---- top bar shared by every flow ----
function CreateTopBar() {
  return (
    <div className="mp-onb__bar">
      <button onClick={() => navigate("/dashboard")} style={{ display: "inline-flex", alignItems: "center", gap: 8, background: "none", border: 0, cursor: "pointer", color: "var(--fg-1)", font: "inherit", fontWeight: 700 }}>
        <Icon name="ArrowLeft" size={18} /> <Logo size={18} />
      </button>
      <div className="mp-onb__bar-r">
        <span className="mp-onb__progress">{briefsLeftLabel()}</span>
      </div>
    </div>
  );
}

// =================== FLOW 1 — SMART BOX ===================
//
// `restore` re-seeds the fields after a FAILED generation. This flow unmounts
// while GeneratingView is up, so a failure that returns here would otherwise
// remount it empty and silently discard everything the user typed — leaving a
// blank form and, once the error toast is closed, no explanation either.
function SmartBoxFlow({ onGenerate, restore }) {
  const [prospect, setProspect] = React.useState((restore && restore.prospect) || "");
  const [website, setWebsite] = React.useState((restore && restore.website) || "");
  const [people, setPeople] = React.useState((restore && restore.contacts) || []);
  const [context, setContext] = React.useState((restore && restore.context) || "");
  // Spec `BhNVUS9sai`: a dismissed credit dialog keeps the attachment selections too (#211).
  const [attachments, setAttachments] = React.useState((restore && restore.attachments) || []);
  const [drag, setDrag] = React.useState(false);
  const [nudge, setNudge] = React.useState("");
  const [nudgeWarn, setNudgeWarn] = React.useState(false);
  const [softWarned, setSoftWarned] = React.useState(false);
  const fileRef = React.useRef(null);

  const addPerson = () => setPeople(p => [...p, { name: "", title: "", email: "", linkedin: "" }]);
  const updatePerson = (i, k, v) => setPeople(p => p.map((person, j) => j === i ? { ...person, [k]: v } : person));
  const removePerson = (i) => setPeople(p => p.filter((_, j) => j !== i));
  const namedPeople = people.filter(p => (p.name || "").trim());

  const onFiles = (fl) => { const names = Array.from(fl || []).map(f => f.name).slice(0, 4); if (names.length) setAttachments(a => [...a, ...names].slice(0, 4)); };
  const clearNudge = () => { setNudge(""); setNudgeWarn(false); };

  const hasCompany = !!(prospect.trim() || website.trim());
  // "Rich" once there's a person, a document, or real written context — a bare
  // company name/URL alone still yields a generic brief.
  const rich = namedPeople.length > 0 || attachments.length > 0 || context.trim().length > 60;
  const thin = !rich;

  const go = () => {
    if (!hasCompany) { setNudgeWarn(false); setNudge("Tell me who you're meeting — a company name or website is plenty to start."); return; }
    if (!rich && !softWarned) {
      setSoftWarned(true); setNudgeWarn(true);
      setNudge("Just the company gives me a thin, generic brief. Add who's in the room, paste the intro email, or attach a document and I'll walk you in genuinely ready — or hit “Prepare anyway” to continue with just this.");
      return;
    }
    const name = prospect.trim() || MP.companyNameFromDomain(website.trim());
    const contacts = namedPeople.map(p => {
      const linkedin = (p.linkedin || "").trim();
      const email = (p.email || "").trim();
      return { name: p.name.trim(), title: (p.title || "").trim(), email, linkedin, contact: linkedin || email };
    });
    // `attachments` rides along only so a failed or refused generation can hand them back (restore
    // above); createBriefStream sends the server the five fields it names, never this one.
    onGenerate({ prospect: name, website: website.trim(), companyLinkedin: "", contacts, context: context.trim(), attachments });
  };

  return (
    <div className="mp-create mp-create--form mp-fade">
      <div className="mp-create__hero">
        <span className="mp-eyebrow mp-pill-eyebrow"><Icon name="Sparkles" size={13} /> New Game Day Brief</span>
        <h1 className="text-heading-1">Who are you meeting?</h1>
        <p className="mp-create__sub"><b>The more you tell me, the sharper your brief.</b> Fill in what you can — every field helps.</p>
      </div>

      {/* the company */}
      <div className="mp-bfcard">
        <div className="mp-bfcard__head"><Icon name="Building2" size={16} /> The company you're meeting</div>
        <div className="mp-grid-2">
          <Field label="Company name"><Input value={prospect} onChange={e => { setProspect(e.target.value); clearNudge(); }} placeholder="e.g. Northwind Freight" autoFocus /></Field>
          <Field label="Website / URL" hint="A site lets me research the exact company, not a namesake."><Input value={website} onChange={e => { setWebsite(e.target.value); clearNudge(); }} placeholder="northwindfreight.com" /></Field>
        </div>
      </div>

      {/* who you're meeting — recommended */}
      <div className="mp-bfcard">
        <div className="mp-bfcard__head"><Icon name="Users" size={16} /> Who you're meeting <span className="mp-people__rec">Recommended</span></div>
        <p className="mp-bfcard__hint">{namedPeople.length ? "I'll prep a tailored angle for each person." : "Naming them sharpens the brief a lot — or skip it and I'll prep the company."}</p>
        {people.length > 0 && (
          <div className="mp-people__rows">
            {people.map((p, i) => (
              <div key={i} className="mp-person-row mp-person-row--4">
                <input className="mp-person-row__in mp-person-row__in--name" placeholder="Name" value={p.name} onChange={e => updatePerson(i, "name", e.target.value)} aria-label="Name" />
                <input className="mp-person-row__in" placeholder="Title — e.g. VP of Sales" value={p.title} onChange={e => updatePerson(i, "title", e.target.value)} aria-label="Title" />
                <input className="mp-person-row__in" placeholder="Email" value={p.email} onChange={e => updatePerson(i, "email", e.target.value)} aria-label="Email" />
                <input className="mp-person-row__in" placeholder="LinkedIn URL" value={p.linkedin} onChange={e => updatePerson(i, "linkedin", e.target.value)} aria-label="LinkedIn URL" />
                <button type="button" className="mp-person-row__rm" onClick={() => removePerson(i)} aria-label="Remove person"><Icon name="X" size={16} /></button>
              </div>
            ))}
          </div>
        )}
        <button type="button" className="mp-people__add" onClick={addPerson}>
          <Icon name="UserPlus" size={15} /> {people.length ? "Add another person" : "Add the people you're meeting"}
        </button>
        <p className="mp-people__tip"><Icon name="Lightbulb" size={14} /> <span>Add a <b>LinkedIn URL</b> for anyone with a common name or at a big company — it's how I lock onto the right person instead of a namesake.</span></p>
      </div>

      {/* additional context + attachments */}
      <div className={`mp-bfcard${drag ? " is-drag" : ""}`}
        onDragOver={(e) => { e.preventDefault(); setDrag(true); }} onDragLeave={() => setDrag(false)}
        onDrop={(e) => { e.preventDefault(); setDrag(false); onFiles(e.dataTransfer.files); }}>
        <div className="mp-bfcard__head"><Icon name="FileText" size={16} /> Additional context <span className="mp-bfcard__opt">optional</span></div>
        <p className="mp-bfcard__hint">Anything else that sharpens the brief — paste the intro email or thread, the deal background, or what you want out of the call.</p>
        <Textarea rows={4} value={context} onChange={e => { setContext(e.target.value); clearNudge(); }}
          placeholder="Paste the intro email, notes on the opportunity, your goal for the call…" />
        {attachments.length > 0 && (
          <div className="mp-attach-row" style={{ marginTop: 12 }}>
            {attachments.map((a, i) => (
              <span key={i} className="mp-attach"><Icon name="FileText" size={14} /> {a}
                <button onClick={() => setAttachments(at => at.filter((_, j) => j !== i))} aria-label="Remove"><Icon name="X" size={13} /></button>
              </span>
            ))}
          </div>
        )}
        <label className="mp-bfcard__attach" title="Upload a pitch deck, one-pager, case study, or any other material you'd like the brief to draw on.">
          <Icon name="Paperclip" size={17} /> <span>Attach documents</span>
          <input ref={fileRef} type="file" multiple style={{ display: "none" }} onChange={(e) => onFiles(e.target.files)} />
        </label>
        <p className="mp-bfcard__attachhint"><Icon name="Info" size={13} /> <span>Upload a <b>pitch deck</b>, one-pager, case study, or anything else you'd like the brief to use to shape it.</span></p>
      </div>

      {nudge && (
        nudgeWarn
          ? <div className="mp-create__warn" role="alert"><Icon name="AlertTriangle" size={18} /> <span>{nudge}</span></div>
          : <p className="mp-create__nudge"><Icon name="Info" size={14} /> {nudge}</p>
      )}

      <div className="mp-bf-submit">
        <Button size="lg" variant={softWarned && thin ? "warn" : "primary"} icon={softWarned && thin ? "Zap" : "Sparkles"} iconRight="ArrowRight" disabled={!hasCompany} onClick={go}>
          {softWarned && thin ? "Prepare anyway" : "Prepare me"}
        </Button>
      </div>
    </div>
  );
}

// =================== FLOW 2 — FORWARD AN INVITE ===================
function InviteFlow({ onGenerate }) {
  const [text, setText] = React.useState("");
  const [draft, setDraft] = React.useState(null); // parsed meeting
  const [drag, setDrag] = React.useState(false);

  const parse = (raw) => {
    const p = MP.parseIntake(raw, []);
    if (!p.prospect && p.website) p.prospect = MP.companyNameFromDomain(p.website);
    setDraft({ prospect: p.prospect || "", website: p.website || "", companyLinkedin: p.companyLinkedin || "", contacts: (p.contacts || []).filter(c => c.name), context: raw });
  };
  const onFile = (file) => { if (!file) return; const r = new FileReader(); r.onload = (e) => { setText(String(e.target.result || "")); parse(String(e.target.result || "")); }; r.readAsText(file); };

  if (draft) {
    const named = draft.contacts.filter(c => c.name.trim());
    return (
      <div className="mp-create mp-fade">
        <div className="mp-create__hero">
          <span className="mp-eyebrow mp-pill-eyebrow is-green"><Icon name="CalendarCheck" size={13} /> Invite read</span>
          <h1 className="text-heading-1">Got it — here's your meeting.</h1>
          <p className="mp-create__sub">I pulled this out of the invite. Fix anything that's off, then I'll prep you.</p>
        </div>
        <div className="mp-invite-detected">
          <div className="mp-cellflex" style={{ gap: 13 }}>
            <Avatar name={draft.prospect || "?"} color="blue" size={44} soft />
            <div style={{ minWidth: 0 }}>
              <input className="mp-invite-name" value={draft.prospect} placeholder="Company name"
                onChange={(e) => setDraft(d => ({ ...d, prospect: e.target.value }))} />
              <input className="mp-invite-url" value={draft.website} placeholder="company.com"
                onChange={(e) => setDraft(d => ({ ...d, website: e.target.value }))} />
            </div>
          </div>
          <div className="mp-invite-people">
            <div className="mp-invite-people__lbl"><Icon name="Users" size={14} /> Who you're meeting</div>
            {named.length === 0
              ? <span style={{ color: "var(--fg-3)", fontSize: "var(--fs-body-2)" }}>Just the company — that's fine.</span>
              : named.map((c, i) => (
                  <div key={i} className="mp-invite-person">
                    <Avatar name={c.name} color={["blue", "purple", "green", "gold"][i % 4]} size={28} />
                    <span style={{ fontWeight: 700, color: "var(--fg-1)" }}>{c.name}</span>
                    {c.title && <span style={{ color: "var(--fg-2)", fontSize: "var(--fs-body-2)" }}>· {c.title}</span>}
                  </div>
                ))}
          </div>
        </div>
        <div className="mp-create__actions">
          <Button variant="secondary" icon="RotateCcw" onClick={() => { setDraft(null); setText(""); }}>Start over</Button>
          <Button size="lg" icon="Sparkles" iconRight="ArrowRight" disabled={!draft.prospect.trim()}
            onClick={() => onGenerate({ ...draft, contacts: named })}>Prepare me for this</Button>
        </div>
      </div>
    );
  }

  return (
    <div className="mp-create mp-fade">
      <div className="mp-create__hero">
        <span className="mp-eyebrow mp-pill-eyebrow"><Icon name="CalendarDays" size={13} /> From your calendar</span>
        <h1 className="text-heading-1">Forward your meeting.</h1>
        <p className="mp-create__sub">Paste the calendar invite or intro email. I'll figure out who, what, and why.</p>
      </div>
      <div className={`mp-invitebox${drag ? " is-drag" : ""}`}
        onDragOver={(e) => { e.preventDefault(); setDrag(true); }} onDragLeave={() => setDrag(false)}
        onDrop={(e) => { e.preventDefault(); setDrag(false); onFile(e.dataTransfer.files[0]); }}>
        <div className="mp-invitebox__head"><Icon name="Forward" size={16} /> Paste invite or drop a .txt / .ics</div>
        <textarea value={text} placeholder={"Microsoft Teams / Google Calendar invite, or the email thread…"}
          onChange={(e) => setText(e.target.value)} />
      </div>
      <div className="mp-create__actions">
        <Button variant="secondary" icon="FileText" onClick={() => { setText(SAMPLE_INVITE); }}>Use a sample invite</Button>
        <Button size="lg" icon="ScanLine" iconRight="ArrowRight" disabled={!text.trim()} onClick={() => parse(text)}>Read the invite</Button>
      </div>
    </div>
  );
}

// =================== FLOW 3 — PICK A MEETING ===================
function MeetingFlow({ onGenerate }) {
  const meetings = React.useMemo(() => MP.upcomingMeetings(), []);
  return (
    <div className="mp-create mp-create--wide mp-fade">
      <div className="mp-create__hero">
        <span className="mp-calchip"><span className="mp-calchip__dot"></span> Calendar connected</span>
        <h1 className="text-heading-1">Your upcoming meetings.</h1>
        <p className="mp-create__sub">Tap a meeting and I'll prep you for it. No typing.</p>
      </div>
      <div className="mp-meetlist">
        {meetings.map(m => {
          const t = meetingTime(m.when);
          const named = m.attendees || [];
          return (
            <button key={m.id} className="mp-meetcard" onClick={() => onGenerate({ prospect: m.prospect, website: m.website, contacts: named.map(attendee => ({ name: attendee.name, title: attendee.title, contact: "" })), context: m.context })}>
              <span className={`mp-meetcard__when${t.soon ? " is-soon" : ""}`}>
                <span className="mp-meetcard__day">{t.day}</span>
                <span className="mp-meetcard__time">{t.time}</span>
              </span>
              <span className="mp-meetcard__body">
                <span className="mp-meetcard__top">
                  <Avatar name={m.prospect} color="blue" size={34} soft />
                  <span style={{ minWidth: 0 }}>
                    <span className="mp-meetcard__co">{m.prospect}</span>
                    <span className="mp-meetcard__people">
                      {named.map(attendee => attendee.name).join(", ") || "Company only"}
                    </span>
                  </span>
                </span>
                <span className="mp-meetcard__ctx">{m.context}</span>
              </span>
              <span className="mp-meetcard__cta"><Icon name="Sparkles" size={15} /> Prep this <Icon name="ArrowRight" size={15} /></span>
            </button>
          );
        })}
      </div>
      <p className="mp-create__nudge" style={{ justifyContent: "center" }}>
        <Icon name="Info" size={14} /> Don't see it? Switch the flow in Tweaks to paste an invite or a link.
      </p>
    </div>
  );
}

// =================== CREDIT AVAILABILITY DIALOG (pitchiq#211, #229, matchiq-for-sellers#1443) ===================
//
// ⭐ THE SHARED DIALOG. Since #1443 this renders the Suite shell's own `CreditAvailabilityDialog`
// (evolve-shell 0.19.0; Figma "Evolve Coach" `879:7126`, `1505:72271`, `2728:10209`, `2728:10263`) —
// the same component Coach shows. It renders exactly what web/app/credit-refusal.js decided (through the
// shell's resolver) and decides nothing itself, so swapping the visual could not change which dialog a
// viewer gets.
//
// What the shell dialog does, and its own tests prove (credit-availability-dialog.test.tsx): the primary
// action is a link that closes the dialog FIRST and then navigates, so a Back (bfcache) restore shows the
// kept form rather than the dialog; Escape, the scrim and Dismiss close it; focus moves into it and Tab
// is trapped. Neither action starts a brief.

// The illustration — Figma's image fill, byte for byte (web/test/credit-dialog-illustration.test.js).
// The shell ships no assets; each product serves this file itself.
const CREDIT_DIALOG_ILLUSTRATION_SRC = "app/assets/credit-limit-illustration.png";

// Where focus returns when the dialog closes (R4-#003). The "Prepare me" that opened it was unmounted
// while the loader showed, so the shell's own opener is gone (<body>); this is the RE-RENDERED form's
// "Prepare me" (SmartBoxFlow `.mp-bf-submit`). A getter on purpose: the hook reads `.current` only at
// close time, after the form is back on screen.
const PREPARE_ME_FOCUS_TARGET = { get current() { return document.querySelector(".mp-bf-submit button"); } };

function CreditRefusalDialog({ dialog, onDismiss }) {
  return (
    <window.EvolveShell.CreditAvailabilityDialog
      content={dialog}
      illustrationSrc={CREDIT_DIALOG_ILLUSTRATION_SRC}
      onDismiss={onDismiss}
      returnFocusRef={PREPARE_ME_FOCUS_TARGET}
    />
  );
}

// #211 — after a credit refusal the tab's balance AND its workspace context may both be stale:
// another tab switched the workspace (the dev repro), or a teammate drained the shared pool.
//
// The dialog needs only the CONTEXT, so only /api/shell is awaited: the dialog describes — and links
// to — the workspace the server just refused, not the one this tab last saw. /me is fired and NOT
// awaited (R1-#002): it only refreshes the balance, and its `mp-state` makes the sidebar re-read its
// own /api/shell (nav.jsx useShellContext), so the header stops claiming credits that are gone.
//
// This is a DIRECT read, outside nav.jsx's shell cache and switch epoch. That is safe here: a
// workspace switch navigates home and unmounts this page, so a context that a switch overtook never
// reaches a dialog. Never throws: a failed read is no context, and the least-privileged scenario shows.
//
// R4-#006: bounded. A healthy /api/shell can be slow — it reads the platform serially, with a
// 2-attempt retry at up to 2s each — so 5s lets a slow-but-working read win, while a read that never
// answers stops holding the loader. On the timeout there is no context: least privilege.
const CREDIT_CONTEXT_TIMEOUT_MS = 5000;

async function refreshCreditContext() {
  MP.refreshMe().catch(() => {});
  // The timer is cleared however the race ends (R5-#001), so a fast answer leaves nothing behind.
  let timer;
  const timedOut = new Promise((resolve) => { timer = setTimeout(() => resolve(null), CREDIT_CONTEXT_TIMEOUT_MS); });
  try { return await Promise.race([PSA.shell(), timedOut]); } catch (e) { return null; } finally { clearTimeout(timer); }
}

// The dialog's context from a shell answer. WHO may manage billing is the Suite shell's own rule —
// `canManageBilling`, which the sidebar's Add-credits button follows — so the two cannot disagree
// (R1-#008). No context, or no shell script: false, least privilege.
function creditDialogContext(ctx) {
  const shell = window.EvolveShell;
  return {
    workspaceKind: ctx ? ctx.workspace.kind : null,
    canManage: !!(ctx && shell && shell.canManageBilling(ctx.workspace, ctx.role)),
    destinations: suiteCreditDestinations(ctx)
  };
}

// R1-#006 — the spend path's 409 when the workspace this browser asked for cannot be used (a stale
// local-only hint, or a member removed from it: server/src/routes/briefs.ts `workspace_unavailable`).
// The server sends only the code, and no sentence for it exists elsewhere in Edge.
const WORKSPACE_UNAVAILABLE_CODE = "workspace_unavailable";
const WORKSPACE_UNAVAILABLE_TEXT = "You can't use this workspace any more. Switch workspace and try again.";

// =================== WRAPPER ===================
function PrepareNewPage({ path }) {
  const flow = useCreateFlow();
  const toast = useToast();
  const [gen, setGen] = React.useState(null); // {prospect, named, progress}
  // Last FAILED submission, re-seeded into the form so a failure never costs
  // the user their typing. Null on the happy path (success navigates away).
  const [restore, setRestore] = React.useState(null);
  // The Credit Availability dialog a refused "Prepare me" opened, or null (#211, #229).
  const [creditDialog, setCreditDialog] = React.useState(null);

  // Spec `BhNVUS9sai`: returning from Add credits / Adjust limit refreshes availability. A normal
  // return reloads the app (hydrate reads /me); a Back that restores this page from the bfcache does
  // not, so /me is re-read here. Generation still waits for another explicit "Prepare me".
  React.useEffect(() => {
    const onShow = (e) => { if (e.persisted) MP.refreshMe().catch(() => {}); };
    window.addEventListener("pageshow", onShow);
    return () => window.removeEventListener("pageshow", onShow);
  }, []);

  // Map a real pipeline stage event to the live loader's progress state.
  const applyProgress = (ev) => setGen(g => {
    if (!g) return g;
    const p = { ...(g.progress || { phase: "researching", sources: null }) };
    if (ev.stage === "open") p.phase = "researching";
    else if (ev.stage === "research" && ev.status === "start") { p.phase = "researching"; p.model = ev.model; }
    else if (ev.stage === "research" && ev.status === "done") { p.sources = ev.sources; }
    else if (ev.stage === "synthesis" && ev.status === "start") { p.phase = "writing"; p.model = ev.model; }
    else if (ev.stage === "synthesis" && ev.status === "done") { p.phase = "finishing"; }
    else if (ev.stage === "done") { p.phase = "done"; }
    return { ...g, progress: p };
  });

  // Single convergence seam for ALL three intake flows (smart box, paste/parse
  // invite, meeting preset): each calls onGenerate({prospect, website, ...}).
  // The company name here is just a HINT — the server canonicalizes it from the
  // grounded research + website (fixes typos/casing, and replaces a hint that
  // conflicts with the real site), so the persisted brief title is authoritative
  // regardless of which flow (or the paste-regex parser) produced the name.
  const onGenerate = async (input) => {
    const named = (input.contacts || []).filter(c => c.name && c.name.trim());
    // Start in the researching phase so the loader is live immediately.
    setGen({ prospect: input.prospect, named: named.length, progress: { phase: "researching", sources: null } });
    try {
      // Stream real deep-research + synthesis stage events into the loader.
      // Falls back to local generation offline / if streaming is unavailable.
      const prep = await MP.createBriefStream({
        prospect: input.prospect,
        website: input.website || "",
        companyLinkedin: input.companyLinkedin || "",
        contacts: named,
        context: input.context || ""
      }, applyProgress);
      // #285: null = the user switched workspace while this brief was generating. It belongs to the
      // workspace they left; the switch already moved them on, so stay where they are.
      if (!prep) { setGen(null); setRestore(input); return; }
      navigate("/prepare/" + prep.id);
    } catch (e) {
      // #211 / #229 — out of credits (402) or a monthly limit (403 credit_limit_reached): the
      // spec's dialog, ALWAYS, on the click that hit it. This used to skip the toast on a 402 and
      // leave the page to a cap wall keyed on a balance the tab had not refreshed, so the refusal
      // rendered nothing. A PLATFORM_PRODUCT_ACCESS_DENIED 403 is not a credit refusal: api.js is
      // already leaving for the Suite app chooser, and it falls through to the toast below.
      //
      // R1-#002: the context is re-read BEFORE the loader goes. Leaving it first put a live
      // "Prepare me" back on screen, unexplained, for as long as /api/shell took — and a second click
      // started a second brief under this refusal's dialog.
      const refusal = CreditRefusal.refusalFrom(e);
      const ctx = refusal ? await refreshCreditContext() : null;
      setGen(null);
      // Hand the user's input back to the form. Without this the flow remounts
      // empty (it was unmounted while GeneratingView was up), so a failed
      // generation costs them everything they typed — and once the toast
      // is closed they are left on a blank form with no explanation at all.
      setRestore(input);
      if (refusal) {
        setCreditDialog(CreditRefusal.dialogFor(refusal, creditDialogContext(ctx)));
        return;
      }
      // Anything else (research/synthesis down, credits unavailable, network) is a
      // real failure: say so — in the server's own sentence when it sent one — instead
      // of silently resetting the form with no explanation.
      // R4-#005: for workspace_unavailable the server's own sentence wins when it sends one.
      const message = e && e.data && e.data.error === WORKSPACE_UNAVAILABLE_CODE
        ? e.data.message || WORKSPACE_UNAVAILABLE_TEXT
        : (e && e.message) || "Couldn't generate that brief — try again shortly.";
      toast.error(message);
    }
  };

  return (
    <Shell path={path} width="mp-w-2xl">
      {gen
        ? <GeneratingView prospect={gen.prospect} named={gen.named} progress={gen.progress} />
        : flow === "invite" ? <InviteFlow onGenerate={onGenerate} />
        : flow === "meeting" ? <MeetingFlow onGenerate={onGenerate} />
        : <SmartBoxFlow onGenerate={onGenerate} restore={restore} />}
      {creditDialog && <CreditRefusalDialog dialog={creditDialog} onDismiss={() => setCreditDialog(null)} />}
    </Shell>
  );
}

Object.assign(window, { PrepareNewPage, SmartBoxFlow, InviteFlow, MeetingFlow, BriefResearchCard, LiveResearchCard, GeneratingView, CreditRefusalDialog, CREDIT_CONTEXT_TIMEOUT_MS });
