/* tour.jsx — the interactive onboarding tour.

   A spotlight-and-tooltip product tour that walks a new user through the real
   app: each step switches to the relevant tab and highlights a live element
   (anchored by a stable `data-tour="…"` attribute). If the target isn't on
   screen (e.g. a sidebar item while the mobile drawer is closed) the card
   gracefully centers instead of pointing at nothing.

   Auto-runs once on first sign-in (per-user localStorage flag) and can be
   relaunched anytime from the Guide via the `sinux:start-tour` window event.
   Steps are role-aware — they mirror what the user can actually see, built from
   the same visibleTabs the sidebar uses.
*/

const TOUR_FLAG = (uid) => "tour:v1:" + (uid || "anon");
function tourSeen(uid) { try { return !!localStorage.getItem(TOUR_FLAG(uid)); } catch (_) { return false; } }
function markTourSeen(uid) { try { localStorage.setItem(TOUR_FLAG(uid), "1"); } catch (_) {} }

// Build the role-aware step list. `tabKeys` is the Set of tab keys the user can
// see (from visibleTabs); only steps whose feature is available are included.
function buildTourSteps({ tabKeys, multiServer }) {
  const has = (k) => tabKeys && tabKeys.has(k);
  const steps = [];
  steps.push({ key: "welcome", title: "Welcome to Sinux Signals 👋",
    body: "Here's a quick tour of the app — about a minute. You can skip anytime, and replay it whenever you like from the Guide." });

  if (has("positions")) steps.push({ key: "positions", tab: "positions", target: "nav-positions", title: "Open Positions",
    body: "Your home tab. Every analyst's live position, built automatically from their Discord posts — entry → latest mark with P&L, contract details, and a tap-to-expand timeline." });
  if (has("events")) steps.push({ key: "events", tab: "events", target: "nav-events", title: "Signal Events",
    body: "The raw feed — every parsed message, newest first, before any aggregation. See exactly what came in and when, and tap a row to read the original Discord message." });
  if (has("sources")) steps.push({ key: "sources", tab: "sources", target: "nav-sources", title: "Sources",
    body: "Choose whose signals you see. Toggle an analyst off and they vanish from every tab; “solo” isolates just one. Your notifications follow these choices too." });
  if (has("watchlist")) steps.push({ key: "watchlist", tab: "watchlist", target: "nav-watchlist", title: "Watchlist",
    body: "Ideas and write-ups — not positions. When an analyst floats tickers or posts analysis, it lands here, grouped by day, without touching Positions or Recaps." });
  if (has("charts")) steps.push({ key: "charts", tab: "charts", target: "nav-charts", title: "Charts",
    body: "Candles for any ticker with the analysts' events marked in price context — intraday through monthly, with the ticker's open positions and recent events alongside." });
  if (has("recaps")) steps.push({ key: "recaps", tab: "recaps", target: "nav-recaps", title: "Recaps",
    body: "Closed history and track record — win rate, average P&L, a wins-and-losses chart, and every closed trade rolled up by day, week or month." });
  if (has("ratings")) steps.push({ key: "ratings", tab: "ratings", target: "nav-ratings", title: "Ratings",
    body: "How your users rate the platform — the average, the 1–5 distribution, a daily trend, and every note. Configure who's asked and how often at the top." });

  if (multiServer) steps.push({ key: "servers", target: "servers", title: "Switch servers",
    body: "You have access to more than one community. This picker (top-right) scopes every tab to the servers you select — pick one, some, or all." });

  steps.push({ key: "bell", target: "bell", title: "Notifications",
    body: "Tap the bell for new signals. Open its settings to turn on push alerts that reach you even when the app is closed, and choose which events you hear about. (Full setup, including iPhone, is in the Guide.)" });
  steps.push({ key: "install", title: "Install it like an app",
    body: "Add Sinux Signals to your home screen for its own icon and a full-screen, distraction-free app — and, on iPhone/iPad, to enable push at all. Step-by-step for every device is in the Guide." });

  if (has("settings")) steps.push({ key: "settings", tab: "settings", target: "nav-settings", title: "Settings",
    body: "Your control room — channels, analysts, access & roles, and server settings. What you see here depends on your permissions." });

  steps.push({ key: "report", target: "report", title: "Found something off?",
    body: "The Report a bug button is on every page, for everyone. Send a bug or an idea in seconds — your page and app version attach automatically." });
  steps.push({ key: "finish", tab: "guide", target: "nav-guide", title: "You're all set 🎉",
    body: "That's the tour. Everything here is documented in depth in the Guide — and you can replay this walkthrough anytime from the button at the top of it. Happy trading!" });

  return steps;
}

