// LensChat.jsx — the single shared Lens chat SESSION.
// One source of truth for messages, thinking state, and guest identity, exposed
// via React context. Every Lens surface — the in-page chat section, the
// floating launcher's compact panel, and the full-screen overlay — reads and
// writes the SAME session here, so history is identical no matter where you open
// it. A lightweight local handler demonstrates Lens's thinking motion on send.

const LENS_FRAMES = [
  "assets/lens-profile.avif",
  "assets/lens-profile.avif",
  "assets/lens-profile.avif",
  "assets/lens-profile.avif",
];
const LENS_THINK_LABELS = ["Reviewing your message", "Thinking it through", "Putting it together", "Almost ready"];
const LENS_USD = (n) => "$" + Math.round(n).toLocaleString("en-US");

// Lens PDP-audit progress steps (driven by the backend audit status once wired)
const LENS_STEPS = [
  { label: "Checking PDP signals" },
  { label: "Reading product content" },
  { label: "Capturing page evidence", note: "longest step" },
  { label: "Building recommendations" },
];

const LENS_API_BASE = String(
  window.LENS_API_BASE ||
  window.localStorage?.getItem("lens_api_base") ||
  "http://127.0.0.1:8000"
).replace(/\/$/, "");
const LENS_SESSION_STORAGE_KEY = "lens_chat_session_v1";
const LENS_DEFAULT_AUDIT = { status: "idle", progress: { percent: 0, status: "Waiting for product page URL" }, needs_email: true };

function lastLensMessageIdFrom(messages) {
  for (let i = messages.length - 1; i >= 0; i -= 1) {
    if (messages[i]?.who === "lens" && messages[i]?.id) return messages[i].id;
  }
  return null;
}

function readStoredLensState() {
  const fallback = {
    messages: [{ kind: "intro", who: "lens" }],
    guestInitial: "G",
    sessionId: null,
    collected: {},
    quickReplies: [],
    actions: [],
    stage: "collect_url",
    audit: LENS_DEFAULT_AUDIT,
    resultsUrl: null,
    pollAfterMs: 2500,
    auditStep: 0,
    auditUrl: "",
    auditStarted: false,
    resultsOpened: false,
  };
  try {
    if (new URLSearchParams(window.location.search).has("lens_reset")) {
      window.localStorage?.removeItem(LENS_SESSION_STORAGE_KEY);
      return fallback;
    }
    const stored = window.localStorage?.getItem(LENS_SESSION_STORAGE_KEY);
    if (!stored) return fallback;
    const parsed = JSON.parse(stored);
    if (!parsed || !Array.isArray(parsed.messages)) return fallback;
    return {
      ...fallback,
      ...parsed,
      messages: parsed.messages.length ? parsed.messages : fallback.messages,
      collected: parsed.collected || fallback.collected,
      quickReplies: parsed.quickReplies || fallback.quickReplies,
      actions: parsed.actions || fallback.actions,
      audit: parsed.audit || fallback.audit,
    };
  } catch (_error) {
    return fallback;
  }
}

function lensClientId() {
  if (window.crypto?.randomUUID) return window.crypto.randomUUID();
  return "msg_" + Date.now().toString(36) + "_" + Math.random().toString(36).slice(2);
}

function asMessage(response) {
  const msg = response?.message || {};
  return {
    id: msg.id || lensClientId(),
    who: "lens",
    text: msg.text || "I am here. Send me a product page URL and I will keep going.",
    cards: msg.cards || [],
    ts: msg.ts,
  };
}

function auditStepFrom(audit, stage) {
  if (!audit?.task_id && stage !== "auditing" && stage !== "ready") return 0;
  const percent = Number(audit?.progress?.percent || 0);
  if (stage === "ready" || percent >= 100) return 4;
  if (percent >= 70) return 3;
  if (percent >= 35) return 2;
  if (percent >= 10) return 1;
  return 0;
}

