// VELOUR — signed-in members' blog. Jennifer's monthly letter, a paid benefit.
// Paid (Jennifer / demo account): read the latest letter + write back.
// Free (fresh account): visible but locked & greyed, with an upgrade nudge.
const { Card, Avatar } = window.VelourDesignSystem_380ed4;
const FRESH = !!window.VELOUR_FRESH;

// ---- the letters Jennifer publishes ----------------------------------------
//
// THE ONLY SOURCE IS public.posts, via /api/posts. There is no built-in letter
// any more.
//
// There used to be: a hard-coded June 2026 letter and three past titles, kept as
// a fallback when this was first wired to the API on 30 Aug 2026 — the reasoning
// being that a members' page going blank on deploy day would be worse than the
// bug it fixed. The owner settled that on 31 Aug by deciding the archive starts
// fresh, and the fallback stopped being a safety net at that moment: with an
// empty table it would have shown every member a letter dated June for as long
// as nobody published, and then made three months of "past letters" vanish the
// day somebody did. An empty state that says so is the honest version.
//
// LOCKED LETTERS ARE FILTERED OUT, not rendered empty. api/posts.js sends a paid
// letter to an unentitled reader as title and month with no body at all — so a
// locked row has nothing for BlogPaid to render, and treating it as the latest
// letter would print a headline over silence. The locked view below reads those
// titles deliberately; the paid view must not.
function useLiveLetters() {
  const [letters, setLetters] = React.useState(null);
  React.useEffect(() => {
    let cancelled = false;
    const ask = (headers) =>
      fetch("/api/posts", { headers: headers || {} })
        .then((r) => (r.ok ? r.json() : null))
        .then((body) => {
          if (cancelled || !body || !Array.isArray(body.posts)) return;
          setLetters(body.posts);
        })
        // A failed request settles as an empty list rather than staying null
        // forever, or the page would sit on its loading state for good.
        .catch(() => { if (!cancelled) setLetters([]); });
    // The session rides along when there is one: it is what turns a paid letter
    // from a title into a letter. No session is not an error here — the demo
    // dashboard has none, and free letters are public.
    if (window.velourAuth) {
      window.velourAuth.ensureFresh()
        .then((s) => ask(s && s.access_token ? { Authorization: "Bearer " + s.access_token } : {}))
        .catch(() => ask({}));
    } else ask({});
    return () => { cancelled = true; };
  }, []);
  return letters;
}

// What to render. `loading` matters: without it the page would flash "no letter
// yet" at a paying member for the length of one fetch, every single visit.
function useLetterView() {
  const live = useLiveLetters();
  const readable = (live || []).filter((p) => !p.locked && p.paras && p.paras.length);
  return {
    loading: live === null,
    latest: readable[0] || null,
    past: readable.slice(1).map((p) => ({ month: p.month, title: p.title })),
    // What a locked reader may still be shown: a real published title and month,
    // never a body.
    headline: (live && live[0]) || null,
  };
}

function JenniferByline() {
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 13 }}>
      <Avatar name="Jennifer Sichenzia" ring="rituals" />
      <div>
        <div style={{ fontSize: 14.5, color: "var(--text-primary)", fontWeight: 500 }}>Jennifer Sichenzia</div>
        <div style={{ fontSize: 13, color: "var(--text-tertiary)" }}>Founder &amp; CEO, VELOUR</div>
      </div>
    </div>
  );
}

