// SQSEO product tour engine (Tour plan Phase T1). A reusable spotlight /
// coach-mark machine: steps anchor to [data-tour="<id>"] targets, the engine
// dims everything else with a 4-pane backdrop (no clip-path jank), and shows an
// anchored card with Back / Next / Skip, progress dots and keyboard control.
// Content-agnostic: callers pass steps; screens opt in by adding data-tour
// attributes. Progress persists to the project's onboarding blob (tour_step /
// tour_done / tour_skip via PATCH /api/onboarding/wizard) + localStorage.
// Start anywhere with window.LTQ.startTour(steps, { onNavigate }).
(function init(){
if(!window.LongtailIQDesignSystem_ae8f12 || !window.LTQ || !window.LTQ.api){return setTimeout(init,30);}
const React = window.React;
const DS = window.LongtailIQDesignSystem_ae8f12;
const { Icon } = DS;
const { api } = window.LTQ;
const t = (k, v) => (window.LTQ.t ? window.LTQ.t(k, v) : k);

let listener = null; // TourHost subscribes; startTour publishes
window.LTQ.startTour = (steps, opts) => { listener && listener(steps, opts || {}); };

const MOBILE = () => window.innerWidth < 860;
const persist = (body) => api.patch("/api/onboarding/wizard", body).catch(() => {});
const lsSet = (k, v) => { try { localStorage.setItem(k, v); } catch (e) {} };

function measure(id) {
  const el = document.querySelector(`[data-tour="${id}"]`);
  if (!el) return null;
  const r = el.getBoundingClientRect();
  if (r.width < 2 || r.height < 2) return null;
  return { top: r.top, left: r.left, width: r.width, height: r.height, bottom: r.bottom, right: r.right };
}

// Where the card sits relative to the spotlight (auto-flips near edges). Every
// branch clamps BOTH axes so the card is always fully on-screen — a target that
// fills the viewport (e.g. the whole content region) used to strand the card
// below the fold, leaving a dimmed page with no visible way out.
function cardPos(r, prefer) {
  const W = 320, H = 170, pad = 14, vw = window.innerWidth, vh = window.innerHeight;
  const clampX = (x) => Math.max(10, Math.min(vw - W - 10, x));
  const clampY = (y) => Math.max(10, Math.min(vh - H - 10, y));
  const fits = {
    bottom: r.bottom + pad + H < vh,
    top: r.top - pad - H > 0,
    right: r.right + pad + W < vw,
    left: r.left - pad - W > 0,
  };
  const order = prefer ? [prefer, "bottom", "top", "right", "left"] : ["bottom", "top", "right", "left"];
  const side = order.find((s) => fits[s]);
  if (side === "bottom") return { top: clampY(r.bottom + pad), left: clampX(r.left + r.width / 2 - W / 2), w: W };
  if (side === "top") return { top: clampY(r.top - pad - H), left: clampX(r.left + r.width / 2 - W / 2), w: W };
  if (side === "right") return { top: clampY(r.top), left: clampX(r.right + pad), w: W };
  if (side === "left") return { top: clampY(r.top), left: clampX(r.left - pad - W), w: W };
  // Nothing fits (the target is nearly the whole viewport): pin the card to the
  // lower-centre, fully on-screen and over the spotlight, so it is always reachable.
  return { top: clampY(vh - H - 20), left: clampX(vw / 2 - W / 2), w: W };
}

function TourHost({ onNavigate }) {
  const [run, setRun] = React.useState(null); // { steps, opts }
  const [i, setI] = React.useState(0);
  const [rect, setRect] = React.useState(null);
  const [, force] = React.useReducer((x) => x + 1, 0);

  React.useEffect(() => { listener = (steps, opts) => { setRun({ steps, opts }); setI(0); }; return () => { listener = null; }; }, []);

  const steps = run ? run.steps : [];
  const step = steps[i] || null;

  // Navigate to the step's screen, wait for its target, then measure. A target
  // that never appears auto-advances so a renamed anchor can't strand the tour.
  React.useEffect(() => {
    if (!step) return;
    let alive = true, tries = 0;
    if (step.screen && onNavigate) onNavigate(step.screen);
    if (step.onEnter) { try { step.onEnter(); } catch (e) {} }
    const tick = () => {
      if (!alive) return;
      const r = measure(step.target);
      if (r) {
        setRect(r);
        const el = document.querySelector(`[data-tour="${step.target}"]`);
        if (el && (r.top < 60 || r.bottom > window.innerHeight - 40)) el.scrollIntoView({ block: "center", behavior: "instant" });
        setTimeout(() => alive && setRect(measure(step.target)), 60);
      } else if (++tries < 30) setTimeout(tick, 120);
      else next(); // target missing -> skip this step
    };
    setRect(null);
    const tm = setTimeout(tick, step.screen ? 450 : 60);
    return () => { alive = false; clearTimeout(tm); };
  }, [run, i]);

  // Keep the spotlight glued to the target on resize/scroll.
  React.useEffect(() => {
    if (!step) return;
    const re = () => { setRect(measure(step.target)); force(); };
    window.addEventListener("resize", re);
    window.addEventListener("scroll", re, true);
    return () => { window.removeEventListener("resize", re); window.removeEventListener("scroll", re, true); };
  }, [run, i]);

  const stop = (kind) => {
    setRun(null);
    if (!(run && run.opts && run.opts.hint)) {
      lsSet("sqseo_tour", kind);
      persist(kind === "done" ? { tour_done: true } : { tour_skip: true });
    }
    if (run && run.opts && run.opts.onEnd) run.opts.onEnd(kind);
  };
  const next = () => { if (i + 1 >= steps.length) stop("done"); else { setI(i + 1); persist({ tour_step: i + 1 }); } };
  const back = () => setI(Math.max(0, i - 1));

  React.useEffect(() => {
    if (!run) return;
    const onKey = (e) => {
      if (e.key === "Escape") { e.preventDefault(); stop("skip"); }
      else if (e.key === "ArrowRight") { e.preventDefault(); next(); }
      else if (e.key === "ArrowLeft") { e.preventDefault(); back(); }
    };
    window.addEventListener("keydown", onKey, true);
    return () => window.removeEventListener("keydown", onKey, true);
  }, [run, i]);

  if (!run || !step) return null;
  const dim = "rgba(10,14,12,0.55)";
  const m = 6; // spotlight margin around the target
  const R = rect;
  const card = R ? cardPos(R, step.placement) : null;
  // For single-step micro-hints, clicking the dimmed area dismisses it (same as
  // Escape) so a hint is never a trap. Multi-step guided tours keep the backdrop
  // inert — the always-visible Skip button is the exit — so a stray click on
  // dimmed UI can't wipe out tour progress by accident.
  const dismissable = !!(run.opts && run.opts.hint);
  const pane = (style) => <div onClick={dismissable ? () => stop("skip") : undefined}
    style={{ position: "fixed", background: dim, cursor: dismissable ? "pointer" : "default", transition: "all 240ms var(--ease-out)", ...style }} />;

  return (
    <div aria-live="polite" style={{ position: "fixed", inset: 0, zIndex: 200 }}>
      {/* 4-pane dimmer with a transparent cut-out over the target */}
      {R ? (
        <>
          {pane({ top: 0, left: 0, right: 0, height: Math.max(0, R.top - m) })}
          {pane({ top: R.bottom + m, left: 0, right: 0, bottom: 0 })}
          {pane({ top: R.top - m, left: 0, width: Math.max(0, R.left - m), height: R.height + m * 2 })}
          {pane({ top: R.top - m, left: R.right + m, right: 0, height: R.height + m * 2 })}
          <div style={{ position: "fixed", top: R.top - m, left: R.left - m, width: R.width + m * 2, height: R.height + m * 2, borderRadius: 12, boxShadow: "0 0 0 2px var(--accent-500)", pointerEvents: "none", transition: "all 240ms var(--ease-out)" }} />
        </>
      ) : (
        <div onClick={dismissable ? () => stop("skip") : undefined} style={{ position: "fixed", inset: 0, background: dim, cursor: dismissable ? "pointer" : "default" }} />
      )}

      {/* coach-mark card — SQ hosts it (waves you in, cheers at the end). */}
      <div className="lt-pop" role="dialog" aria-label={step.title}
        style={MOBILE()
          ? { position: "fixed", left: 10, right: 10, bottom: 12, background: "var(--paper)", borderRadius: 14, border: "1px solid var(--border-subtle)", boxShadow: "var(--shadow-lg)", padding: 18 }
          : { position: "fixed", top: card ? card.top : "40%", left: card ? card.left : "50%", width: card ? card.w : 340, background: "var(--paper)", borderRadius: 14, border: "1px solid var(--border-subtle)", boxShadow: "var(--shadow-lg)", padding: 18 }}>
        <div style={{ display: "flex", alignItems: "flex-start", gap: 12 }}>
          {window.LTQ.SQ && (
            <div style={{ flex: "none", width: 46, marginTop: -3, marginBottom: -8 }} aria-hidden="true">
              <window.LTQ.SQ key={i === 0 ? "greet" : (i + 1 >= steps.length ? "fin" : "run")} state={i === 0 ? "wave" : (i + 1 >= steps.length ? "cheer" : "idle")} width={46} interactive={false} bubble={false} track={false} />
            </div>
          )}
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 10 }}>
              <div style={{ fontSize: 14.5, fontWeight: 700, color: "var(--text-strong)" }}>{step.title}</div>
              <button onClick={() => stop("skip")} aria-label={t("tour.skip")} style={{ flex: "none", width: 26, height: 26, display: "grid", placeItems: "center", border: "none", background: "transparent", cursor: "pointer", color: "var(--text-faint)", borderRadius: 7 }}><Icon name="x" size={15} /></button>
            </div>
            <div style={{ fontSize: 13, color: "var(--text-muted)", lineHeight: 1.55, marginTop: 6 }}>{step.body}</div>
          </div>
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 14 }}>
          <span style={{ display: "inline-flex", gap: 4, flex: 1 }}>
            {steps.map((_, di) => <span key={di} style={{ width: 6, height: 6, borderRadius: 999, background: di === i ? "var(--accent-500)" : "var(--ink-100)", transition: "background var(--dur-fast)" }} />)}
          </span>
          <button onClick={() => stop("skip")} style={{ height: 30, padding: "0 10px", border: "none", background: "transparent", cursor: "pointer", fontFamily: "var(--font-sans)", fontSize: 12, fontWeight: 600, color: "var(--text-faint)" }}>{t("tour.skip")}</button>
          {i > 0 && <button onClick={back} style={{ height: 30, padding: "0 11px", borderRadius: 8, border: "1px solid var(--border-subtle)", background: "var(--paper)", cursor: "pointer", fontFamily: "var(--font-sans)", fontSize: 12.5, fontWeight: 600, color: "var(--text-body)" }}>{t("wizard.back")}</button>}
          <button onClick={next} autoFocus style={{ height: 30, padding: "0 13px", borderRadius: 8, border: "none", background: "var(--accent-500)", color: "#fff", cursor: "pointer", fontFamily: "var(--font-sans)", fontSize: 12.5, fontWeight: 600 }}>
            {i + 1 >= steps.length ? t("tour.finish") : t("tour.next")}
          </button>
        </div>
      </div>
    </div>
  );
}