// Where to place the popover relative to the target rect (or centered if none).
function popPosition(rect) {
  const GAP = 12;
  const PW = Math.min(340, (typeof window !== "undefined" ? window.innerWidth : 360) - 24);
  const vh = typeof window !== "undefined" ? window.innerHeight : 640;
  const vw = typeof window !== "undefined" ? window.innerWidth : 360;
  if (!rect) return { place: "center", style: { width: PW, left: "50%", top: "50%", transform: "translate(-50%, -50%)" } };
  const left = Math.min(Math.max(GAP, rect.left), vw - PW - GAP);
  const below = (vh - rect.bottom) > 240 || rect.top < 240;
  if (below) return { place: "below", style: { width: PW, left, top: rect.bottom + GAP } };
  return { place: "above", style: { width: PW, left, top: rect.top - GAP, transform: "translateY(-100%)" } };
}

function AppTour({ steps, onNavigate, onClose }) {
  const [i, setI] = useState(0);
  const [rect, setRect] = useState(null);
  const step = steps[i] || null;
  const last = i >= steps.length - 1;

  // On each step: switch to its tab, then measure the target after paint. Keep
  // the highlight in sync on resize/scroll while the step is showing.
  React.useLayoutEffect(() => {
    if (!step) return;
    if (step.tab && onNavigate) onNavigate(step.tab);
    const measure = () => {
      let r = null;
      if (step.target) {
        const el = document.querySelector('[data-tour="' + step.target + '"]');
        if (el) {
          const b = el.getBoundingClientRect();
          const onscreen = b.width > 1 && b.height > 1 && b.bottom > 4 && b.right > 4 &&
            b.top < window.innerHeight - 4 && b.left < window.innerWidth - 4;
          if (onscreen) r = { top: b.top, left: b.left, width: b.width, height: b.height, bottom: b.bottom };
        }
      }
      setRect(r);
    };
    const raf = requestAnimationFrame(() => requestAnimationFrame(measure));
    const t1 = setTimeout(measure, 160);
    const t2 = setTimeout(measure, 380);
    window.addEventListener("resize", measure);
    window.addEventListener("scroll", measure, true);
    return () => {
      cancelAnimationFrame(raf); clearTimeout(t1); clearTimeout(t2);
      window.removeEventListener("resize", measure);
      window.removeEventListener("scroll", measure, true);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [i]);

  const finish = () => onClose(true);
  const skip = () => onClose(false);
  const next = () => (last ? finish() : setI(i + 1));
  const back = () => setI(n => Math.max(0, n - 1));

  useEffect(() => {
    const onKey = (e) => {
      // Enter is handled natively by the focused Next button — don't double-advance.
      if (e.key === "Escape") { e.preventDefault(); skip(); }
      else if (e.key === "ArrowRight") { e.preventDefault(); next(); }
      else if (e.key === "ArrowLeft") { e.preventDefault(); back(); }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [i, last]);

  if (!step) return null;
  const pos = popPosition(rect);

  return (
    <div className="tour-root" role="dialog" aria-modal="true" aria-label="App tour">
      {/* Full-screen catcher blocks interaction with the app during the tour; it
          dims only when there's no spotlight (the highlight's box-shadow provides
          the dim otherwise). Dismissal is via the ✕ / Skip only — not a stray click. */}
      <div className={cx("tour-catch", !rect && "dim")} />
      {rect && (
        <div className="tour-highlight" style={{ top: rect.top - 6, left: rect.left - 6, width: rect.width + 12, height: rect.height + 12 }} />
      )}
      <div className="tour-pop" data-place={pos.place} style={pos.style}>
        <div className="tour-pop-hd">
          <span className="tour-step-n mono">{i + 1} / {steps.length}</span>
          <button className="icon-btn" title="Skip tour" onClick={skip}>{I("close", { size: 15 })}</button>
        </div>
        <div className="tour-pop-title">{step.title}</div>
        <div className="tour-pop-body">{step.body}</div>
        <div className="tour-dots" aria-hidden="true">
          {steps.map((s, k) => <span key={s.key} className={cx("tour-dot", k === i && "on")} />)}
        </div>
        <div className="tour-pop-foot">
          <button className="btn ghost tour-skip" onClick={skip}>Skip</button>
          <div className="tour-nav">
            {i > 0 && <button className="btn" onClick={back}>Back</button>}
            <button className="btn primary" autoFocus onClick={next}>{last ? "Finish" : "Next"}</button>
          </div>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { AppTour, buildTourSteps, tourSeen, markTourSeen });