// ---- feedback (paid) -------------------------------------------------------
const REACTIONS = ["This resonated", "Made me think", "Trying it this month"];
// The write-back. It POSTs now.
//
// It used to be `onClick={() => setSent(true)}` — a local flag and nothing else,
// under a confirmation reading "Jennifer reads every note." The note went
// nowhere. api/letter-reply.js is the endpoint that makes that sentence true;
// see migration 015.
//
// THE CONFIRMATION WAITS FOR THE SERVER. That is the whole point of the change:
// "Sent" is now a thing that happened rather than a thing the button says.
function LetterFeedback({ postId }) {
  const [reaction, setReaction] = React.useState(null);
  const [note, setNote] = React.useState("");
  const [sent, setSent] = React.useState(false);
  const [sending, setSending] = React.useState(false);
  const [error, setError] = React.useState(null);

  const submit = async () => {
    if (sending || (!reaction && !note.trim())) return;
    // No letter id means the letter came from nowhere the server knows about,
    // and there is nothing to attach a reply to.
    if (!postId) { setError("This letter can't take replies yet."); return; }
    setSending(true); setError(null);
    try {
      const s = window.velourAuth ? await window.velourAuth.ensureFresh() : null;
      if (!s || !s.access_token) throw new Error("Sign in again to write back.");
      const res = await fetch("/api/letter-reply", {
        method: "POST",
        headers: { "Content-Type": "application/json", Authorization: "Bearer " + s.access_token },
        body: JSON.stringify({ postId, reaction, note: note.trim() }),
      });
      const body = await res.json().catch(() => ({}));
      if (!res.ok) throw new Error(body.error || "That didn't send.");
      setSent(true);
    } catch (err) {
      setError(err.message);
    } finally {
      setSending(false);
    }
  };

  if (sent) {
    return (
      <Card padding={24} style={{ background: "var(--sage-fill)", border: "none" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10, color: "var(--sage-text)" }}>
          <i className="ph ph-check-circle" style={{ fontSize: 20 }}></i>
          <span style={{ fontSize: 15 }}>Sent — thank you. Jennifer reads every note.</span>
        </div>
      </Card>
    );
  }
  return (
    <Card padding={24}>
      <div style={{ fontFamily: "var(--font-serif)", fontSize: 19, color: "var(--text-primary)", letterSpacing: "-0.01em", marginBottom: 14 }}>What did this spark for you?</div>
      <div style={{ display: "flex", flexWrap: "wrap", gap: 8, marginBottom: 16 }}>
        {REACTIONS.map((r) => (
          <button key={r} onClick={() => setReaction(r)} style={{ padding: "8px 14px", borderRadius: 999, cursor: "pointer", fontFamily: "var(--font-sans)", fontSize: 13.5, fontWeight: 500, border: "var(--border-width) solid " + (reaction === r ? "var(--sage)" : "var(--border)"), background: reaction === r ? "var(--sage-fill)" : "var(--surface-card)", color: reaction === r ? "var(--sage-text)" : "var(--text-secondary)" }}>{r}</button>
        ))}
      </div>
      <textarea
        value={note}
        onChange={(e) => setNote(e.target.value)}
        placeholder="Write back to Jennifer…"
        rows={3}
        style={{ width: "100%", boxSizing: "border-box", padding: "12px 14px", borderRadius: 11, border: "var(--border-width) solid var(--border)", background: "var(--surface-card)", fontFamily: "var(--font-sans)", fontSize: 14, color: "var(--text-primary)", outline: "none", resize: "vertical" }}
      />
      {error && <div style={{ fontSize: 13.5, lineHeight: 1.5, color: "var(--terracotta-text)", marginTop: 12 }}>{error}</div>}
      <div style={{ display: "flex", justifyContent: "flex-end", marginTop: 14 }}>
        <button
          onClick={submit}
          disabled={sending || (!reaction && !note.trim())}
          style={{ display: "inline-flex", alignItems: "center", gap: 8, background: "var(--ink)", color: "var(--parchment)", border: "none", borderRadius: 999, padding: "11px 20px", fontFamily: "var(--font-sans)", fontSize: 14, fontWeight: 500, cursor: sending || (!reaction && !note.trim()) ? "default" : "pointer", opacity: sending || (!reaction && !note.trim()) ? 0.45 : 1 }}
        >
          <i className="ph ph-paper-plane-tilt" style={{ fontSize: 15 }}></i>{sending ? "Sending…" : "Send to Jennifer"}
        </button>
      </div>
    </Card>
  );
}