// ---- the core-loop tour (Phase T2): where everything is, in ~60 seconds ----
function coreTour() {
  return [
    { id: "sidebar", target: "sidebar", title: t("tour.core.sidebar.title"), body: t("tour.core.sidebar.body") },
    { id: "seed", screen: "research", target: "seed", title: t("tour.core.seed.title"), body: t("tour.core.seed.body") },
    { id: "results", screen: "research", target: "content", title: t("tour.core.results.title"), body: t("tour.core.results.body") },
    { id: "ideas", screen: "content", target: "content", title: t("tour.core.ideas.title"), body: t("tour.core.ideas.body") },
    { id: "plan", screen: "calendar", target: "calendar-grid", title: t("tour.core.plan.title"), body: t("tour.core.plan.body") },
    { id: "setup", screen: "dashboard", target: "getting-started", title: t("tour.core.setup.title"), body: t("tour.core.setup.body") },
    { id: "topbar", screen: "dashboard", target: "topbar", title: t("tour.core.topbar.title"), body: t("tour.core.topbar.body") },
  ];
}
window.LTQ.coreTour = coreTour;

// ---- the depth chapter (Phase T3): the analytical surfaces ----
function depthTour() {
  return [
    { id: "aivis", screen: "ai-visibility", target: "content", title: t("tour.depth.aivis.title"), body: t("tour.depth.aivis.body") },
    { id: "perf", screen: "performance", target: "content", title: t("tour.depth.perf.title"), body: t("tour.depth.perf.body") },
    { id: "reports", screen: "exports", target: "content", title: t("tour.depth.reports.title"), body: t("tour.depth.reports.body") },
    { id: "settings", screen: "settings", target: "gsc-card", title: t("tour.depth.settings.title"), body: t("tour.depth.settings.body") },
  ];
}
window.LTQ.depthTour = depthTour;

