/* Nudge — tour.jsx: opt-in guided walkthrough.
   Companion to the teach-by-doing ContextHints: this is the deliberate,
   replayable overview. Offered ONCE after onboarding (a calm bottom card),
   with a quiet corner chip that stays until the tour is taken or dismissed;
   also replayable from Settings. NEVER forced.

   Mechanics: own React root (#tour-root), like tweaks.jsx. Each stop switches
   the app route, then spotlights a live element. The walkthrough stops are
   show-only; the hands-on moment is the FINALE (drop a real thought into Capture),
   which ends the tour — being last, whatever the sort pipeline does with the input
   (board, or a clarify question) can't strand the scripted flow. The scrim is four
   rects framing an interactive hole; a violet ring outlines it; the tooltip sits
   beside it and clamps to the viewport. Targets not on screen (e.g. Clarify with an
   empty inbox, or a hidden sidebar on mobile) fall back to a centered card. A tiny
   sample task is seeded for the duration so the board stops have something to point
   at. */

/* ---------- stops (ordered along the real loop) ---------- */
const TOUR_STOPS = [
  {
    id: 'capture', route: 'capture', target: '.dump-box', kicker: 'Capture',
    title: 'Start here — empty your head',
    body: "This is where every thought goes — tasks, worries, half-ideas. You offload; Enstow reads it and files each thing as a task. No categories, no decisions on your part."
  },
  {
    id: 'board', route: 'tasks', target: '[data-tour="nav-tasks"]', kicker: 'Your board',
    title: 'Everything lands here, sorted',
    body: 'Enstow reads what you offloaded and files each thing as a task for you. This is where they wait — nothing to organise by hand.'
  },
  {
    id: 'flagtoday', route: 'tasks', target: '.task-card', kicker: 'Two signals',
    title: '⚑ Flag vs ＋ Today',
    body: "⚑ Flag means \u201cthis matters.\u201d ＋ Today means \u201cI'll actually do it today.\u201d Flagged tasks feed into Today each morning — aim for about three."
  },
  {
    id: 'today', route: 'today', target: '[data-tour="nav-today"]', kicker: 'Today',
    title: 'Your short list for right now',
    body: 'The few things you committed to, plus quick wins. When you\u2019re ready to act, start a focus timer and do one thing at a time.'
  },
  {
    id: 'calendar', route: 'calendar', target: '.cal-plan-btn', kicker: 'Plan your day',
    title: 'See the real shape of your day',
    body: 'Your fixed anchors show how much time you actually have free. \u201cPlan my day\u201d suggests times for loose tasks — gentle suggestions, never locked-in appointments.'
  },
  {
    id: 'sweep', route: 'today', target: '[data-tour="nav-sweep"]', kicker: 'Weekly sweep',
    title: 'A gentle catch-up',
    body: "Once a week, the Sweep walks you through what's waiting, someday, or gone a bit stale — so nothing quietly rots at the bottom of the list."
  },
  {
    id: 'clarify', route: 'clarify', target: '[data-tour="nav-clarify"]', kicker: 'Clarify',
    title: 'One quick question, only when needed',
    body: "When something you offloaded is too vague to act on, Enstow asks a single short question instead of guessing. Those questions wait quietly under Clarify."
  },
  {
    id: 'connect', route: 'connect', target: '[data-tour="nav-connect"]', kicker: 'Connect',
    title: 'Pull actions in from your inbox',
    body: 'Link email, Slack or WhatsApp and Enstow surfaces the actual to-dos. You approve each one before it lands on your board — nothing is created behind your back.'
  },
  {
    id: 'yourturn', route: 'capture', targets: ['.dump-wrap', '.capture-actions'], kicker: 'Your turn', finale: true,
    title: 'Now you try',
    body: "That's the tour. One last thing — drop a real thought into the box and hit “Sort it out.” Enstow takes it from here, and you're all set."
  }
];

const TOUR_LAST = TOUR_STOPS.length - 1;