// ---- paid view -------------------------------------------------------------
function BlogPaid() {
  const { latest, past, loading } = useLetterView();

  // Nothing rendered while the request is in flight. A paying member must not see
  // "no letter yet" flash before the letter they are entitled to arrives.
  if (loading) return <main style={{ maxWidth: 760, margin: "0 auto", padding: "36px 32px 64px", minHeight: 240 }} />;

  // No letter has been published yet. Says so, rather than showing an old one:
  // the archive starts fresh (owner, 31 Aug 2026), so there is nothing behind
  // this page until Jennifer writes the first one.
  if (!latest) {
    return (
      <main style={{ maxWidth: 760, margin: "0 auto", padding: "36px 32px 64px", fontFamily: "var(--font-sans)" }}>
        <span style={{ display: "inline-flex", alignItems: "center", gap: 8, fontSize: 11.5, letterSpacing: "0.14em", textTransform: "uppercase", fontWeight: 500, color: "var(--sage-text)", background: "var(--sage-fill)", borderRadius: 999, padding: "6px 12px" }}><i className="ph ph-envelope-open" style={{ fontSize: 14 }}></i>Monthly letter</span>
        <Card padding={40} style={{ marginTop: 24 }}>
          <div style={{ fontFamily: "var(--font-serif)", fontWeight: 500, fontSize: 28, lineHeight: 1.2, letterSpacing: "-0.015em", color: "var(--text-primary)" }}>The first letter hasn’t been written yet.</div>
          <p style={{ fontSize: 16, lineHeight: 1.7, color: "var(--text-secondary)", maxWidth: 520, margin: "14px 0 0" }}>Jennifer writes once a month. When the first one is published it will open here, with the space to write back underneath it.</p>
          <div style={{ marginTop: 26 }}><JenniferByline /></div>
        </Card>
      </main>
    );
  }

  return (
    <main style={{ maxWidth: 760, margin: "0 auto", padding: "36px 32px 64px", fontFamily: "var(--font-sans)", display: "flex", flexDirection: "column", gap: 28 }}>
      <div>
        <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 22 }}>
          <span style={{ display: "inline-flex", alignItems: "center", gap: 8, fontSize: 11.5, letterSpacing: "0.14em", textTransform: "uppercase", fontWeight: 500, color: "var(--sage-text)", background: "var(--sage-fill)", borderRadius: 999, padding: "6px 12px" }}><i className="ph ph-envelope-open" style={{ fontSize: 14 }}></i>Monthly letter</span>
          <span style={{ fontSize: 13.5, color: "var(--text-tertiary)" }}>{latest.month} · members only</span>
        </div>
        <h1 style={{ fontFamily: "var(--font-serif)", fontWeight: 500, fontSize: 42, lineHeight: 1.1, letterSpacing: "-0.025em", color: "var(--text-primary)", margin: 0, textWrap: "balance" }}>{latest.title}</h1>
        <div style={{ marginTop: 24 }}><JenniferByline /></div>
      </div>
      <div style={{ fontSize: 18.5, lineHeight: 1.78, color: "var(--text-secondary)" }}>
        {latest.paras.map((p, i) => <p key={i} style={{ margin: i === 0 ? "0 0 24px" : "0 0 24px" }}>{p}</p>)}
        <p style={{ margin: 0, fontFamily: "var(--font-serif)", fontStyle: "italic", color: "var(--text-primary)" }}>— Jennifer</p>
      </div>
      <LetterFeedback postId={latest.id} />
      {/* Hidden when there is nothing behind it. With the letters coming from the
          table rather than a fixed array, "past letters" is empty on the day the
          first one is published — and a heading standing over nothing reads as a
          page that failed to load. */}
      {past.length > 0 && (
      <div>
        <div style={{ fontSize: 11.5, letterSpacing: "0.14em", textTransform: "uppercase", color: "var(--text-tertiary)", fontWeight: 500, margin: "0 0 14px" }}>Past letters</div>
        <div style={{ display: "flex", flexDirection: "column", gap: 1, background: "var(--divider)", borderRadius: 14, overflow: "hidden", border: "var(--border-width) solid var(--divider)" }}>
          {past.map((l) => (
            <div key={l.month} style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 14, padding: "16px 20px", background: "var(--surface-card)", cursor: "pointer" }}>
              <span style={{ fontSize: 15, color: "var(--text-primary)" }}>{l.title}</span>
              <span style={{ fontSize: 13, color: "var(--text-tertiary)", whiteSpace: "nowrap" }}>{l.month}</span>
            </div>
          ))}
        </div>
      </div>
      )}
    </main>
  );
}