// ---- first-visit micro-hints (Phase T4): one per screen, ever ----
const HINTS = {
  research: { target: "seed" },
  calendar: { target: "calendar-grid" },
  "ai-visibility": { target: "content" },
  content: { target: "content" },
  exports: { target: "content" },
};
const hintKey = (route, part) => "tour.hint." + route + "." + part;
const seenKey = "sqseo_seen_screens";
function MicroHints({ route, onNavigate }) {
  React.useEffect(() => {
    const hint = HINTS[route];
    if (!hint) return;
    if (document.querySelector("[role=dialog]")) return; // never during a tour/offer
    let seen = [];
    try { seen = JSON.parse(localStorage.getItem(seenKey) || "[]"); } catch (e) {}
    if (seen.includes(route)) return;
    const b = window.LTQ.boot || {};
    const ob = (b.project && b.project.onboarding) || {};
    const blobSeen = Array.isArray(ob.seen_screens) ? ob.seen_screens : [];
    if (blobSeen.includes(route)) return;
    const tm = setTimeout(() => {
      if (document.querySelector("[role=dialog]")) return;
      const nextSeen = [...new Set([...seen, ...blobSeen, route])];
      try { localStorage.setItem(seenKey, JSON.stringify(nextSeen)); } catch (e) {}
      persist({ seen_screens: nextSeen });
      window.LTQ.startTour([{ id: "hint-" + route, target: hint.target, title: t(hintKey(route, "title")), body: t(hintKey(route, "body")) }], { onNavigate, hint: true });
    }, 1400);
    return () => clearTimeout(tm);
  }, [route]);
  return null;
}
window.LTQ.MicroHints = MicroHints;

