/* Assumption Mapper — React app */

const { useState, useEffect, useRef, useCallback, useMemo } = React;
const C = window.AM_CONTENT;

/* -------- Icons (Lucide-style inline SVG, 2px stroke, round caps) -------- */
const Icon = ({ name, size = 18 }) => {
  const stroke = "currentColor";
  const common = { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke, strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" };
  switch (name) {
    case "arrow-left":return <svg {...common}><path d="M19 12H5" /><path d="M12 19l-7-7 7-7" /></svg>;
    case "arrow-right":return <svg {...common}><path d="M5 12h14" /><path d="M12 5l7 7-7 7" /></svg>;
    case "download":return <svg {...common}><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /><path d="M7 10l5 5 5-5" /><path d="M12 15V3" /></svg>;
    case "thumbs-up":return <svg {...common}><path d="M7 10v12" /><path d="M15 5.88L14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H7V10l4-9c1.66 0 3 1.34 3 3v1.88z" /></svg>;
    case "thumbs-down":return <svg {...common}><path d="M17 14V2" /><path d="M9 18.12L10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H17v12l-4 9c-1.66 0-3-1.34-3-3v-1.88z" /></svg>;
    case "lock":return <svg {...common}><rect x="3" y="11" width="18" height="11" rx="2" /><path d="M7 11V7a5 5 0 0 1 10 0v4" /></svg>;
    default:return null;
  }
};

/* -------- Header -------- */
function Header({ onClear, canClear }) {
  return (
    <header className="am-header">
      <div className="am-header-inner">
        <img src="assets/logo-black.png" alt="The Product Bus" className="am-header-logo" />
        <span className="am-header-eyebrow">Assumption Mapper</span>
        <div className="am-header-spacer" />
        {canClear &&
        <button className="am-header-clear" onClick={onClear}>{C.ui.clearLabel}</button>
        }
        <a href={C.toolkitUrl} className="am-header-link">
          <Icon name="arrow-left" size={16} />
          Back to Toolkit
        </a>
      </div>
    </header>);

}

/* -------- Text field -------- */
function TextField({ value, onChange, placeholder, large }) {
  const max = 1000;
  const len = (value || "").length;
  return (
    <div className={"am-field" + (large ? " am-field-closing" : "")}>
      <textarea
        value={value || ""}
        maxLength={max}
        placeholder={placeholder || "Write here. There are no right answers."}
        onChange={(e) => onChange(e.target.value)} />
      
      <span className="am-field-count">{len}/{max}</span>
    </div>);

}

/* -------- Nav row -------- */
function NavRow({ onBack, onForward, forwardLabel = "Continue", forwardIcon = true, endOnly = false }) {
  return (
    <div className={"am-nav-row" + (endOnly ? " am-nav-row-end" : "")}>
      {!endOnly &&
      <button className="am-btn am-btn-ghost" onClick={onBack}>
          <Icon name="arrow-left" size={16} />
          Back
        </button>
      }
      <button className="am-btn am-btn-primary am-btn-large" onClick={onForward}>
        {forwardLabel}
        {forwardIcon && <Icon name="arrow-right" size={16} />}
      </button>
    </div>);

}

/* -------- Screen 1: Intro -------- */
function IntroScreen({ onContinue }) {
  return (
    <div className="am-canvas">
      <span className="am-intro-meta">
        <span className="am-intro-meta-dot" />
        Toolkit resource &middot; {C.timeEstimate}
      </span>
      <h1 className="am-display">{C.toolTitle}</h1>
      <div className="am-body am-body-l" style={{ marginTop: 12 }}>
        {C.intro.map((p, i) => <p key={i}>{p}</p>)}
      </div>
      {C.saveNotice &&
      <p className="am-save-notice">
        <span className="am-save-notice-icon"><Icon name="lock" size={15} /></span>
        {C.saveNotice}
      </p>
      }
      <div className="am-nav-row am-nav-row-end">
        <button className="am-btn am-btn-primary am-btn-large" onClick={onContinue}>
          Begin
          <Icon name="arrow-right" size={16} />
        </button>
      </div>
    </div>);

}

/* -------- Screens 3-7: Category -------- */
function CategoryScreen({ category, answers, setAnswer, onBack, onForward }) {
  return (
    <div className="am-canvas">
      <div className="am-eyebrow">Category {category.number} of 5</div>
      <h1 className="am-h2">{category.name}</h1>
      <p className="am-framing">{category.framing}</p>
      <div className="am-prompts">
        {category.prompts.map((p) =>
        <div key={p.id}>
            <div className="am-prompt-label">Prompt {p.id}</div>
            <h2 className="am-prompt-question">{p.question}</h2>
            <TextField value={answers[p.id]} onChange={(v) => setAnswer(p.id, v)} />
          </div>
        )}
      </div>
      <NavRow onBack={onBack} onForward={onForward} />
    </div>);

}

/* -------- Lightweight answer evaluation (heuristic, on-device) -------- */
const HEDGE_RE = /\b(i think|i guess|i hope|hopefully|maybe|perhaps|probably|possibly|i feel|feel like|i assume|i reckon|should be|might be|could be|i believe|kind of|sort of|not (really )?sure|pretty sure|i'd say|i would say|i suppose)\b/i;

function evaluateAnswer(text) {
  const t = (text || "").trim();
  if (!t) return { state: "empty", flags: [] };
  const words = t.split(/\s+/).filter(Boolean);
  const flags = [];
  if (words.length < 12) flags.push("thin");
  if (HEDGE_RE.test(t)) flags.push("hedged");
  return { state: flags.length ? "flag" : "ok", flags };
}

function nudgeFor(flags) {
  const thin = flags.includes("thin");
  const hedged = flags.includes("hedged");
  if (thin && hedged) return "Short, and written in maybes. A strong candidate to test first.";
  if (hedged) return "Written in maybes. That is usually the sound of an assumption, not evidence.";
  if (thin) return "Brief. If you cannot say more yet, that is worth noticing.";
  return null;
}

/* -------- Recap of all answers (shown on closing screen) -------- */
function AnswerRecap({ answers, onJump }) {
  let brief = 0, hedged = 0, blank = 0;
  C.categories.forEach((cat) => cat.prompts.forEach((p) => {
    const ev = evaluateAnswer(answers[p.id]);
    if (ev.state === "empty") blank++;
    else { if (ev.flags.includes("thin")) brief++; if (ev.flags.includes("hedged")) hedged++; }
  }));
  const flaggedTotal = brief + hedged + blank;
  const parts = [];
  if (brief) parts.push(`${brief} brief`);
  if (hedged) parts.push(`${hedged} hedged`);
  if (blank) parts.push(`${blank} left blank`);
  const breakdown = parts.length > 1
    ? parts.slice(0, -1).join(", ") + " and " + parts[parts.length - 1]
    : parts[0];

  return (
    <div className="am-recap">
      <p className="am-recap-intro">
        Here is everything you wrote, in one place. Read back over it before you choose. The bets that felt thin to write are usually the ones worth testing first.
      </p>

      <div className={"am-recap-summary" + (flaggedTotal ? "" : " is-clear")}>
        <span className="am-recap-summary-mark">{flaggedTotal ? "!" : "\u2713"}</span>
        <div className="am-recap-summary-body">
          {flaggedTotal ? (
            <React.Fragment>
              <p className="am-recap-summary-lead">A quick read flagged {breakdown}.</p>
              <p className="am-recap-summary-sub">These are not wrong, just worth a second look. Brief or hedged answers are often where your real bets are hiding. Use them to choose what to test first.</p>
            </React.Fragment>
          ) : (
            <React.Fragment>
              <p className="am-recap-summary-lead">Your answers read solid.</p>
              <p className="am-recap-summary-sub">Still, certainty on paper is not the same as evidence. Pick the two or three bets you would least want to be wrong about.</p>
            </React.Fragment>
          )}
        </div>
      </div>

      {C.categories.map((cat, i) => (
        <div key={cat.id} className="am-recap-cat">
          <div className="am-recap-cat-head">
            <span className="am-recap-cat-name">
              <span className="am-recap-cat-num">{cat.number}</span>
              {cat.name}
            </span>
            <button className="am-recap-edit" onClick={() => onJump(i)}>Edit</button>
          </div>
          <div className="am-recap-items">
            {cat.prompts.map((p) => {
              const a = (answers[p.id] || "").trim();
              const ev = evaluateAnswer(a);
              const nudge = ev.state === "flag" ? nudgeFor(ev.flags) : null;
              return (
                <div key={p.id} className="am-recap-item">
                  <p className="am-recap-q">{p.question}</p>
                  {a ? (
                    <React.Fragment>
                      <p className="am-recap-a">{a}</p>
                      {ev.flags.length > 0 && (
                        <div className="am-recap-flags">
                          {ev.flags.includes("thin") && <span className="am-recap-flag is-thin">Brief</span>}
                          {ev.flags.includes("hedged") && <span className="am-recap-flag is-hedged">Hedged</span>}
                          <span className="am-recap-nudge">{nudge}</span>
                        </div>
                      )}
                    </React.Fragment>
                  ) : (
                    <p className="am-recap-a is-empty">Left blank. <button className="am-recap-inline-link" onClick={() => onJump(i)}>Add an answer</button></p>
                  )}
                </div>
              );
            })}
          </div>
        </div>
      ))}
    </div>
  );
}

/* -------- Screen 8: Closing reflection -------- */
function ClosingScreen({ value, onChange, answers, onJump, onBack, onForward }) {
  return (
    <div className="am-canvas am-canvas-wide">
      <div className="am-eyebrow">Closing reflection</div>
      <h1 className="am-h2">{C.closing.framing}</h1>
      <p className="am-framing">A pause before you export. Below is your full map so far, so you can weigh the bets against each other rather than from memory.</p>

      <AnswerRecap answers={answers} onJump={onJump} />

      <div className="am-prompts am-closing-prompt">
        <div>
          <h2 className="am-prompt-question" style={{ fontWeight: "500" }}>{C.closing.question}</h2>
          <TextField value={value} onChange={onChange} large />
        </div>
      </div>
      <NavRow onBack={onBack} onForward={onForward} forwardLabel="Continue to export" />
    </div>);

}

/* -------- PDF render template (hidden, off-screen) -------- */
function PdfTemplate({ answers, closing }) {
  return (
    <div className="am-pdf-shell" id="am-pdf-shell">
      {/* Cover */}
      <div className="am-pdf-page">
        <div className="am-pdf-header">
          <div className="am-pdf-brand">THE PRODUCT BUS<span className="am-pdf-brand-dot">.</span></div>
          <div className="am-pdf-url">{C.url}</div>
        </div>
        <h1 className="am-pdf-tool-title">Your assumption map</h1>
        <p className="am-pdf-tool-sub">{C.toolTitle}</p>
        <div style={{ marginTop: 32, fontSize: 12, color: "#4A4A4A", lineHeight: 1.6 }}>
          <p style={{ margin: "0 0 16px" }}>{C.disclaimer}</p>
          <p style={{ margin: 0 }}>
            <strong>How to use this document:</strong> revisit it when your direction changes, or when evidence has shifted what you believe about your customer, problem, pricing, solution, or timing. Update it. Save the new version. The map is meant to evolve.
          </p>
        </div>
        <div className="am-pdf-footer">
          <span>Map exported {new Date().toLocaleDateString("en-AU", { day: "numeric", month: "long", year: "numeric" })}</span>
          <span>{C.url}</span>
        </div>
      </div>

      {/* Categories */}
      {C.categories.map((cat) =>
      <div key={cat.id} className="am-pdf-page">
          <div className="am-pdf-header">
            <div className="am-pdf-brand">THE PRODUCT BUS<span className="am-pdf-brand-dot">.</span></div>
            <div className="am-pdf-url">{C.url}</div>
          </div>
          <div className="am-pdf-cat-eyebrow">Category {cat.number} of 5</div>
          <h2 className="am-pdf-cat-title">{cat.name}</h2>
          <p className="am-pdf-cat-framing">{cat.framing}</p>
          {cat.prompts.map((p) => {
          const a = (answers[p.id] || "").trim();
          return (
            <div key={p.id} className="am-pdf-prompt">
                <p className="am-pdf-prompt-q">{p.question}</p>
                <div className={"am-pdf-prompt-a" + (a ? "" : " is-empty")}>{a || "(left blank)"}</div>
              </div>);

        })}
          <div className="am-pdf-footer">
            <span>{cat.name}</span>
            <span>{C.url}</span>
          </div>
        </div>
      )}

      {/* Closing */}
      <div className="am-pdf-page">
        <div className="am-pdf-header">
          <div className="am-pdf-brand">THE PRODUCT BUS<span className="am-pdf-brand-dot">.</span></div>
          <div className="am-pdf-url">{C.url}</div>
        </div>
        <div className="am-pdf-cat-eyebrow">Closing reflection</div>
        <h2 className="am-pdf-cat-title">The bets you test first</h2>
        <p className="am-pdf-cat-framing">{C.closing.framing}</p>
        <div className="am-pdf-prompt">
          <p className="am-pdf-prompt-q">{C.closing.question}</p>
          <div className={"am-pdf-prompt-a" + ((closing || "").trim() ? "" : " is-empty")}>
            {(closing || "").trim() || "(left blank)"}
          </div>
        </div>
        <div className="am-pdf-footer">
          <span>End of assumption map</span>
          <span>{C.url}</span>
        </div>
      </div>
    </div>);

}

/* -------- Screen 9: Export -------- */
function ExportScreen({ answers, closing, onBack, blankCount }) {
  const [feedback, setFeedback] = useState(null);
  const [showBlankPrompt, setShowBlankPrompt] = useState(false);
  const [exporting, setExporting] = useState(false);

  // Fire tool_completed once when the founder reaches the export step.
  // Opaque counters only, never any written content.
  const completedRef = useRef(false);
  useEffect(() => {
    if (completedRef.current) return;
    completedRef.current = true;
    let filled = 0;
    C.categories.forEach((cat) => cat.prompts.forEach((p) => {
      if ((answers[p.id] || "").trim()) filled++;
    }));
    if (window.AMBridge) {
      window.AMBridge.completed({
        assumptions_count: filled,
        closing_filled: (closing || "").trim() ? 1 : 0
      });
    }
  }, []);

  const runExport = useCallback(async () => {
    setExporting(true);
    const el = document.getElementById("am-pdf-shell");
    if (!el || !window.html2pdf) {
      window.print();
      setExporting(false);
      return;
    }
    // Briefly move element into view bounds so html2pdf can measure cleanly
    const prevTop = el.style.top;
    el.style.top = "0px";
    el.style.visibility = "hidden";
    try {
      await window.html2pdf().
      set({
        margin: 0,
        filename: "assumption-map.pdf",
        image: { type: "jpeg", quality: 0.98 },
        html2canvas: { scale: 2, useCORS: true, backgroundColor: "#ffffff" },
        jsPDF: { unit: "pt", format: "a4", orientation: "portrait" },
        pagebreak: { mode: ["css", "legacy"], before: ".am-pdf-page", avoid: ".am-pdf-prompt" }
      }).
      from(el).
      save();
    } catch (err) {
      console.error("PDF export failed, falling back to print", err);
      window.print();
    } finally {
      el.style.top = prevTop;
      el.style.visibility = "";
      setExporting(false);
    }
  }, []);

  const onExportClick = () => {
    if (blankCount > 0) {
      setShowBlankPrompt(true);
    } else {
      runExport();
    }
  };

  return (
    <div className="am-canvas">
      <div className="am-eyebrow">Your assumption map</div>
      <h1 className="am-display">Your assumption map is ready.</h1>

      <div className="am-body am-body-l">
        <p>{C.exportPrompt}</p>
        <p>{C.exportFollowup}</p>
      </div>

      <div className="am-export-actions">
        <button className="am-btn am-btn-primary am-btn-large" onClick={onExportClick} disabled={exporting}>
          <Icon name="download" size={18} />
          {exporting ? "Preparing your PDF…" : "Export as PDF"}
        </button>
        <button className="am-btn am-btn-ghost" onClick={onBack}>
          <Icon name="arrow-left" size={16} />
          Back to closing reflection
        </button>
      </div>

      <div className="am-feedback">
        <span className="am-feedback-label">Was this tool useful?</span>
        <div className="am-feedback-buttons">
          <button
            className={"am-thumb" + (feedback === "up" ? " is-active" : "")}
            onClick={() => setFeedback("up")}
            aria-label="Yes, useful"
            aria-pressed={feedback === "up"}>
            <Icon name="thumbs-up" /></button>
          <button
            className={"am-thumb" + (feedback === "down" ? " is-active" : "")}
            onClick={() => setFeedback("down")}
            aria-label="No, not useful"
            aria-pressed={feedback === "down"}>
            <Icon name="thumbs-down" /></button>
        </div>
        {feedback && <span className="am-feedback-thanks">Thanks. Noted.</span>}
      </div>

      <h2 className="am-next-heading">What to do next</h2>
      <div className="am-next-grid">
        {C.nextActions.map((t) =>
        t.live ?
        <a key={t.id} href={t.href || "#"} className="am-tile am-tile-live">
              <span className="am-tile-pill">Next step</span>
              <h3 className="am-tile-title">{t.name}</h3>
              <p className="am-tile-desc">{t.description}</p>
              <span className="am-tile-arrow"><Icon name="arrow-right" size={16} /></span>
            </a> :

        <div key={t.id} className="am-tile am-tile-placeholder" aria-disabled="true">
              <span className="am-tile-pill">Recommendations coming soon</span>
              <h3 className="am-tile-title">{t.name}</h3>
              <p className="am-tile-desc">{t.description}</p>
            </div>

        )}
      </div>

      <div className="am-return-row">
        <a href={C.toolkitUrl} className="am-btn am-btn-secondary am-btn-large">
          Return to the Toolkit
          <Icon name="arrow-right" size={16} />
        </a>
      </div>

      {showBlankPrompt &&
      <div className="am-modal-scrim" role="dialog" aria-modal="true">
          <div className="am-modal">
            <h3>You have {blankCount} blank {blankCount === 1 ? "prompt" : "prompts"}.</h3>
            <p>Export anyway, or go back and fill them in? Either is fine. Empty prompts will appear in your PDF as a note that they were left blank.</p>
            <div className="am-modal-actions">
              <button className="am-btn" onClick={() => setShowBlankPrompt(false)}>Go back</button>
              <button className="am-btn am-btn-primary" onClick={() => {setShowBlankPrompt(false);runExport();}}>Export anyway</button>
            </div>
          </div>
        </div>
      }
    </div>);

}

/* -------- Root App -------- */
function App() {
  // Step 0 = intro, 1-5 = categories, 6 = closing, 7 = export
  const [step, setStep] = useState(0);
  const [answers, setAnswers] = useState({});
  const [closing, setClosing] = useState("");
  const [showClear, setShowClear] = useState(false);

  // Persistence bridge state.
  // hydratedRef: have we received the first progress_loaded yet?
  // applyingRemoteRef: the next [answers, closing] change came from the host
  //   (hydration or a clear reset), so skip echoing it back as a save.
  const hydratedRef = useRef(false);
  const applyingRemoteRef = useRef(false);

  const setAnswer = useCallback((id, value) => {
    setAnswers((prev) => ({ ...prev, [id]: value }));
  }, []);

  // Mount: register hydration handler, request stored progress, fire lifecycle.
  useEffect(() => {
    const B = window.AMBridge;
    if (!B) return;
    B.onProgressLoaded((state) => {
      applyingRemoteRef.current = true;
      if (state && typeof state === "object") {
        setAnswers(state.answers && typeof state.answers === "object" ? state.answers : {});
        setClosing(typeof state.closing === "string" ? state.closing : "");
      } else {
        // null => start empty / just cleared
        setAnswers({});
        setClosing("");
      }
      hydratedRef.current = true;
    });
    B.requestProgress();
    B.started({});
  }, []);

  // Save the whole map on meaningful change. The host debounces ~700ms, so
  // posting on every keystroke is fine. We wait until after first hydration
  // and skip the change that hydration itself caused.
  useEffect(() => {
    const B = window.AMBridge;
    if (!B) return;
    if (!hydratedRef.current) return;
    if (applyingRemoteRef.current) { applyingRemoteRef.current = false; return; }
    B.saveProgress({ v: 1, answers, closing });
  }, [answers, closing]);

  const go = (n) => {
    setStep(n);
    window.scrollTo({ top: 0, behavior: "smooth" });
  };

  const doClear = () => {
    if (window.AMBridge) window.AMBridge.clearProgress();
    // Optimistic local reset; the host's progress_loaded {null} reply is
    // idempotent with this. Skip the save echo from this reset.
    applyingRemoteRef.current = true;
    setAnswers({});
    setClosing("");
    setShowClear(false);
    go(0);
  };

  const blankCount = useMemo(() => {
    let n = 0;
    C.categories.forEach((cat) => {
      cat.prompts.forEach((p) => {
        if (!(answers[p.id] || "").trim()) n++;
      });
    });
    if (!(closing || "").trim()) n++;
    return n;
  }, [answers, closing]);

  const hasContent = useMemo(() => {
    if ((closing || "").trim()) return true;
    return C.categories.some((cat) => cat.prompts.some((p) => (answers[p.id] || "").trim()));
  }, [answers, closing]);

  let screen;
  if (step === 0) {
    screen = <IntroScreen onContinue={() => go(1)} />;
  } else if (step >= 1 && step <= 5) {
    const cat = C.categories[step - 1];
    screen =
    <CategoryScreen
      category={cat}
      answers={answers}
      setAnswer={setAnswer}
      onBack={() => go(step - 1)}
      onForward={() => go(step + 1)} />;


  } else if (step === 6) {
    screen =
    <ClosingScreen
      value={closing}
      onChange={setClosing}
      answers={answers}
      onJump={(catIndex) => go(catIndex + 1)}
      onBack={() => go(5)}
      onForward={() => go(7)} />;


  } else {
    screen =
    <ExportScreen
      answers={answers}
      closing={closing}
      blankCount={blankCount}
      onBack={() => go(6)} />;


  }

  return (
    <div className="am-app pb">
      <Header onClear={() => setShowClear(true)} canClear={hasContent} />
      {screen}
      <PdfTemplate answers={answers} closing={closing} />

      {showClear &&
      <div className="am-modal-scrim" role="dialog" aria-modal="true">
        <div className="am-modal">
          <h3>{C.ui.clearTitle}</h3>
          <p>{C.ui.clearBody}</p>
          <div className="am-modal-actions">
            <button className="am-btn" onClick={() => setShowClear(false)}>{C.ui.clearCancel}</button>
            <button className="am-btn am-btn-danger" onClick={doClear}>{C.ui.clearConfirm}</button>
          </div>
        </div>
      </div>
      }
    </div>);

}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);