/* ---------- seed / cleanup a sample task ---------- */
function tourSeedTask() {
  const s = DumpStore.getState();
  if ((s.tasks || []).some((t) => t._tourSeed)) return;
  const cat = (s.settings.categories || [])[0] || 'Personal';
  const now = new Date().toISOString();
  const base = {
    notes: '', done: false, doneAt: null, calendarType: null, time: null,
    big: false, waitingOn: null, binnedAt: null, steps: null, outcome: null,
    recurrence: null, childId: null, origin: null, blockedBy: null,
    source: 'tour', createdAt: now, fresh: false, parentTaskId: null,
    trackDaily: false, completions: [], _tourSeed: true
  };
  // A board task (for the ⚑ vs ＋Today stop) + an inbox question (so the Clarify
  // nav item actually exists to spotlight). Both cleared on finish/skip.
  const boardTask = Object.assign({}, base, {
    id: DumpUtil.uid(), text: 'Book the dentist', category: cat,
    level: 'high', priority: true, dueDate: null, minutes: 25,
    status: 'active', bucket: 'next-action', clarifyKind: null, clarifyAsk: null, clarify: false
  });
  const inboxTask = Object.assign({}, base, {
    id: DumpUtil.uid(), text: 'that thing about the car', category: cat,
    level: 'normal', priority: false, dueDate: null, minutes: null,
    status: 'active', bucket: 'inbox', clarifyKind: 'vague',
    clarifyAsk: 'What do you need to do about the car?', clarify: true
  });
  DumpStore.set({ tasks: [boardTask, inboxTask].concat(s.tasks) });
}
function tourRemoveSeed() {
  const s = DumpStore.getState();
  if ((s.tasks || []).some((t) => t._tourSeed)) {
    DumpStore.set({ tasks: s.tasks.filter((t) => !t._tourSeed) });
  }
}

/* ---------- geometry ---------- */
const TOUR_PAD = 8;
function visibleRect(sel) {
  if (!sel) return null;
  const els = Array.prototype.slice.call(document.querySelectorAll(sel));
  for (let i = 0; i < els.length; i++) {
    const r = els[i].getBoundingClientRect();
    if (r.width > 4 && r.height > 4 && r.bottom > 0 && r.top < window.innerHeight && r.right > 0 && r.left < window.innerWidth) {
      return { top: r.top, left: r.left, width: r.width, height: r.height };
    }
  }
  return null;
}
function clamp(v, lo, hi) { return Math.max(lo, Math.min(hi, v)); }

// Rect for a stop: a single target, or the bounding union of several (the finale
// spotlights the dump box AND the "Sort it out" button so both are interactive).
function measureStop(stop) {
  if (!stop) return null;
  if (stop.targets) {
    let u = null;
    stop.targets.forEach((sel) => {
      const r = visibleRect(sel);
      if (!r) return;
      const box = { top: r.top, left: r.left, right: r.left + r.width, bottom: r.top + r.height };
      if (!u) u = box;
      else { u.top = Math.min(u.top, box.top); u.left = Math.min(u.left, box.left); u.right = Math.max(u.right, box.right); u.bottom = Math.max(u.bottom, box.bottom); }
    });
    return u ? { top: u.top, left: u.left, width: u.right - u.left, height: u.bottom - u.top } : null;
  }
  return visibleRect(stop.target);
}