// ---- free (locked) view ----------------------------------------------------
function BlogLocked({ onNavigate }) {
  // NO BLURRED LETTER BEHIND THE LOCK ANY MORE. There used to be one, built from
  // the hard-coded letter this file carried — and a CSS blur over real text is
  // not a paywall, it is a rendering choice with the letter still in the page.
  // With the built-in letter gone there is nothing to blur, which turns out to be
  // the right design: the lock states the offer plainly instead of teasing a
  // specific letter through frosted glass.
  //
  // What a locked reader IS shown is what api/posts.js is willing to send them —
  // the real month and title of the latest published letter, and no body. That is
  // a better nudge than a smudge, and it is honest: it names the thing they would
  // be unlocking.
  const { headline } = useLetterView();
  return (
    <main style={{ maxWidth: 860, margin: "0 auto", padding: "36px 32px 64px", fontFamily: "var(--font-sans)" }}>
      <span style={{ display: "inline-flex", alignItems: "center", gap: 8, fontSize: 11.5, letterSpacing: "0.14em", textTransform: "uppercase", fontWeight: 500, color: "var(--sage-text)", background: "var(--sage-fill)", borderRadius: 999, padding: "6px 12px" }}><i className="ph ph-envelope-open" style={{ fontSize: 14 }}></i>Monthly letter</span>
      <div style={{ marginTop: 24, borderRadius: 18, border: "var(--border-width) solid var(--border)", background: "var(--surface-card)", padding: "48px 40px", display: "flex", flexDirection: "column", alignItems: "center", textAlign: "center" }}>
        <span style={{ width: 56, height: 56, borderRadius: 16, background: "var(--ink)", display: "inline-flex", alignItems: "center", justifyContent: "center", marginBottom: 20 }}><i className="ph ph-lock-simple" style={{ fontSize: 26, color: "var(--parchment)" }}></i></span>
        {headline && (
          <div style={{ marginBottom: 18 }}>
            <div style={{ fontSize: 12.5, color: "var(--text-tertiary)", marginBottom: 6 }}>{headline.month}</div>
            <div style={{ fontFamily: "var(--font-serif)", fontWeight: 500, fontSize: 30, lineHeight: 1.15, letterSpacing: "-0.02em", color: "var(--text-primary)", maxWidth: 520, textWrap: "balance" }}>{headline.title}</div>
          </div>
        )}
        <div style={{ fontFamily: "var(--font-serif)", fontSize: 26, color: "var(--text-primary)", letterSpacing: "-0.01em", maxWidth: 460 }}>Jennifer’s monthly letter is part of the paid plan.</div>
        <p style={{ fontSize: 15.5, lineHeight: 1.6, color: "var(--text-secondary)", maxWidth: 440, margin: "12px 0 0" }}>A short, founder-written note each month — and the space to write back. Free keeps your full Education library; the letters are a members’ thing.</p>
        <button onClick={() => window.velourOpenPricing(onNavigate)} style={{ marginTop: 24, display: "inline-flex", alignItems: "center", gap: 8, background: "var(--ink)", color: "var(--parchment)", border: "none", borderRadius: 999, padding: "13px 24px", fontFamily: "var(--font-sans)", fontSize: 14.5, fontWeight: 500, cursor: "pointer", boxShadow: "0 4px 14px rgba(44,44,42,0.18)" }}>
          See what Premium adds <i className="ph ph-arrow-right" style={{ fontSize: 15 }}></i>
        </button>
      </div>
    </main>
  );
}

function DashBlog({ onNavigate }) {
  const plan = window.useVelourPlan ? window.useVelourPlan() : { monthlyLetter: !FRESH };
  return plan.monthlyLetter ? <BlogPaid /> : <BlogLocked onNavigate={onNavigate} />;
}
window.DashBlog = DashBlog;
