// app.jsx — flow controller for Gravida Scan (Atelier AI tier)
// Production version: no Android device frame, no tweaks panel. Renders full viewport.

const { useState: useApp, useEffect: useAppE } = React;

const SCREENS = {
  welcome:      { C: 'WelcomeScreen',      dark: false },
  choose:       { C: 'ChooseScreen',       dark: false },
  privacy:      { C: 'PrivacyScreen',      dark: false },
  setup:        { C: 'SetupScreen',        dark: false },
  overview:     { C: 'OverviewScreen',     dark: false },
  capture:      { C: 'CaptureScreen',      dark: true  },
  details:      { C: 'DetailsScreen',      dark: false },
  processing:   { C: 'ProcessingScreen',   dark: false },
  preview:      { C: 'PreviewScreen',      dark: false },
  paidPreview:  { C: 'PaidPreviewScreen',  dark: false },
  submitted:    { C: 'SubmittedScreen',    dark: false },
  atelier:      { C: 'AtelierScreen',      dark: false },
  poses:        { C: 'PosesScreen',        dark: false },
  bts:          { C: 'BTSScreen',          dark: false },
};

// Fixed brand defaults, chosen from the design hand-off (silhouette guide, warm voice,
// rose accent, help affordance prominent on most screens).
const CTX_DEFAULTS = {
  framing: 'silhouette',
  tone:    'warm',
  help:    6,
};

// Laila's contact channels. Phone is the same WhatsApp number that appears in
// the existing gravida-booking transactional emails footer.
const LAILA_WA_NUMBER       = '31687062504';                       // E.164 minus the +, for wa.me links
const LAILA_WA_DISPLAY      = '+31 6 8706 2504';                  // pretty-printed for the UI

function waLink(text) {
  // wa.me prefilled-message URL. Encoding is per WhatsApp's spec.
  return `https://wa.me/${LAILA_WA_NUMBER}?text=${encodeURIComponent(text)}`;
}