function TourRoot() {
  const state = useDumpStore();
  const [active, setActive] = React.useState(false);
  const [step, setStep] = React.useState(0);
  const [rect, setRect] = React.useState(null);
  const [seen, setSeen] = React.useState(() => { try { return localStorage.getItem('braindump.tourSeen') === 'true'; } catch (e) { return false; } });
  const [offerPhase, setOfferPhase] = React.useState(() => {
    // pending → auto-offer hasn't fired yet (show nothing); open → the offer card;
    // dismissed → "Maybe later", or a prior session already offered → the quiet chip.
    let seenNow = false, offeredNow = false;
    try {
      seenNow = localStorage.getItem('braindump.tourSeen') === 'true';
      offeredNow = localStorage.getItem('braindump.tourOffered') === 'true';
    } catch (e) {}
    return seenNow ? 'hidden' : (offeredNow ? 'dismissed' : 'pending');
  });
  const activeRef = React.useRef(false);
  const stepRef = React.useRef(0);
  const offeredRef = React.useRef(false);
  activeRef.current = active; stepRef.current = step;

  const markSeen = () => { try { localStorage.setItem('braindump.tourSeen', 'true'); } catch (e) {} setSeen(true); };

  // Clean up any sample task left behind by an interrupted prior tour.
  React.useEffect(() => { tourRemoveSeed(); }, []);

  const start = React.useCallback(() => {
    if (activeRef.current) return;
    setOfferPhase('dismissed');
    tourSeedTask();
    setStep(0);
    setRect(null);
    setActive(true);
  }, []);
  const finish = React.useCallback((completed) => {
    setActive(false);
    DumpStore.set({ sweepOpen: false }); // never leave the sweep modal open behind us
    tourRemoveSeed();
    markSeen();
    if (completed) DumpAPI.toast("That's the tour — replay it anytime from Settings.");
  }, []);
  const next = React.useCallback(() => {
    if (stepRef.current >= TOUR_LAST) { finish(true); return; }
    setStep((n) => n + 1);
  }, [finish]);
  const prev = React.useCallback(() => { setStep((n) => Math.max(0, n - 1)); }, []);
  const skip = React.useCallback(() => finish(false), [finish]);

  // Expose the trigger for the Settings "Take the tour" link + console.
  React.useEffect(() => { window.__startTour = start; window.__nudgeTour = { start: start }; });

  // Offer once, after onboarding, when the app is actually up.
  React.useEffect(() => {
    if (seen || offerPhase !== 'pending') return;
    let id, tries = 0;
    const check = () => {
      if (offeredRef.current || activeRef.current) return;
      if (document.querySelector('.onb-root, #onb-preview')) { id = setTimeout(check, 500); return; } // wait out onboarding
      if (document.querySelector('.sidebar, .tabbar')) {
        offeredRef.current = true;
        try { localStorage.setItem('braindump.tourOffered', 'true'); } catch (e) {}
        setOfferPhase('open');
        return;
      }
      if (++tries < 30) id = setTimeout(check, 400);
    };
    id = setTimeout(check, 1200);
    return () => clearTimeout(id);
  }, [seen, offerPhase]);

  // On step change: route, then measure the target (with retries for the re-render).
  React.useEffect(() => {
    if (!active) return;
    setRect(null);
    const stop = TOUR_STOPS[step];
    if (stop.route && DumpStore.getState().route !== stop.route) DumpStore.set({ route: stop.route });
    if (DumpStore.getState().sweepOpen) DumpStore.set({ sweepOpen: false }); // don't carry the sweep modal across stops
    let id, tries = 0;
    const tick = () => {
    const r = measureStop(stop);
      if (r) { setRect(r); return; }
      if (++tries > 22) { setRect(null); return; } // give up → centered card
      id = setTimeout(tick, 55);
    };
    id = setTimeout(tick, 70);
    return () => clearTimeout(id);
  }, [active, step]);

  // Keep the spotlight glued to the target on scroll/resize.
  React.useEffect(() => {
    if (!active) return;
    const on = () => { const r = measureStop(TOUR_STOPS[stepRef.current]); if (r) setRect(r); };
    window.addEventListener('resize', on);
    window.addEventListener('scroll', on, true);
    return () => { window.removeEventListener('resize', on); window.removeEventListener('scroll', on, true); };
  }, [active]);

  // Finale: focus the real capture box so they can just start typing.
  React.useEffect(() => {
    if (!active || !TOUR_STOPS[step].finale) return;
    const t = setTimeout(() => {
      const box = document.querySelector('.dump-box');
      if (box) { try { box.focus(); } catch (e) {} }
    }, 320);
    return () => clearTimeout(t);
  }, [active, step]);

  // Finale: end the tour once they've engaged with Capture — a new task appears, OR
  // they submit/clear the box (covers inputs that produce nothing, e.g. "home").
  const finaleBaseRef = React.useRef(0);
  React.useEffect(() => {
    if (!active || !TOUR_STOPS[step].finale) return;
    finaleBaseRef.current = (DumpStore.getState().tasks || []).length;
    let hadText = false;
    const id = setInterval(() => {
      const box = document.querySelector('.dump-box');
      const len = box && box.value ? box.value.trim().length : 0;
      if (len > 0) hadText = true;
      const grew = (DumpStore.getState().tasks || []).length > finaleBaseRef.current;
      if (grew || (hadText && len === 0)) { clearInterval(id); finish(true); }
    }, 350);
    return () => clearInterval(id);
  }, [active, step]);

  // Keyboard: arrows navigate, Esc skips; shield the app's n/d capture shortcut.
  React.useEffect(() => {
    const onKey = (e) => {
      if (!activeRef.current) return;
      const tag = (document.activeElement && document.activeElement.tagName) || '';
      const typing = tag === 'INPUT' || tag === 'TEXTAREA' || (document.activeElement && document.activeElement.isContentEditable);
      if (e.key === 'Escape') { e.stopPropagation(); skip(); return; }
      if (typing) return; // don't hijack typing — arrows move the cursor in the finale box
      if (e.key === 'ArrowRight') { e.stopPropagation(); next(); return; }
      if (e.key === 'ArrowLeft') { e.stopPropagation(); prev(); return; }
      if (/^[nNdD]$/.test(e.key)) e.stopPropagation();
    };
    window.addEventListener('keydown', onKey, true);
    return () => window.removeEventListener('keydown', onKey, true);
  }, [next, prev, skip]);

  const busy = state.settingsOpen ||
    (state.pomodoro && state.pomodoro.status && state.pomodoro.status !== 'idle') ||
    (state.scheduler && state.scheduler.open);

  // ---- render: offer + chip when idle ----
  if (!active) {
    if (busy || seen || offerPhase === 'pending') return null;
    if (offerPhase === 'open') {
      return (
        <div className="tour-offer" role="dialog" aria-label="Take a quick tour">
          <span className="tour-offer-icon">{Icons.sparkle(18)}</span>
          <div className="tour-offer-text">
            <p className="tour-offer-title">Want a quick look around?</p>
            <p className="tour-offer-sub">A quick guided look — about a minute. Skip anytime.</p>
          </div>
          <div className="tour-offer-btns">
            <button className="tour-offer-go" onClick={start}>Show me →</button>
            <button className="tour-offer-no" onClick={() => setOfferPhase('dismissed')}>Maybe later</button>
          </div>
        </div>
      );
    }
    // quiet corner chip remains until the tour is taken or dismissed
    return (
      <div className="tour-chip">
        <button className="tour-chip-go" onClick={start}>{Icons.sparkle(14)} Take the tour</button>
        <button className="tour-chip-x" onClick={markSeen} aria-label="Dismiss">{Icons.x(14)}</button>
      </div>
    );
  }

  // ---- render: active spotlight ----
  const stop = TOUR_STOPS[step];
  const vw = window.innerWidth, vh = window.innerHeight;
  const scrims = [];
  let ring = null, tipStyle, tipCentered = false;

  if (rect) {
    const h = { t: rect.top - TOUR_PAD, l: rect.left - TOUR_PAD, w: rect.width + 2 * TOUR_PAD, h: rect.height + 2 * TOUR_PAD };
    const hb = h.t + h.h, hr = h.l + h.w;
    scrims.push({ top: 0, left: 0, width: vw, height: Math.max(0, h.t) });
    scrims.push({ top: hb, left: 0, width: vw, height: Math.max(0, vh - hb) });
    scrims.push({ top: h.t, left: 0, width: Math.max(0, h.l), height: h.h });
    scrims.push({ top: h.t, left: hr, width: Math.max(0, vw - hr), height: h.h });
    ring = { top: h.t, left: h.l, width: h.w, height: h.h };

    const TW = Math.min(324, vw - 24), GAP = 14, TH = 210;
    if (hr + GAP + TW <= vw) {
      tipStyle = { left: hr + GAP, top: clamp(h.t + h.h / 2, 130, vh - 130), transform: 'translateY(-50%)' };
    } else if (h.l - GAP - TW >= 0) {
      tipStyle = { left: h.l - GAP, top: clamp(h.t + h.h / 2, 130, vh - 130), transform: 'translate(-100%,-50%)' };
    } else if (hb + GAP + TH <= vh) {
      tipStyle = { top: hb + GAP, left: clamp(h.l + h.w / 2 - TW / 2, 12, vw - TW - 12) };
    } else if (h.t - GAP - TH >= 0) {
      tipStyle = { top: h.t - GAP, left: clamp(h.l + h.w / 2 - TW / 2, 12, vw - TW - 12), transform: 'translateY(-100%)' };
    } else {
      // No side has room (tall target on a short viewport) — center the card; the ring
      // still marks the spot so it never clips off-screen.
      tipCentered = true;
      tipStyle = { top: '50%', left: '50%', transform: 'translate(-50%,-50%)' };
    }
  } else {
    tipCentered = true;
    tipStyle = { top: '50%', left: '50%', transform: 'translate(-50%,-50%)' };
  }

  return (
    <React.Fragment>
      {rect ? (
        scrims.map((s, i) => (
          <div key={i} className="tour-scrim" onClick={(e) => e.stopPropagation()}
            style={{ top: s.top, left: s.left, width: s.width, height: s.height }}></div>
        ))
      ) : (
        <div className="tour-scrim full" onClick={(e) => e.stopPropagation()}></div>
      )}
      {ring ? <div className="tour-ring" style={ring}></div> : null}
      {ring && !stop.finale ? <div className="tour-catch" style={ring} onClick={(e) => e.stopPropagation()}></div> : null}

      <div className={'tour-tip' + (tipCentered ? ' centered' : '')} style={tipStyle} role="dialog" aria-label={stop.title}>
        <button className="tour-skip" onClick={skip}>Skip</button>
        <p className="tour-kicker">{Icons.sparkle(12)} {stop.kicker}</p>
        <h3 className="tour-tip-title">{stop.title}</h3>
        <p className="tour-tip-body">{stop.body}</p>
        {stop.finale ? <span className="tour-turn"><span className="tour-pulse"></span> Type a thought, then hit “Sort it out”</span> : null}
        <div className="tour-controls">
          <span className="tour-dots" aria-hidden="true">
            {TOUR_STOPS.map((_, i) => <span key={i} className={'tour-dot' + (i === step ? ' on' : '')}></span>)}
          </span>
          <span className="tour-count">{step + 1} of {TOUR_STOPS.length}</span>
          <span className="tour-btns">
            {step > 0 ? <button className="tour-back" onClick={prev}>Back</button> : null}
            <button className="tour-next" onClick={next}>{step >= TOUR_LAST ? 'Finish' : 'Next →'}</button>
          </span>
        </div>
      </div>
    </React.Fragment>
  );
}

(function mountTour() {
  function go() {
    if (window.__tourMounted) return;
    if (!window.React || !window.ReactDOM || !window.useDumpStore || !window.Icons || !window.DumpAPI || !document.body) {
      setTimeout(go, 70); return;
    }
    window.__tourMounted = true;
    const host = document.createElement('div');
    host.id = 'tour-root';
    document.body.appendChild(host);
    ReactDOM.createRoot(host).render(<TourRoot />);
  }
  go();
})();
