/* AllLaw v3. Coachmark / walkthrough engine (Story 0.12).

   Drives a step-by-step overlay tour over the v3 demo surfaces. Each step
   declaratively references its target via a data-coach attribute, plus an
   optional screen + matter so a step can navigate the App before measuring.

   Render contract:
     <V3CoachmarkOverlay
       script={V3_WALKTHROUGHS[id]}
       stepIdx={n}
       onPrev|onNext|onSkip|onFinish={...}
       onScreenChange={(screen) => ...}
       onMatterChange={(matter) => ...} />

   The overlay is portaled to document.body so it isn't affected by the
   iPad scene's transform: scale. Targets are measured AFTER two animation
   frames so screen-switches have time to commit before the spotlight
   positions itself.

   Step shape:
     { id, target?, title, body, screen?, matter?, position? }
       - target: string matching [data-coach="..."]; omit for centered modal
       - screen / matter: declarative App nav before measuring
       - position: 'auto' | 'top' | 'bottom' | 'left' | 'right' (default auto)
*/

const V3_WALKTHROUGHS = {
  maria: {
    id: 'maria',
    title: "Maria's path through her case",
    matter: 'maria',
    steps: [
      { id: 'intro', screen: 'home', matter: 'maria',
        title: "Meet Maria",
        body: "Maria rented an apartment in Salt Lake City. When she moved out, her landlord kept her $1,800 deposit and never sent the itemized statement Utah law requires. She can't afford a lawyer. AllLaw helps her file the case herself, the right way." },
      { id: 'home-hero', target: 'home-hero', screen: 'home',
        title: "Her case at a glance",
        body: "Tier 1 means a small-claims case Maria can file pro se. The statute on top is the law that anchors her claim: Utah § 57-17-3, the security-deposit recovery statute." },
      { id: 'home-stage', target: 'home-stage', screen: 'home',
        title: "Where she is in the process",
        body: "Five stages: Intake, Preparation, Filing, Hearing, Resolution. Maria is in Preparation right now. Gathering and organizing evidence so she can file with confidence." },
      { id: 'home-readiness', target: 'home-readiness', screen: 'home',
        title: "Case readiness — 87%",
        body: "Four scores roll up into one number: how complete each legal element is, how well evidence maps to those elements, whether the law is current, and how ready the procedural steps are. Click any row to see what's missing." },
      { id: 'sidebar-theory', target: 'sidebar-theory', screen: 'home',
        title: "Open the legal theory",
        body: "Before evidence, Maria needs the right legal theory. Which law was broken, and what does it ask her to prove? Click Legal theory in the sidebar." },
      { id: 'theory-causes', target: 'theory-causes', screen: 'theory',
        title: "Wrongful retention of security deposit",
        body: "AllLaw lays out the legal theories that fit Maria's facts and shows what each one would require her to prove. She picks. We don't. The selected theory shows the four elements the court will look for." },
      { id: 'sidebar-evidence', target: 'sidebar-evidence', screen: 'theory',
        title: "Now connect her evidence",
        body: "Each piece of evidence has to support a specific element. AllLaw suggests which evidence matches which element; Maria affirms or rejects each connection." },
      { id: 'evidence-elements', target: 'evidence-elements', screen: 'evidence',
        title: "Four elements, all supported",
        body: "Lease, move-out walkthrough photos with EXIF dates, the certified-mail receipt, the 30-day timeline — every element has at least one affirmed piece of evidence backing it. That's what file-ready means." },
      { id: 'evidence-list', target: 'evidence-list', screen: 'evidence',
        title: "Maria's evidence",
        body: "Seven items uploaded, original capture dates and GPS coordinates preserved on the photos so the court can verify when and where each was taken. Maria can review the metadata before filing." },
      { id: 'sidebar-damages', target: 'sidebar-damages', screen: 'evidence',
        title: "What she's owed",
        body: "Once the elements are proved, the damages calculation is mechanical: actual loss plus what the statute allows. Click Damages." },
      { id: 'damages-math', target: 'damages-math', screen: 'damages',
        title: "$1,800 + statutory 2× = $3,600",
        body: "Utah § 57-17-3 lets the court double the wrongfully-withheld deposit. Add the $60 filing fee Maria's seeking back. Total recovery sought: $3,660. The math is shown line by line so the judge can verify it." },
      { id: 'sidebar-readiness', target: 'sidebar-readiness', screen: 'damages',
        title: "File-ready check",
        body: "Before generating the packet, AllLaw runs a final readiness check. Anything below threshold is flagged with a one-click fix. No surprises in court." },
      { id: 'sidebar-documents', target: 'sidebar-documents', screen: 'readiness',
        title: "The filing packet",
        body: "Statement of Claim, Exhibit Index, Damages Worksheet — drafted from Maria's affirmed facts. She reads each one and re-affirms before the packet is generated." },
      { id: 'documents-list', target: 'documents-list', screen: 'documents',
        title: "Documents drafted from her facts",
        body: "Every line in these documents traces back to something Maria already affirmed. Nothing is invented; nothing is legal advice. The footer makes that clear: AllLaw is not a law firm, the user prepared this with platform tools." },
      { id: 'sidebar-filing', target: 'sidebar-filing', screen: 'documents',
        title: "How she files it",
        body: "AllLaw shows Maria exactly which court, what it costs, how long until the hearing, and how to serve the landlord. Click Filing & service." },
      { id: 'filing-summary', target: 'filing-summary', screen: 'filing',
        title: "Salt Lake County Justice Court",
        body: "The right court for a small-claims security-deposit case in Utah. Filing fee, hearing timeline, and service options laid out side by side. Maria picks how she wants to serve the landlord, and AllLaw walks her through it." },
      { id: 'sidebar-courtroom', target: 'sidebar-courtroom', screen: 'filing',
        title: "Walking into court ready",
        body: "Last step: courtroom prep. Because Utah is sandbox-authorized, AllLaw can give Maria a loose script for her opening, a walk-through of her evidence, and a Q&A prep covering the questions judges ask in deposit cases." },
      { id: 'courtroom-opening', target: 'courtroom-opening', screen: 'courtroom',
        title: "Her opening statement, drafted",
        body: "Sixty seconds. Who Maria is, what happened, what she wants. Specific dates and the statute do the work; outrage and adjectives don't. She practices it; she doesn't recite it." },
      { id: 'finish', screen: 'courtroom',
        title: "That's the path",
        body: "From a kept deposit to a courtroom-ready case, with no lawyer required. AllLaw kept Maria moving forward at every step, told her what to do next, and made the legal mechanics visible. That's the difference between pro se as a last resort and pro se as a real option." },
    ],
  },
};