// ---- auto-offer after the setup wizard (Phase T2) ----
// A small, dismissible card for accounts that finished setup but never toured.
function TourOffer({ onNavigate }) {
  const [show, setShow] = React.useState(false);
  React.useEffect(() => {
    const b = window.LTQ.boot || {};
    const ob = (b.project && b.project.onboarding) || {};
    let seen = "";
    try { seen = localStorage.getItem("sqseo_tour") || ""; } catch (e) {}
    if (b.needs_onboarding || ob.tour_completed_at || ob.tour_skipped_at || seen) return;
    if (new URLSearchParams(location.search).get("tour")) return; // explicit start wins
    const tm = setTimeout(() => { if (!window.__sqAutoTour) setShow(true); }, 1600); // auto-tour suppresses the offer
    return () => clearTimeout(tm);
  }, []);
  if (!show) return null;
  const start = () => { setShow(false); window.LTQ.startTour(coreTour(), { onNavigate }); };
  const later = () => { setShow(false); try { localStorage.setItem("sqseo_tour", "skip"); } catch (e) {} persist({ tour_skip: true }); };
  const SQ = window.LTQ.SQ;
  return (
    <div className="lt-toast-in" role="dialog" aria-label={t("tour.offer.title")} style={{ position: "fixed", right: 18, bottom: 18, zIndex: 150, width: 300, background: "var(--paper)", border: "1px solid var(--border-subtle)", borderRadius: 14, boxShadow: "var(--shadow-lg)", padding: 16, overflow: "hidden" }}>
      {/* SQ offers to show you around — waves you in, then steps aside for the
          anchored coach-marks. Falls back to the map icon if the rig is absent. */}
      <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 6 }}>
        {SQ && window.SQRobot
          ? <div style={{ flex: "none", marginTop: -2, marginBottom: -6 }}><SQ state="wave" width={58} interactive={false} bubble={false} /></div>
          : <span style={{ width: 30, height: 30, borderRadius: 9, display: "grid", placeItems: "center", background: "var(--ink-900)", color: "#fff", flex: "none" }}><Icon name="map" size={15} /></span>}
        <span style={{ fontSize: 13.5, fontWeight: 700, color: "var(--text-strong)" }}>{t("tour.offer.title")}</span>
      </div>
      <div style={{ fontSize: 12.5, color: "var(--text-muted)", lineHeight: 1.5 }}>{t("tour.offer.sub")}</div>
      <div style={{ display: "flex", gap: 8, marginTop: 12 }}>
        <button onClick={start} style={{ flex: 1, height: 32, borderRadius: 8, border: "none", background: "var(--ink-900)", color: "#fff", cursor: "pointer", fontFamily: "var(--font-sans)", fontSize: 12.5, fontWeight: 600 }}>{t("tour.offer.start")}</button>
        <button onClick={later} style={{ height: 32, padding: "0 12px", borderRadius: 8, border: "1px solid var(--border-subtle)", background: "var(--paper)", cursor: "pointer", fontFamily: "var(--font-sans)", fontSize: 12.5, fontWeight: 600, color: "var(--text-body)" }}>{t("tour.offer.later")}</button>
      </div>
    </div>
  );
}
window.LTQ.TourOffer = TourOffer;

window.LTQ.TourHost = TourHost;
})();