function resolveActionUrl(url) {
  if (!url) return "";
  try { return new URL(url, window.location.origin).href; }
  catch (_e) { return url; }
}

// Pull a display initial from a guest message — explicit "I'm <name>" phrasing,
// or a lone name-like word. Falls back to null (keep the current initial).
function guestInitialFrom(text) {
  const m = text.match(/\b(?:i am|i'm|im|my name is|name is|name's|this is|it is|it's|call me|i am called|i'm called)\s+([A-Za-z][A-Za-z'’-]{1,})/i);
  if (m) return m[1][0].toUpperCase();
  const t = text.trim();
  if (/^[A-Za-z][A-Za-z'’-]{1,19}$/.test(t)) {
    const stop = new Set(["yes", "no", "yeah", "yep", "nope", "ok", "okay", "hi", "hello", "hey", "sure", "thanks", "thank", "maybe", "nothing", "none", "help", "hii", "yo"]);
    if (!stop.has(t.toLowerCase())) return t[0].toUpperCase();
  }
  return null;
}

// ── shared context ──────────────────────────────────────────────────────────
const LensChatContext = React.createContext(null);
window.LensChatContext = LensChatContext;
function useLensChat() { return React.useContext(LensChatContext); }

function LensChatProvider({ estimate, children }) {
  const initialStateRef = React.useRef(null);
  if (!initialStateRef.current) initialStateRef.current = readStoredLensState();
  const initialState = initialStateRef.current;
  const [messages, setMessages] = React.useState(initialState.messages);
  const [thinking, setThinking] = React.useState(false);
  const [frame, setFrame] = React.useState(0);
  const [guestInitial, setGuestInitial] = React.useState(initialState.guestInitial);
  const [fullscreen, setFullscreen] = React.useState(false);
  const timerRef = React.useRef(null);
  const pollRef = React.useRef(null);
  const openedResultsRef = React.useRef(initialState.resultsOpened);
  const lastLensMessageIdRef = React.useRef(lastLensMessageIdFrom(initialState.messages));
  const [sessionId, setSessionId] = React.useState(initialState.sessionId);
  const [collected, setCollected] = React.useState(initialState.collected);
  const [quickReplies, setQuickReplies] = React.useState(initialState.quickReplies);
  const [actions, setActions] = React.useState(initialState.actions);
  const [stage, setStage] = React.useState(initialState.stage);
  const [audit, setAudit] = React.useState(initialState.audit);
  const [resultsUrl, setResultsUrl] = React.useState(initialState.resultsUrl);
  const [pollAfterMs, setPollAfterMs] = React.useState(initialState.pollAfterMs);
  const [auditStep, setAuditStep] = React.useState(initialState.auditStep);
  const [auditUrl, setAuditUrl] = React.useState(initialState.auditUrl);
  const [auditStarted, setAuditStarted] = React.useState(initialState.auditStarted);
  const [resultsOpened, setResultsOpened] = React.useState(initialState.resultsOpened);

  React.useEffect(() => {
    if (!thinking) return;
    const id = setInterval(() => setFrame((f) => Math.min(f + 1, 3)), 720);
    return () => clearInterval(id);
  }, [thinking]);
  React.useEffect(() => () => {
    clearTimeout(timerRef.current);
    clearTimeout(pollRef.current);
  }, []);

  // let any CTA on the page open the chat directly (used on mobile — see App.jsx)
  React.useEffect(() => {
    window.__lensOpen = () => setFullscreen(true);
    return () => { if (window.__lensOpen) delete window.__lensOpen; };
  }, []);

  React.useEffect(() => {
    const snapshot = {
      messages,
      guestInitial,
      sessionId,
      collected,
      quickReplies,
      actions,
      stage,
      audit,
      resultsUrl,
      pollAfterMs,
      auditStep,
      auditUrl,
      auditStarted,
      resultsOpened,
    };
    try {
      window.localStorage?.setItem(LENS_SESSION_STORAGE_KEY, JSON.stringify(snapshot));
    } catch (_error) {}
  }, [messages, guestInitial, sessionId, collected, quickReplies, actions, stage, audit, resultsUrl, pollAfterMs, auditStep, auditUrl, auditStarted, resultsOpened]);

  function applyLensResponse(response, opts) {
    if (!response) return;
    setSessionId(response.session_id || null);
    setCollected(response.collected || {});
    setQuickReplies(response.quick_replies || []);
    setActions(response.actions || []);
    setStage(response.stage || "collect_url");
    setAudit(response.audit || {});
    setResultsUrl(response.results_url || null);
    setPollAfterMs(response.poll_after_ms || 2500);

    const nextUrl = response.collected?.product_url || response.collected?.submitted_url || "";
    setAuditUrl(nextUrl);
    setAuditStarted(Boolean(response.audit?.task_id || response.stage === "auditing" || response.stage === "ready"));
    setAuditStep(auditStepFrom(response.audit, response.stage));

    const nextMessage = asMessage(response);
    if (opts?.append !== false && nextMessage.id !== lastLensMessageIdRef.current) {
      lastLensMessageIdRef.current = nextMessage.id;
      setMessages((m) => [...m, nextMessage]);
    }

    if (response.stage === "ready" && response.results_url && !response.audit?.needs_email && !openedResultsRef.current) {
      openedResultsRef.current = true;
      setResultsOpened(true);
      window.open(resolveActionUrl(response.results_url), "_blank", "noopener,noreferrer");
    }
  }

  async function sendToLens({ text, quickReplyId, actionId, displayText }) {
    const v = (text || "").trim();
    if (thinking) return;
    if (displayText || v) {
      const shown = displayText || v;
      const initial = guestInitialFrom(shown);
      if (initial) setGuestInitial(initial);
      setMessages((m) => [...m, { id: lensClientId(), who: "you", text: shown }]);
    }
    setQuickReplies([]);
    setActions([]);
    setFrame(0);
    setThinking(true);
    try {
      const body = {
        session_id: sessionId,
        message: v ? { id: lensClientId(), text: v } : null,
        quick_reply_id: quickReplyId || undefined,
        action_id: actionId || undefined,
        collected,
        client: { page_url: window.location.href, surface: "conversion_works_export" },
      };
      const response = await fetch(`${LENS_API_BASE}/lens/chat`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(body),
      });
      if (!response.ok) throw new Error(`Lens returned ${response.status}`);
      applyLensResponse(await response.json());
    } catch (error) {
      setMessages((m) => [...m, {
        id: lensClientId(),
        who: "lens",
        text: "I could not reach the Lens backend from this local page. Check that the local backend is running, then try again.",
      }]);
    } finally {
      setThinking(false);
    }
  }

  function send(text) {
    const v = (text || "").trim();
    if (!v) return;
    sendToLens({ text: v });
  }

  function sendQuickReply(reply) {
    sendToLens({ quickReplyId: reply.id, displayText: reply.label });
  }

  function sendAction(action) {
    if (action.url) window.open(resolveActionUrl(action.url), "_blank", "noopener,noreferrer");
    sendToLens({ actionId: action.id, displayText: action.label });
  }

  React.useEffect(() => {
    clearTimeout(pollRef.current);
    if (stage !== "auditing" || !sessionId) return;
    const delay = Number(pollAfterMs || 2500);
    pollRef.current = setTimeout(async () => {
      try {
        const response = await fetch(`${LENS_API_BASE}/lens/session/${encodeURIComponent(sessionId)}`);
        if (!response.ok) throw new Error(`Lens poll returned ${response.status}`);
        applyLensResponse(await response.json());
      } catch (_error) {
        setAudit((a) => ({ ...a, status_error: "Could not refresh audit status" }));
      }
    }, delay);
    return () => clearTimeout(pollRef.current);
  }, [stage, sessionId, pollAfterMs, audit?.status, audit?.progress?.percent]);

  React.useEffect(() => {
    const existingSessionId = initialStateRef.current?.sessionId;
    if (!existingSessionId) return;
    let cancelled = false;
    fetch(`${LENS_API_BASE}/lens/session/${encodeURIComponent(existingSessionId)}`)
      .then((response) => {
        if (!response.ok) throw new Error(`Lens rehydrate returned ${response.status}`);
        return response.json();
      })
      .then((response) => {
        if (!cancelled) applyLensResponse(response, { append: false });
      })
      .catch(() => {});
    return () => { cancelled = true; };
  }, []);

  const value = {
    messages, thinking, frame, guestInitial, estimate, fullscreen, send,
    quickReplies, actions, sendQuickReply, sendAction,
    audit, auditStep, auditUrl, auditStarted, steps: LENS_STEPS,
    started: messages.length > 1,
    openFullscreen: () => setFullscreen(true),
    closeFullscreen: () => setFullscreen(false),
  };
  return <LensChatContext.Provider value={value}>{children}</LensChatContext.Provider>;
}

// ── shared pieces ────────────────────────────────────────────────────────────
function LensFace({ sm }) {
  return (
    <span className={"lens-av " + (sm ? "lens-av--sm" : "lens-av--head")} aria-hidden="true">
      <img src={assetUrl("assets/lens-profile.avif")} alt="" />
    </span>
  );
}

function LensThinking({ frame }) {
  return (
    <div className="chat-msg chat-msg--sol lens-think">
      <span className="lens-av lens-av--sm" aria-hidden="true">
        <span className="lens-think-frames">
          {LENS_FRAMES.map((src, i) => (
            <img key={i} src={assetUrl(src)} alt="" className={i === frame ? "is-on" : ""} />
          ))}
        </span>
      </span>
      <div className="lens-think-body">
        <div className="lens-think-label">{LENS_THINK_LABELS[frame]}</div>
        <div className="lens-dots" role="status" aria-label="Lens is preparing an answer">
          {[0, 1, 2, 3].map((i) =>
            i === 3 && frame >= 3
              ? <i key={i} className="ri-check-line" />
              : <span key={i} className={i <= frame ? "on" : ""} />
          )}
        </div>
      </div>
    </div>
  );
}

function LensIntro({ estimate: e }) {
  if (e) {
    return (
      <React.Fragment>
        <p>Based on what you filled out in the pricing calculator, you are looking at:</p>
        <div className="chat-estimate-card" data-hook="estimate-card">
          <div className="cec-title">Selected Conversion Works direction</div>
          <div className="cec-row"><span>Website scope</span><b>{e.pages} page{e.pages > 1 ? "s" : ""}</b></div>
          <div className="cec-row"><span>Payment path</span><b>{e.plan === "annual" ? "Year-one care upfront" : "Monthly care"}</b></div>
          <div className="cec-row"><span>Landing pages</span><b>{e.landingCount ? `${e.landingCount} campaign landing page${e.landingCount > 1 ? "s" : ""}` : "No campaign landing pages"}</b></div>
          <div className="cec-row"><span>Website cost</span><b>{LENS_USD(e.setupFee)}</b></div>
          <div className="cec-row"><span>Due upfront</span><b>{LENS_USD(e.plan === "annual" ? e.annualFY : e.setupFee)}</b></div>
          <div className="cec-row"><span>Monthly Site Care &amp; Updates</span><b>{e.plan === "annual" ? "Included for the year" : "$199/mo"}</b></div>
          <div className="cec-row"><span>First-year total</span><b>{LENS_USD(e.plan === "annual" ? e.annualFY : e.monthlyFY)}</b></div>
          {e.plan === "annual" && <div className="cec-row"><span>Upfront payment savings</span><b>{LENS_USD(e.annualSavings)}</b></div>}
        </div>
        <p>The useful next step is simple: share one individual product page, and Lens will start a quick PDP scan for visible conversion opportunities.</p>
        <p>What product page URL should I audit?</p>
      </React.Fragment>
    );
  }
  return (
    <React.Fragment>
      <p>Hi, I&rsquo;m Lens. I can help visually audit your store&rsquo;s product page for conversion fixes and point you toward the PDP friction worth fixing first.</p>
      <p>Let&rsquo;s start with one of your product pages. Can you share the URL?</p>
    </React.Fragment>
  );
}

// The shared message list — rendered identically in every surface.
function MessageText({ text }) {
  return (
    <React.Fragment>
      {String(text || "").split(/\n\n+/).map((part, i) => <p key={i}>{part}</p>)}
    </React.Fragment>
  );
}

function LensCards({ cards }) {
  if (!cards || !cards.length) return null;
  return (
    <div className="lens-cards">
      {cards.map((card, i) => {
        if (card.type === "audit_status") {
          const a = card.audit || {};
          const percent = Math.max(0, Math.min(100, Number(a.progress?.percent || 0)));
          return (
            <div className="lens-card lens-card--audit" key={i}>
              <div className="lens-card-row">
                <span>Audit status</span>
                <b>{a.status || "waiting"}</b>
              </div>
              <div className="lens-card-bar"><i style={{ width: percent + "%" }} /></div>
              <p>{a.progress?.status || "Waiting for the next audit update."}</p>
            </div>
          );
        }
        return (
          <div className="lens-card" key={i}>
            {card.title ? <b>{card.title}</b> : null}
            {card.text ? <p>{card.text}</p> : null}
          </div>
        );
      })}
    </div>
  );
}

function LensOptions() {
  const c = useLensChat();
  if (c.thinking || (!c.quickReplies.length && !c.actions.length)) return null;
  return (
    <div className="chat-msg chat-msg--sol lens-options-msg">
      <span className="lens-av lens-av--sm" aria-hidden="true" />
      <div className="lens-options">
        {c.quickReplies.map((reply) => (
          <button type="button" className="lens-pill" key={reply.id} onClick={() => c.sendQuickReply(reply)}>{reply.label}</button>
        ))}
        {c.actions.map((action) => (
          <button type="button" className={"lens-action lens-action--" + (action.style || "secondary")} key={action.id} onClick={() => c.sendAction(action)}>
            {action.label}
          </button>
        ))}
      </div>
    </div>
  );
}

function LensMessages() {
  const c = useLensChat();
  return (
    <React.Fragment>
      {c.messages.map((m, i) => {
        if (m.kind === "intro") {
          return (
            <div key={i} className="chat-msg chat-msg--sol">
              <LensFace sm />
              <div className="chat-bubble"><span className="chat-sender">Lens</span><LensIntro estimate={c.estimate} /></div>
            </div>
          );
        }
        if (m.who === "you") {
          return (
            <div key={i} className="chat-msg chat-msg--you">
              <span className="chat-you-av" aria-hidden="true">{c.guestInitial}</span>
              <div className="chat-you-bubble">{m.text}</div>
            </div>
          );
        }
        return (
          <div key={i} className="chat-msg chat-msg--sol">
            <LensFace sm />
            <div className="chat-bubble">
              <span className="chat-sender">Lens</span>
              <MessageText text={m.text} />
              <LensCards cards={m.cards} />
            </div>
          </div>
        );
      })}
      <LensOptions />
      {c.thinking && <LensThinking frame={c.frame} />}
    </React.Fragment>
  );
}

// A simple rounded composer reused by the full-screen overlay (and launcher).
function LensComposer({ onSend, autoFocus }) {
  const [draft, setDraft] = React.useState("");
  const ref = React.useRef(null);
  React.useEffect(() => { if (autoFocus && ref.current) ref.current.focus(); }, [autoFocus]);
  function submit() {
    const v = draft.trim();
    if (!v) return;
    onSend(v);
    setDraft("");
  }
  return (
    <div className="chat-input" data-hook="chat-input">
      <button type="button" className="chat-attach" aria-label="Add attachment"><i className="ri-add-line" /></button>
      <input ref={ref} type="text" className="chat-field" data-hook="chat-message-field" placeholder="Message Lens..." aria-label="Message Lens"
        value={draft} onChange={(ev) => setDraft(ev.target.value)}
        onKeyDown={(ev) => { if (ev.key === "Enter") { ev.preventDefault(); submit(); } }} />
      <button type="button" className="chat-send" aria-label="Send message" onClick={submit} disabled={!draft.trim()}><i className="ri-arrow-up-line" /></button>
    </div>
  );
}

// ── full-screen overlay ──────────────────────────────────────────────────────
function LensProgress({ vertical }) {
  const c = useLensChat();
  return (
    <div className={"lens-progress" + (vertical ? " lens-progress--v" : "")} role="list" aria-label="Audit progress">
      {c.steps.map((s, i) => {
        let state;
        if (!c.auditStarted) state = i === 0 ? "wait" : "pending";
        else state = i < c.auditStep ? "done" : i === c.auditStep ? "active" : "pending";
        const label = (i === 0 && !c.auditStarted) ? "Waiting for site" : s.label;
        return (
          <div className={"lp-step lp-" + state} role="listitem" key={s.label}>
            <span className="lp-node">
              {state === "done" ? <i className="ri-check-line" /> : state === "wait" ? <i className="ri-time-line" /> : i + 1}
            </span>
            <span className="lp-label">{label}
              {i === 0 && c.auditUrl ? <em>{c.auditUrl}</em> : null}
              {s.note ? <em>{s.note}</em> : null}
            </span>
          </div>
        );
      })}
    </div>
  );
}

function LensFullscreen() {
  const c = useLensChat();
  const threadRef = React.useRef(null);

  React.useEffect(() => {
    const t = threadRef.current;
    if (t) t.scrollTop = t.scrollHeight;
  }, [c.messages, c.thinking, c.frame, c.fullscreen]);

  React.useEffect(() => {
    if (!c.fullscreen) return;
    function onKey(e) { if (e.key === "Escape") c.closeFullscreen(); }
    window.addEventListener("keydown", onKey);
    document.body.style.overflow = "hidden";
    return () => { window.removeEventListener("keydown", onKey); document.body.style.overflow = ""; };
  }, [c.fullscreen]);

  if (!c.fullscreen) return null;
  return (
    <div className="lens-fullscreen-overlay" role="dialog" aria-modal="true" aria-label="Lens chat">
      <div className="lens-fullscreen-backdrop" onClick={c.closeFullscreen} />
      <div className="lens-fullscreen-panel">
        <div className="lens-fullscreen-head">
          <span className="lens-av lens-av--head" aria-hidden="true"><img src={assetUrl("assets/lens-profile.avif")} alt="Lens" /></span>
          <div className="lens-fullscreen-head-text">
            <h4 className="chat-title">Lens</h4>
            <p className="chat-subtitle">PDP conversion audit</p>
          </div>
          <button type="button" className="chat-expand lens-fullscreen-collapse" aria-label="Exit full screen" onClick={c.closeFullscreen}><i className="ri-fullscreen-exit-line" /></button>
          <button type="button" className="chat-expand" aria-label="Close chat" onClick={c.closeFullscreen}><i className="ri-close-line" /></button>
        </div>
        <LensProgress />
        <div className="lens-fullscreen-thread" ref={threadRef}>
          <div className="lens-fullscreen-thread-inner"><LensMessages /></div>
        </div>
        <div className="lens-fullscreen-foot"><LensComposer onSend={c.send} autoFocus /></div>
      </div>
    </div>
  );
}

Object.assign(window, {
  LensChatProvider, useLensChat, LensMessages, LensThinking,
  LensIntro, LensComposer, LensFace, LensFullscreen, LensProgress, guestInitialFrom,
});