function HelpSheet({ open, onClose, sessionId }) {
  if (!open) return null;
  // Every prefilled WhatsApp message ends with an Atelier-side footer so
  // Laila instantly sees this came from the Gravida Scan app, and includes
  // a short scan reference (first 8 chars of the session UUID) so she can
  // look the scan up in /admin/ai-beoordeling without asking the customer.
  const ref = sessionId ? sessionId.replace(/-/g, '').slice(0, 8) : null;
  const sourceLine = ref
    ? `\n\n— sent from Gravida Scan · ref ${ref}`
    : `\n\n— sent from Gravida Scan`;

  // All three rows resolve to a WhatsApp conversation with Laila, but each
  // pre-fills a different opening message so she has context the moment she
  // sees it. This is intentionally not three separate channels: keeping the
  // customer in WhatsApp is the simplest way to start a real conversation.
  const opts = [
    {
      icon:  <IcChat size={20} />,
      title: 'Chat with the Atelier',
      sub:   'Laila usually replies in minutes',
      href:  waLink(`Hi Laila, I have a question about the Gravida Scan app.${sourceLine}`),
    },
    {
      icon:  <IcPhone size={20} />,
      title: `WhatsApp ${LAILA_WA_DISPLAY}`,
      sub:   "Send a photo of your setup, she'll guide you",
      href:  ref
        ? waLink(`Hi Laila,${sourceLine}`)
        : `https://wa.me/${LAILA_WA_NUMBER}`,
    },
    {
      icon:  <IcRing size={20} />,
      title: 'Plan a video call',
      sub:   'A calm walk-through, or a home visit if needed',
      href:  waLink(`Hi Laila, I'd love to plan a video call about my scan.${sourceLine}`),
    },
  ];
  const openHref = (href) => {
    // window.open in TWA + iOS Safari opens the WhatsApp app directly when
    // the user has it installed (wa.me handles the deep-link).
    try { window.open(href, '_blank', 'noopener'); } catch (e) {}
    onClose();
  };
  return (
    <div onClick={onClose} style={{ position: 'absolute', inset: 0, zIndex: 40, background: 'rgba(11,12,8,.5)', backdropFilter: 'blur(3px)', display: 'flex', flexDirection: 'column', justifyContent: 'flex-end' }}>
      <div onClick={(e) => e.stopPropagation()} className="fadein" style={{ background: 'var(--paper)', borderRadius: '26px 26px 0 0', padding: 22, color: 'var(--ink)' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
          <div>
            <span className="eyebrow">We're here</span>
            <h3 className="title" style={{ fontSize: 22, marginTop: 6 }}>Talk to Atelier Gravida</h3>
          </div>
          <button onClick={onClose} aria-label="Close" style={{ background: 'var(--paper-2)', border: '1px solid var(--hairline)', borderRadius: 999, width: 38, height: 38, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', color: 'var(--ink)' }}>
            <IcX size={18} />
          </button>
        </div>
        <div style={{ marginTop: 14 }}>
          {opts.map((o, i) => (
            <button key={i} onClick={() => openHref(o.href)} style={{
              width: '100%', textAlign: 'left', display: 'flex', alignItems: 'center', gap: 14,
              padding: '14px 0', borderTop: i ? '1px solid var(--hairline)' : 'none',
              border: 'none', borderRadius: 0, background: 'transparent', cursor: 'pointer',
              fontFamily: 'inherit', color: 'inherit',
            }}>
              <div className="center" style={{ width: 44, height: 44, borderRadius: 999, flex: '0 0 auto', background: 'color-mix(in oklch, var(--rose), transparent 84%)', color: 'var(--rose-deep)' }}>{o.icon}</div>
              <div style={{ flex: 1 }}>
                <div style={{ fontWeight: 700, fontSize: 15 }}>{o.title}</div>
                <div style={{ fontSize: 13, color: 'var(--ink-mute)' }}>{o.sub}</div>
              </div>
              <IcArrow size={18} />
            </button>
          ))}
        </div>
      </div>
    </div>
  );
}

// Phone-shaped frame for desktop / tablet preview. On a real phone the viewport already
// matches, so we fill the screen edge-to-edge instead.
function Stage({ children, dark }) {
  const [shape, setShape] = useApp(() => computeShape());
  function computeShape() {
    const vw = window.innerWidth;
    const vh = window.innerHeight;
    const isPhone = vw <= 480;
    if (isPhone) {
      return { width: vw, height: vh, scale: 1, frame: false };
    }
    // Desktop / tablet: show the design at its 412x892 reference, scaled to fit.
    const s = Math.min((vw - 32) / 412, (vh - 32) / 892, 1);
    return { width: 412, height: 892, scale: s, frame: true };
  }
  useAppE(() => {
    const fit = () => setShape(computeShape());
    window.addEventListener('resize', fit);
    return () => window.removeEventListener('resize', fit);
  }, []);

  if (!shape.frame) {
    return (
      <div style={{ width: '100vw', height: '100vh', overflow: 'hidden', background: dark ? 'var(--dark)' : 'var(--beige)' }}>
        {children}
      </div>
    );
  }
  return (
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: '100vw', height: '100vh', overflow: 'hidden' }}>
      <div style={{ width: shape.width * shape.scale, height: shape.height * shape.scale }}>
        <div style={{
          width: shape.width, height: shape.height,
          transform: `scale(${shape.scale})`, transformOrigin: 'top left',
          borderRadius: 36, overflow: 'hidden', position: 'relative',
          background: dark ? 'var(--dark)' : 'var(--beige)',
          boxShadow: '0 30px 80px rgba(20,22,15,.35), 0 2px 0 rgba(255,255,255,.5) inset',
          border: '1px solid rgba(20,22,15,.08)',
        }}>
          {children}
        </div>
      </div>
    </div>
  );
}

function App() {
  // If Mollie returned the customer to us with a `?paid=GRV-XXXX` (and
  // optional `?session=<uuid>`) query, jump straight to PaidPreviewScreen and
  // remember the coupon code. We strip the query before rendering so a
  // page-refresh later does not re-trigger this branch.
  const initialBoot = (() => {
    if (typeof window === 'undefined') return { screen: 'welcome', coupon: null, sessionId: null };
    try {
      const url = new URL(window.location.href);
      const coupon    = url.searchParams.get('paid');
      const sessionId = url.searchParams.get('session');
      if (coupon) {
        // Clean up the URL so the route doesn't stick.
        url.searchParams.delete('paid');
        url.searchParams.delete('session');
        window.history.replaceState({}, '', url.pathname + (url.search || '') + url.hash);
        return { screen: 'paidPreview', coupon, sessionId };
      }
    } catch (e) {}
    return { screen: 'welcome', coupon: null, sessionId: null };
  })();

  const [screen, setScreen] = useApp(initialBoot.screen);
  const [hist,   setHist]   = useApp([]);
  const [help,   setHelp]   = useApp(false);
  // Lifted preview-poll state so it survives screen navigation. PreviewScreen
  // reads from here via ctx instead of running its own poller; that way the
  // "Notify me & keep browsing" button can navigate the customer to the
  // Atelier collection while we keep polling and ping them when the mesh
  // is ready.
  const [previewState, setPreviewState] = useApp(null); // { state, glb_url?, elapsed_seconds?, ... } | null

  // Cross-screen capture store. CaptureScreen pushes the four main photos here;
  // DetailsScreen pushes any extra close-ups + the customer's contact details;
  // ProcessingScreen reads it all and ships it to the dashboard.
  const captureRef = React.useRef({
    photos: [],                                       // { angle, file, note? }
    contact: null,                                    // { first_name, last_name, email, ... }
    sessionId: initialBoot.sessionId,                 // populated by ProcessingScreen
    couponCode: initialBoot.coupon,                   // populated by Mollie return
    scanMode: 'standing',                             // 'standing' or 'seated'; set on OverviewScreen
  });

  // Background preview poller. Active whenever a sessionId is set and the
  // preview is not yet ready. Runs every 7 seconds, posts a system
  // notification (if permission granted) the moment status flips to 'ready'
  // and the customer is on a non-preview screen.
  const prevReadyRef = React.useRef(false);
  useAppE(() => {
    const poll = async () => {
      const sessionId = captureRef.current.sessionId;
      if (!sessionId) return;
      if (prevReadyRef.current) return;            // already ready, no need to poll
      const cfg = (typeof window !== 'undefined' && window.GRAVIDA_API_CONFIG) || {};
      const base  = (cfg.base  || '').replace(/\/$/, '');
      const token = cfg.token || '';
      try {
        const res = await fetch(`${base}/api/scan/${encodeURIComponent(sessionId)}/preview`, {
          headers: token ? { 'X-Scan-App-Token': token } : {},
        });
        if (!res.ok) return;
        const data = await res.json();
        setPreviewState(data);
        if (data.state === 'ready' && !prevReadyRef.current) {
          prevReadyRef.current = true;
          // Fire a system notification if the user has granted permission.
          // Silently fails on iOS Safari + older browsers without the API.
          if (typeof Notification !== 'undefined' && Notification.permission === 'granted') {
            try {
              new Notification('Your Gravida preview is ready', {
                body: 'Tap to see your first 3D look.',
                icon: '/img/icon-512.png',
                tag:  'gravida-preview-ready',
              });
            } catch (e) {}
          }
        }
      } catch (e) {
        // network blip, try again next tick
      }
    };
    poll();                                         // immediate first call so the spinner doesn't sit empty
    const id = setInterval(poll, 7000);
    return () => clearInterval(id);
  }, []);

  const ctx = {
    tone:    CTX_DEFAULTS.tone,
    help:    CTX_DEFAULTS.help,
    framing: CTX_DEFAULTS.framing,
    capture: captureRef.current,
    previewState,                                     // for PreviewScreen to read from
    go:      (s) => { setHist((h) => [...h, screen]); setScreen(s); setHelp(false); },
    back:    () => {
      setHelp(false);
      setScreen(hist[hist.length - 1] || 'welcome');
      setHist((h) => h.slice(0, -1));
    },
    onHelp:  () => setHelp(true),
  };

  const meta = SCREENS[screen];
  const Comp = window[meta.C];

  // Render a small floating banner whenever the preview becomes ready and the
  // customer is browsing somewhere else (Atelier collection, BTS, etc.).
  const showPreviewReadyBanner =
    previewState?.state === 'ready' &&
    screen !== 'preview' &&
    screen !== 'paidPreview' &&
    screen !== 'processing';

  return (
    <Stage dark={meta.dark}>
      <div style={{ position: 'relative', height: '100%', width: '100%' }}>
        <div key={screen} style={{ position: 'absolute', inset: 0, overflow: 'hidden' }}>
          {Comp ? <Comp ctx={ctx} /> : null}
        </div>
        {showPreviewReadyBanner && (
          <button onClick={() => { setHist((h) => [...h, screen]); setScreen('preview'); }}
            style={{
              position: 'absolute', top: 14, left: 14, right: 14, zIndex: 35,
              background: 'var(--ink)', color: 'var(--paper)',
              padding: '12px 16px', borderRadius: 14, border: 'none', cursor: 'pointer',
              boxShadow: '0 12px 30px rgba(20,22,15,.45)',
              display: 'flex', alignItems: 'center', gap: 12, textAlign: 'left',
              animation: 'scr-in .4s cubic-bezier(.22,.61,.36,1) both',
            }}>
            <span style={{ flex: 1 }}>
              <span style={{ display: 'block', fontSize: 10.5, letterSpacing: '.08em', textTransform: 'uppercase', color: 'var(--gold-soft)', fontWeight: 700 }}>Preview ready</span>
              <span style={{ display: 'block', fontSize: 14, fontWeight: 600, marginTop: 1 }}>Your first 3D look is here</span>
            </span>
            <IcArrow size={18} />
          </button>
        )}
        <HelpSheet open={help} onClose={() => setHelp(false)} sessionId={captureRef.current.sessionId} />
      </div>
    </Stage>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
