// app.jsx — Shell.
// Flow: splash → website → scanning → call → review → provisioning
//
//  1. Splash: "Start" button (unlocks audio, grants mic)
//  2. Website: URL input → triggers n8n crawl
//  3. Scanning: crawl loader → pre-fills known fields
//  4. Call: Vapi voice agent conducts the full onboarding interview
//           form fills live as agent calls save_section tool
//  5. Review: confirm all collected data
//  6. Provisioning: submit to intake_forms → done

const { useState: useState_p, useEffect: useEffect_p, useRef: useRef_p,
        useMemo: useMemo_p, useCallback: useCallback_p } = React;

const STORAGE_KEY = "cfm_onboarding_v4";
const INITIAL     = {};
const CFG         = () => window.CFM_CONFIG || {};

// ─── Splash ──────────────────────────────────────────────────────────────────
function IntroSplash({ launching, hide, onStart }) {
  const b = CFG().brand || {};
  return (
    <div className={`intro ${hide ? "hide" : ""}`}>
      <div className="intro-grid"></div>
      <div className="intro-brand">
        <div className="brand-mark">{b.mark || "CF"}</div>
        <div className="brand-text">
          <strong>{b.name || "Client Focused Media"}</strong>
          <span>{b.subtitle || "Website Onboarding"}</span>
        </div>
      </div>
      <div className="intro-inner">
        <div className="intro-eyebrow">Onboarding · Website Project</div>
        <h1 className="intro-title">Let's build your<br /><em>brand's future.</em></h1>
        <p className="intro-sub">
          One conversation. {b.agentName || "Momentum"} scans your current site,
          pre-fills what it can, then walks you through the rest —
          all by voice, in about 30–40 minutes.
        </p>
        <div className={`intro-mic-wrap ${launching ? "launching" : ""}`}>
          <div className="intro-mic-ring r1"></div>
          <div className="intro-mic-ring r2"></div>
          <div className="intro-mic-ring r3"></div>
          <button className="intro-mic-btn" onClick={onStart} aria-label="Start">
            <Icon.Mic />
          </button>
        </div>
        <div className="intro-cta-label">Tap to begin</div>
        <div className="intro-cta-hint">Allow mic access when prompted · you can also type</div>
      </div>
    </div>
  );
}