const V3CoachmarkOverlay = ({ script, stepIdx, onPrev, onNext, onSkip, onFinish, onScreenChange, onMatterChange }) => {
  const step = script.steps[stepIdx];
  const [rect, setRect] = React.useState(null);
  const [visible, setVisible] = React.useState(false);

  // 1. Tell App to switch screens / matters when a step demands it.
  //    Runs first so the DOM is up-to-date before we measure.
  React.useEffect(() => {
    if (step.matter && typeof onMatterChange === 'function') onMatterChange(step.matter);
    if (step.screen && typeof onScreenChange === 'function') onScreenChange(step.screen);
  }, [stepIdx]);

  // 2. Measure the target rect after two animation frames so the screen-
  //    switch from step (1) has time to commit.
  React.useEffect(() => {
    setVisible(false);
    if (!step.target) {
      setRect(null);
      const t = setTimeout(() => setVisible(true), 60);
      return () => clearTimeout(t);
    }
    let cancelled = false;
    let raf1, raf2, retryT;
    const measure = () => {
      if (cancelled) return;
      const el = document.querySelector(`[data-coach="${step.target}"]`);
      if (!el) {
        // Target not yet in DOM (slow render). Retry a couple of times.
        retryT = setTimeout(measure, 120);
        return;
      }
      setRect(el.getBoundingClientRect());
      setVisible(true);
    };
    raf1 = requestAnimationFrame(() => {
      raf2 = requestAnimationFrame(() => measure());
    });
    const onResize = () => {
      if (!step.target) return;
      const el = document.querySelector(`[data-coach="${step.target}"]`);
      if (el) setRect(el.getBoundingClientRect());
    };
    window.addEventListener('resize', onResize);
    return () => {
      cancelled = true;
      cancelAnimationFrame(raf1); cancelAnimationFrame(raf2);
      clearTimeout(retryT);
      window.removeEventListener('resize', onResize);
    };
  }, [stepIdx, step.target, step.screen, step.matter]);

  // 3. Keyboard shortcuts: Esc skips, Arrows step.
  React.useEffect(() => {
    const onKey = (e) => {
      if (e.key === 'Escape') { e.preventDefault(); onSkip(); }
      else if (e.key === 'ArrowRight') { e.preventDefault(); stepIdx === script.steps.length - 1 ? onFinish() : onNext(); }
      else if (e.key === 'ArrowLeft' && stepIdx > 0) { e.preventDefault(); onPrev(); }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [stepIdx, onPrev, onNext, onSkip, onFinish, script.steps.length]);

  const isLast = stepIdx === script.steps.length - 1;
  const calloutW = 360, calloutH = 220, gap = 18, edge = 20;

  // Compute callout position: prefer below the spotlight, then above, right, left.
  // No spotlight → center modal.
  let calloutPos = { top: '50%', left: '50%', transform: 'translate(-50%, -50%)' };
  if (rect) {
    const W = window.innerWidth, H = window.innerHeight;
    const cx = rect.left + rect.width / 2;
    const fits = {
      bottom: rect.bottom + gap + calloutH + edge < H,
      top: rect.top - gap - calloutH - edge > 0,
      right: rect.right + gap + calloutW + edge < W,
      left: rect.left - gap - calloutW - edge > 0,
    };
    const pref = step.position && step.position !== 'auto' ? step.position : null;
    const order = pref ? [pref, 'bottom', 'top', 'right', 'left'] : ['bottom', 'top', 'right', 'left'];
    const choice = order.find((o) => fits[o]) || 'bottom';
    if (choice === 'bottom') {
      calloutPos = { top: rect.bottom + gap, left: clamp(cx - calloutW / 2, edge, W - calloutW - edge) };
    } else if (choice === 'top') {
      calloutPos = { top: rect.top - gap - calloutH, left: clamp(cx - calloutW / 2, edge, W - calloutW - edge) };
    } else if (choice === 'right') {
      const cy = rect.top + rect.height / 2;
      calloutPos = { top: clamp(cy - calloutH / 2, edge, H - calloutH - edge), left: rect.right + gap };
    } else {
      const cy = rect.top + rect.height / 2;
      calloutPos = { top: clamp(cy - calloutH / 2, edge, H - calloutH - edge), left: rect.left - gap - calloutW };
    }
  }

  function clamp(v, lo, hi) { return Math.max(lo, Math.min(hi, v)); }

  const overlay = (
    <div style={{
      position: 'fixed', inset: 0, zIndex: 9000,
      pointerEvents: 'none',
      opacity: visible ? 1 : 0,
      transition: 'opacity 220ms ease',
      fontFamily: "'Inter', system-ui, sans-serif",
    }}>
      {/* Dimmed scrim with spotlight cutout via SVG mask. Clicking the scrim advances. */}
      <svg
        style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', pointerEvents: 'auto', cursor: rect ? 'pointer' : 'default' }}
        onClick={() => { if (!rect) return; isLast ? onFinish() : onNext(); }}>
        <defs>
          <mask id={`v3-coachmark-mask-${stepIdx}`}>
            <rect width="100%" height="100%" fill="white"/>
            {rect && (
              <rect
                x={rect.left - 8} y={rect.top - 8}
                width={rect.width + 16} height={rect.height + 16}
                rx="14" ry="14" fill="black"/>
            )}
          </mask>
        </defs>
        <rect width="100%" height="100%" fill="rgba(10,16,26,0.62)" mask={`url(#v3-coachmark-mask-${stepIdx})`}/>
      </svg>

      {/* Spotlight ring around the cutout. */}
      {rect && (
        <div style={{
          position: 'absolute',
          top: rect.top - 8, left: rect.left - 8,
          width: rect.width + 16, height: rect.height + 16,
          borderRadius: 14,
          border: '2px solid rgba(159,179,255,0.85)',
          boxShadow: '0 0 0 4px rgba(159,179,255,0.18), 0 18px 50px rgba(0,0,0,0.55)',
          pointerEvents: 'none',
          transition: 'all 260ms cubic-bezier(0.4, 0, 0.2, 1)',
        }}/>
      )}

      {/* Callout card. */}
      <div style={{
        position: 'absolute',
        top: calloutPos.top, left: calloutPos.left, transform: calloutPos.transform || 'none',
        width: calloutW,
        background: 'linear-gradient(180deg, rgba(35,48,61,0.97), rgba(26,35,44,0.99))',
        border: '1px solid rgba(159,179,255,0.32)',
        borderRadius: 18,
        boxShadow: '0 28px 70px rgba(0,0,0,0.60), inset 0 1px 0 rgba(255,255,255,0.05)',
        padding: '22px 24px 18px',
        color: '#F2F4F8',
        pointerEvents: 'auto',
        backdropFilter: 'blur(20px) saturate(1.4)',
        WebkitBackdropFilter: 'blur(20px) saturate(1.4)',
        transition: 'top 260ms cubic-bezier(0.4, 0, 0.2, 1), left 260ms cubic-bezier(0.4, 0, 0.2, 1)',
      }}>
        <div style={{
          display: 'flex', justifyContent: 'space-between', alignItems: 'center',
          fontSize: 10.5, letterSpacing: 1.4, textTransform: 'uppercase',
          color: 'rgba(159,179,255,0.85)', marginBottom: 12,
        }}>
          <span>Step {stepIdx + 1} of {script.steps.length}</span>
          <span style={{ display: 'flex', gap: 4 }}>
            {script.steps.map((_, i) => (
              <span key={i} style={{
                width: i === stepIdx ? 14 : 6, height: 6, borderRadius: 3,
                background: i === stepIdx ? '#9FB3FF' : (i < stepIdx ? 'rgba(159,179,255,0.55)' : 'rgba(159,179,255,0.20)'),
                transition: 'all 220ms ease',
              }}/>
            ))}
          </span>
        </div>
        <div style={{
          fontFamily: "'Fraunces', Georgia, serif", fontSize: 20, fontWeight: 500,
          letterSpacing: -0.3, lineHeight: 1.22, marginBottom: 10, color: '#F8F4E9',
        }}>
          {step.title}
        </div>
        <div style={{
          fontSize: 14, lineHeight: 1.55,
          color: 'rgba(242,244,248,0.78)', marginBottom: 18,
        }}>
          {step.body}
        </div>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <button onClick={onSkip} style={{
            background: 'transparent', border: 'none', color: 'rgba(242,244,248,0.55)',
            fontSize: 12.5, cursor: 'pointer', padding: '6px 8px', letterSpacing: 0.2,
            fontFamily: 'inherit',
          }}>
            Skip walkthrough
          </button>
          <div style={{ display: 'flex', gap: 8 }}>
            {stepIdx > 0 && (
              <button onClick={onPrev} style={{
                background: 'rgba(255,255,255,0.06)', border: '1px solid rgba(255,255,255,0.14)',
                color: '#F2F4F8', fontSize: 13, padding: '8px 16px',
                borderRadius: 999, cursor: 'pointer', fontFamily: 'inherit',
              }}>Back</button>
            )}
            <button onClick={isLast ? onFinish : onNext} style={{
              background: 'rgba(159,179,255,0.22)', border: '1px solid rgba(159,179,255,0.45)',
              color: '#F2F4F8', fontSize: 13, fontWeight: 500, padding: '8px 18px',
              borderRadius: 999, cursor: 'pointer', fontFamily: 'inherit',
            }}>{isLast ? 'Done' : 'Next'}</button>
          </div>
        </div>
      </div>
    </div>
  );

  // Portal to document.body so the overlay isn't affected by the iPad scene's
  // transform: scale (fixed positioning is broken inside transformed ancestors).
  return ReactDOM.createPortal(overlay, document.body);
};

window.V3_WALKTHROUGHS = V3_WALKTHROUGHS;
window.V3CoachmarkOverlay = V3CoachmarkOverlay;