// ─── Website entry ────────────────────────────────────────────────────────────
function WebsiteEntryScreen({ onScan, onSkip }) {
  const [url, setUrl] = useState_p("");
  const b = CFG().brand || {};

  const submit = (e) => {
    e && e.preventDefault();
    let u = url.trim();
    if (!u) return;
    if (!/^https?:\/\//i.test(u)) u = "https://" + u;
    onScan(u);
  };

  return (
    <div className="website-entry">
      <div className="website-entry-orb"><Orb size={84} state="speaking" /></div>
      <div className="website-entry-eyebrow">{b.agentName || "Momentum"} · let's begin</div>
      <h2 className="website-entry-title">What's your current website?</h2>
      <p className="website-entry-sub">
        I'll scan it in seconds and pre-fill your contact info, socials, brand
        colours, and pages — so we can move through everything faster together.
      </p>
      <form className="website-entry-form" onSubmit={submit}>
        <div className="website-url-input-wrap">
          <span className="url-prefix">https://</span>
          <input
            className="website-url-input" type="text" placeholder="yourcompany.com"
            value={url.replace(/^https?:\/\//i, "")}
            onChange={(e) => setUrl(e.target.value)}
            autoFocus autoComplete="url" spellCheck={false}
          />
        </div>
        <button type="submit" className="btn-primary big website-scan-btn" disabled={!url.trim()}>
          Scan &amp; pre-fill <Icon.Arrow />
        </button>
      </form>
      <button className="website-skip-link" onClick={onSkip}>
        I don't have a website yet — skip
      </button>
    </div>
  );
}

// ─── Scanning (full-screen loader) ────────────────────────────────────────────
const SCAN_STEPS = [
  "Opening your website…",
  "Reading your homepage…",
  "Following links to your key pages…",
  "Pulling contact details & socials…",
  "Sampling your brand colours…",
  "Analysing your services & tone…",
  "Putting it all together…",
];
function ScanningScreen({ url, onDone, onSkip }) {
  const [stepIdx, setStepIdx] = useState_p(0);
  const [pct,     setPct]     = useState_p(6);
  const doneRef = useRef_p(false);
  const displayUrl = (url || "").replace(/^https?:\/\//i, "").replace(/\/$/, "");

  useEffect_p(() => {
    let step = 0;
    const id = setInterval(() => {
      if (doneRef.current) return;
      step = Math.min(step + 1, SCAN_STEPS.length - 1);
      setStepIdx(step);
      setPct(Math.round(((step + 1) / SCAN_STEPS.length) * 90));
    }, 1300);
    return () => clearInterval(id);
  }, []);

  useEffect_p(() => {
    if (!url) { setTimeout(() => onDone({}), 700); return; }
    const crawlUrl = CFG().CRAWL_WEBHOOK_URL || "/crawl";
    const headers  = { "Content-Type": "application/json" };
    if (CFG().WEBHOOK_TOKEN) headers["x-cfm-token"] = CFG().WEBHOOK_TOKEN;
    fetch(crawlUrl, { method: "POST", headers, body: JSON.stringify({ url }) })
      .then((r) => r.text())
      .then((text) => {
        let obj = {};
        try { obj = JSON.parse(text); } catch (e) {}
        for (const k of ["output", "data", "json", "result"])
          if (obj && typeof obj === "object" && obj[k] && typeof obj[k] !== "string") obj = obj[k];
        if (Array.isArray(obj)) obj = obj[0] || {};
        doneRef.current = true; setPct(100);
        setTimeout(() => onDone({
          updates:     obj.updates     || {},
          crawlContext:obj.crawlContext || {},
          reply:       obj.reply       || "",
        }), 550);
      })
      .catch(() => { doneRef.current = true; onDone({}); });
  }, [url]);

  return (
    <div className="scan-overlay">
      <div className="scan-overlay-inner">
        <Orb size={100} state="thinking" />
        <div className="scan-overlay-url">{displayUrl}</div>
        <div className="scan-overlay-step">{SCAN_STEPS[Math.min(stepIdx, SCAN_STEPS.length - 1)]}</div>
        <div className="scan-bar-wrap">
          <div className="scan-bar">
            <div className="scan-fill" style={{ width: pct + "%" }} />
          </div>
          <span className="scan-pct">{pct}%</span>
        </div>
        <button className="scan-skip-btn" onClick={onSkip}>Taking too long? Skip &amp; continue</button>
      </div>
    </div>
  );
}

// ─── Call phase (Vapi in progress) ────────────────────────────────────────────
function CallScreen({ agent, data, onEndCall }) {
  const {
    callStatus, transcript, sectionsCompleted, currentSection,
    agentSpeaking, userSpeaking, volumeLevel, justSet, pct,
  } = agent;

  const cfg = CFG();
  const agentName = (cfg.brand || {}).agentName || "Momentum";
  const totalSections = 17;

  // Status hint shown below the orb
  const hint = {
    idle:       `${agentName} is ready to start.`,
    connecting: `Connecting to ${agentName}…`,
    active:     agentSpeaking ? `${agentName} is speaking…`
                : userSpeaking ? "I'm listening…"
                : "Go ahead and speak — or wait for the next question.",
    ending:     "Wrapping up…",
    ended:      "Call complete — reviewing your answers.",
    error:      "Something went wrong. Try ending and restarting.",
  }[callStatus] || "";

  return (
    <div className="call-screen">
      <div className="call-screen-inner">
        {/* Orb + status */}
        <div className="call-orb-wrap" style={{ "--volume": volumeLevel }}>
          <Orb size={120} state={
            callStatus === "connecting" ? "thinking"
            : callStatus === "ending"   ? "thinking"
            : agentSpeaking             ? "speaking"
            : userSpeaking              ? "listening"
            : callStatus === "active"   ? "idle"
            : "idle"
          } />
        </div>
        <p className="call-hint">{hint}</p>

        {/* VapiCallWidget handles transcript, progress bar, and controls */}
        <VapiCallWidget agent={agent} onEndCall={onEndCall} />
      </div>
    </div>
  );
}

// Section label map (same as in agent.jsx but needed here too)
const SECTION_LABELS = {
  basic_info: "Basic Information", heart_of_business: "Heart of Your Business",
  problem_you_solve: "The Problem You Solve", life_before_after: "Life Before & After",
  your_difference: "Your Difference", ideal_customer: "Ideal Customer",
  identity: "Identity", industry_perspective: "Industry Perspective",
  brand_personality: "Brand Personality", language_messaging: "Language & Messaging",
  brand_style: "Brand Style", design_branding: "Design & Branding",
  reference_sites: "Reference Websites", content_functions: "Content & Functions",
  dev_logistics: "Development & Logistics", public_info: "Public Info", legal: "Legal",
};

// ─── Field / Section renderers ────────────────────────────────────────────────
function Field({ field, value, onChange, isActive, justSet }) {
  const cls = "field " + (field.width === "half" ? "field-half" : "field-full") +
              (isActive ? " focused" : "") + (justSet ? " just-set" : "");
  const filled = window.isFilled(value);
  const inner = (() => {
    switch (field.type) {
      case "text": case "email": case "tel": case "url":
        return <TextField id={field.id} value={value} onChange={onChange}
                          placeholder={field.placeholder} type={field.type} />;
      case "textarea":
        return <TextAreaField id={field.id} value={value} onChange={onChange}
                              placeholder={field.placeholder} rows={field.rows || 3} />;
      case "radio":
        return <RadioGroup value={value} onChange={onChange} options={field.options}
                           cols={field.options.length > 4 ? 2 : 1} justSet={justSet} />;
      case "checkbox":
        return <CheckboxGroup value={value} onChange={onChange} options={field.options}
                              cols={field.options.length > 6 ? 3 : 2} justSet={justSet} />;
      case "limited-checkbox":
        return <LimitedCheckbox value={value} onChange={onChange} options={field.options}
                                max={field.max || 3} justSet={justSet} />;
      case "file":
        return <FileDrop value={value} onChange={onChange} hint={field.hint} />;
      case "color":
        return <ColorField value={value} onChange={onChange} />;
      default:
        return null;
    }
  })();
  return (
    <div className={cls}>
      <label className="field-label" htmlFor={field.id}>
        {field.label.replace(/\s*\*$/, "")}
        {field.required && <span className="req">*</span>}
        {filled && !isActive && <span className="filled-tag"><Icon.Check /> filled</span>}
      </label>
      {field.hint && field.type !== "file" && (
        <div className="field-hint"><span className="bulb"><Icon.Bulb /></span> {field.hint}</div>
      )}
      {inner}
    </div>
  );
}

function SectionPanel({ section, data, isActive, justSet, onChange, panelRef }) {
  const filledCount = section.fields.reduce((n, f) => n + (window.isFilled(data[f.id]) ? 1 : 0), 0);
  const isComplete  = filledCount === section.fields.length;
  const cls = "panel " +
    (isActive    ? "active"    :
     isComplete  ? "complete"  :
     filledCount > 0 ? ""      : "idle");
  return (
    <section ref={panelRef} className={cls} data-section-id={section.id}>
      <div className="panel-head">
        <div className="panel-head-left">
          <span className="panel-num">{section.num}</span>
          <div className="panel-titles">
            <div className="eyebrow">{section.eyebrow}</div>
            <h2>{section.title}</h2>
            <p className="blurb">{section.blurb}</p>
          </div>
        </div>
        <span className="panel-status">
          <span className="status-dot"></span>
          {isActive ? "Active" : isComplete ? "Complete"
            : filledCount > 0 ? `${filledCount} / ${section.fields.length}`
            : `${section.minutes} min`}
        </span>
      </div>
      <div className="form">
        {section.fields.map((f) => (
          <Field key={f.id} field={f} value={data[f.id]}
                 onChange={(v) => onChange(f.id, v)}
                 isActive={false}
                 justSet={justSet[f.id]} />
        ))}
      </div>
    </section>
  );
}

// ─── Review ───────────────────────────────────────────────────────────────────
function formatValue(field, v) {
  if (!window.isFilled(v)) return null;
  if (field.type === "file") return (Array.isArray(v) ? v : []).map((f) => f.name || String(f)).join(", ");
  if (Array.isArray(v)) return v.join(", ");
  return String(v);
}
function ReviewScreen({ data, onEditSection, onConfirm, submitting }) {
  const missingRequired = useMemo_p(() => {
    const miss = [];
    for (const s of SECTIONS)
      for (const f of s.fields)
        if (f.required && !window.isFilled(data[f.id])) miss.push({ label: f.label });
    return miss;
  }, [data]);
  return (
    <div className="shell review">
      <div className="main-eyebrow"><Icon.Check /> Almost there</div>
      <h1 className="main-h1">Let's confirm everything.</h1>
      <p className="main-sub">
        Give this a once-over. Tap <strong>Edit</strong> on any section, then confirm to submit.
        You can also upload logo and brand files on the relevant sections.
      </p>
      {missingRequired.length > 0 && (
        <div className="review-warn">
          <strong>{missingRequired.length} required {missingRequired.length === 1 ? "field" : "fields"} still empty.</strong>
          <span> {missingRequired.slice(0, 5).map((m) => m.label.replace(/\s*\*$/, "")).join(", ")}{missingRequired.length > 5 ? "…" : ""}.</span>
        </div>
      )}
      <div className="review-list">
        {SECTIONS.map((s) => {
          const rows = s.fields.map((f) => ({ f, val: formatValue(f, data[f.id]) })).filter((r) => r.val != null);
          return (
            <div key={s.id} className="review-section">
              <div className="review-section-head">
                <div><span className="review-num">{s.num}</span> <h3>{s.title}</h3></div>
                <button className="review-edit" onClick={() => onEditSection(s.id)}>
                  <Icon.Edit /> Edit
                </button>
              </div>
              {rows.length === 0
                ? <div className="review-empty">Nothing captured for this section yet.</div>
                : (
                  <dl className="review-rows">
                    {rows.map((r) => (
                      <div key={r.f.id} className="review-row">
                        <dt>{r.f.label.replace(/\s*\*$/, "")}</dt>
                        <dd>{r.val}</dd>
                      </div>
                    ))}
                  </dl>
                )}
            </div>
          );
        })}
      </div>
      <div className="review-actions">
        <button className="btn-ghost" onClick={() => onEditSection(SECTIONS[0].id)}>
          Back to form
        </button>
        <button className="btn-primary big" onClick={onConfirm} disabled={submitting}>
          {submitting ? "Saving…" : <>Confirm &amp; submit <Icon.Arrow /></>}
        </button>
      </div>
    </div>
  );
}

// ─── Provisioning / done ──────────────────────────────────────────────────────
function ProvisioningScreen({ result }) {
  const ok = !(result && result.failed);
  const cfg = CFG();
  const mins = cfg.provisioningMinutes || { min: 1, max: 2 };
  return (
    <div className="shell provisioning">
      <Orb size={120} state={ok ? "speaking" : "thinking"} />
      <div className="main-eyebrow" style={{ marginTop: 28 }}>
        <Icon.Check /> {ok ? "All done" : "Saved locally"}
      </div>
      <h1 className="main-h1">{ok ? "You're all set! 🎉" : "Answers saved."}</h1>
      <p className="main-sub">
        {ok
          ? "Your onboarding is in — your account manager will review everything and " +
            "reach out within one business day to kick off the build."
          : `We stored everything locally. There was a hiccup saving to our system (${result && result.message}); your account manager has it covered.`}
      </p>
      {ok && result && result.rowId
        ? <p className="main-sub" style={{ opacity: 0.6 }}>Reference #{result.rowId}</p>
        : null}
    </div>
  );
}

// ─── Edit form panel (shown when user taps "Edit" from review) ────────────────
function EditFormOverlay({ sections, data, sectionId, justSet, onChange, onDone }) {
  const panelRefs = useRef_p({});
  useEffect_p(() => {
    const el = panelRefs.current[sectionId];
    if (el) setTimeout(() => el.scrollIntoView({ behavior: "smooth", block: "start" }), 80);
  }, [sectionId]);
  return (
    <div className="shell edit-overlay">
      <div className="main-eyebrow"><Icon.Edit /> Editing</div>
      <h1 className="main-h1">Update your answers.</h1>
      <p className="main-sub">Make any changes below, then tap Done to return to the review.</p>
      <div className="sections">
        {sections.map((s) => (
          <SectionPanel key={s.id} section={s} data={data}
            isActive={s.id === sectionId}
            justSet={justSet} onChange={onChange}
            panelRef={(el) => { panelRefs.current[s.id] = el; }} />
        ))}
      </div>
      <div className="edit-done-bar">
        <button className="btn-primary big" onClick={onDone}>Done — back to review <Icon.Arrow /></button>
      </div>
    </div>
  );
}

// ─── App ──────────────────────────────────────────────────────────────────────
function App() {
  const { data, update, status, lastSaved } = useAutosave(STORAGE_KEY, INITIAL);
  const dataRef = useRef_p(data);
  useEffect_p(() => { dataRef.current = data; }, [data]);

  // phases: splash | website | scanning | call | review | editing | provisioning
  const [phase,       setPhase]       = useState_p("splash");
  const [launching,   setLaunching]   = useState_p(false);
  const [confettiTick,setConfettiTick]= useState_p(0);
  const [submitting,  setSubmitting]  = useState_p(false);
  const [provisionResult, setProvisionResult] = useState_p(null);
  const [crawlContext,setCrawlContext]= useState_p(null);
  const [crawlData,   setCrawlData]   = useState_p({});
  const [websiteUrl,  setWebsiteUrl]  = useState_p("");
  const [editSectionId, setEditSectionId] = useState_p(null);

  const handleUpdate = useCallback_p((fieldId, value) => update({ [fieldId]: value }), [update]);

  const agent = useVapiAgent({
    sections: SECTIONS,
    onUpdate: handleUpdate,
    onComplete: () => {
      // call-end triggers transition to review via callStatus === "ended" effect below
    },
    crawlContext,
    crawlData: data,
    websiteUrl,
  });

  // Transition to review when call ends normally (not when an error caused the end)
  useEffect_p(() => {
    if (agent.callStatus === "ended" && phase === "call" && !agent.error) {
      setTimeout(() => { setPhase("review"); setConfettiTick((n) => n + 1); }, 800);
    }
  }, [agent.callStatus, phase, agent.error]);

  // ── Splash → website ─────────────────────────────────────────────────────
  const handleStart = () => {
    setLaunching(true);
    setTimeout(() => setPhase("website"), 900);
  };

  // ── Website → scanning ───────────────────────────────────────────────────
  const handleScan = (url) => {
    setWebsiteUrl(url);
    update({ current_url: url });
    setPhase("scanning");
  };
  const handleWebsiteSkip = () => setPhase("call");

  // ── Scanning → call ──────────────────────────────────────────────────────
  const handleScanDone = useCallback_p((result) => {
    if (result.updates && Object.keys(result.updates).length > 0) {
      update(result.updates);
      setCrawlData((prev) => ({ ...prev, ...result.updates }));
    }
    if (result.crawlContext) setCrawlContext(result.crawlContext);
    setPhase("call");
  }, [update]);
  const handleScanSkip = useCallback_p(() => setPhase("call"), []);

  // ── Call → review (manual end call or from error state) ─────────────────
  const handleEndCall = useCallback_p(() => {
    if (agent.callStatus === "ended" || agent.callStatus === "error") {
      setPhase("review");
    }
    // If still active, endCall was triggered by VapiCallWidget;
    // the callStatus effect handles transition once Vapi fires call-end
  }, [agent.callStatus]);

  // Auto-start the Vapi call when entering the call phase
  useEffect_p(() => {
    if (phase === "call" && agent.callStatus === "idle") {
      // Small delay to let React settle + allow user to see the call screen
      setTimeout(() => agent.controls.startCall(), 500);
    }
  }, [phase]);

  // ── Edit section from review ─────────────────────────────────────────────
  const handleEditSection = (sid) => {
    setEditSectionId(sid);
    setPhase("editing");
  };
  const handleEditDone = () => {
    setPhase("review");
    setEditSectionId(null);
  };

  // ── Submit → intake_forms ────────────────────────────────────────────────
  const handleConfirm = useCallback_p(async () => {
    const cfg = CFG();
    const payload = {
      sessionId:    STORAGE_KEY,
      submittedAt:  new Date().toISOString(),
      source:       "cfm-momentum",
      brand:        cfg.brand || {},
      answers:      dataRef.current,
      flat:         Object.fromEntries(
        SECTIONS.flatMap((s) =>
          s.fields
            .filter((f) => window.isFilled(dataRef.current[f.id]))
            .map((f) => [f.id, dataRef.current[f.id]])
        )
      ),
    };
    setSubmitting(true); setProvisionResult(null);
    try { localStorage.setItem(STORAGE_KEY + "_submitted", JSON.stringify(payload)); } catch (e) {}
    if (!cfg.PROVISION_WEBHOOK_URL) {
      setProvisionResult({ failed: true, message: "no submit endpoint configured — saved locally" });
      setPhase("provisioning"); setSubmitting(false); setConfettiTick((n) => n + 1); return;
    }
    try {
      const headers = { "Content-Type": "application/json" };
      if (cfg.WEBHOOK_TOKEN) headers["x-cfm-token"] = cfg.WEBHOOK_TOKEN;
      const res = await fetch(cfg.PROVISION_WEBHOOK_URL, { method: "POST", headers, body: JSON.stringify(payload) });
      if (!res.ok) throw new Error("HTTP " + res.status);
      let body = {}; try { body = await res.json(); } catch (e) {}
      setProvisionResult({ rowId: body.rowId || body.id || null });
      setPhase("provisioning"); setConfettiTick((n) => n + 1);
    } catch (e) {
      setProvisionResult({ failed: true, message: e.message || "network error" });
      setPhase("provisioning");
    } finally { setSubmitting(false); }
  }, []);

  const showTopbar = phase === "review" || phase === "editing";

  return (
    <div className="app">
      {showTopbar && (
        <div className="topbar">
          <div className="topbar-inner">
            <Logo />
            <div className="topbar-right">
              <SaveIndicator status={status} lastSaved={lastSaved} />
            </div>
          </div>
        </div>
      )}

      {phase === "website"      && <WebsiteEntryScreen onScan={handleScan} onSkip={handleWebsiteSkip} />}
      {phase === "scanning"     && <ScanningScreen url={websiteUrl} onDone={handleScanDone} onSkip={handleScanSkip} />}
      {phase === "call"         && <CallScreen agent={agent} data={data} onEndCall={handleEndCall} />}
      {phase === "review"       && (
        <ReviewScreen data={data} onEditSection={handleEditSection}
                      onConfirm={handleConfirm} submitting={submitting} />
      )}
      {phase === "editing"      && (
        <EditFormOverlay sections={SECTIONS} data={data} sectionId={editSectionId}
                         justSet={agent.justSet} onChange={handleUpdate} onDone={handleEditDone} />
      )}
      {phase === "provisioning" && <ProvisioningScreen result={provisionResult} />}

      <IntroSplash launching={launching} hide={phase !== "splash"} onStart={handleStart} />
      <ConfettiBurst active={confettiTick} />
    </div>
  );
}

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