/* React bridge for the isolated Three.js viewport. */
function PlannerViewport({ plan, ctx }) {
  const host = React.useRef(null), engine = React.useRef(null), viewport = React.useRef(null);
  const expandedRef = React.useRef(false), escapeGuard = React.useRef(0);
  const [expanded, setExpanded] = React.useState(false), [fallbackExpanded, setFallbackExpanded] = React.useState(false);
  const [ready, setReady] = React.useState(false), [error, setError] = React.useState(null);
  const [navigation, setNavigation] = React.useState('orbit'), [fullWalls, setFullWalls] = React.useState(false);
  const [hover, setHover] = React.useState('');
  const [position, setPosition] = React.useState({ x: plan.room.entrance.x + plan.room.entrance.w / 2, z: plan.room.h - .4, yaw: 0 });
  const selectedFixture = plan.fixtures.find(f => f.id === ctx.selBay?.fixtureId);
  const selectedBay = selectedFixture?.bays.find(b => b.id === ctx.selBay?.bayId);
  const options = plan.fixtures.flatMap(f => f.bays.map((b, i) => ({ value: f.id + '/' + b.id, fixture: f, bay: b, label: f.label + ' · Bay ' + (i + 1) })));
  function colorForBay(bay) {
    if (!bay.cat) return '#b8b7ad';
    const s = window.PlannerShared, v = s.bayMetricValue(plan, bay, ctx.metric, ctx.period, ctx.basis);
    return ctx.metric === 's2s' ? s.heatS2S(v) : s.heatSeq(ctx.range.max > ctx.range.min ? (v - ctx.range.min) / (ctx.range.max - ctx.range.min) : .5);
  }
  const config = { plan, ctx, navigation, fullWalls, colorForBay, onHover: setHover, onPosition: setPosition, onError: setError, onExitWalk: () => {
    // Escape first leaves fullscreen; a second Escape can leave Walk.
    if (!expandedRef.current && performance.now() > escapeGuard.current) setNavigation('orbit');
  } };
  async function exitFullscreen() {
    engine.current?.preserveCameraOnResize();
    escapeGuard.current = performance.now() + 500;
    if (document.fullscreenElement === viewport.current) {
      try { await document.exitFullscreen(); } catch (_) { /* Browser may already be exiting after Escape. */ }
    } else {
      expandedRef.current = false; setExpanded(false); setFallbackExpanded(false);
    }
    host.current?.querySelector('canvas')?.focus({ preventScroll: true });
  }
  async function toggleFullscreen() {
    if (expandedRef.current) { await exitFullscreen(); return; }
    engine.current?.preserveCameraOnResize();
    expandedRef.current = true; setExpanded(true);
    try {
      if (!viewport.current.requestFullscreen || !document.fullscreenEnabled) throw new Error('Fullscreen unavailable');
      await viewport.current.requestFullscreen();
    } catch (_) {
      // Embedded browsers may disallow native fullscreen. Fill the page instead.
      setFallbackExpanded(true);
    }
    host.current?.querySelector('canvas')?.focus({ preventScroll: true });
  }
  React.useEffect(() => {
    let wasNative = false;
    const changed = () => {
      const active = document.fullscreenElement === viewport.current;
      engine.current?.preserveCameraOnResize();
      if (active) { expandedRef.current = true; setExpanded(true); setFallbackExpanded(false); }
      else if (wasNative) {
        escapeGuard.current = performance.now() + 500;
        expandedRef.current = false; setExpanded(false); setFallbackExpanded(false);
        host.current?.querySelector('canvas')?.focus({ preventScroll: true });
      }
      wasNative = active;
    };
    const keyboard = e => {
      if (e.key === 'Escape' && expandedRef.current) {
        e.preventDefault(); e.stopImmediatePropagation(); exitFullscreen();
      } else if (e.key === 'Tab' && expandedRef.current && !document.fullscreenElement) {
        const items = Array.from(viewport.current.querySelectorAll('button:not(:disabled), select, canvas'));
        const first = items[0], last = items[items.length - 1];
        if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last?.focus(); }
        else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first?.focus(); }
      }
    };
    document.addEventListener('fullscreenchange', changed);
    window.addEventListener('keydown', keyboard, true);
    return () => { document.removeEventListener('fullscreenchange', changed); window.removeEventListener('keydown', keyboard, true); };
  }, []);
  React.useEffect(() => {
    try {
      if (!window.createPlanner3D) throw new Error('The 3D module could not load. Restart the local preview and refresh.');
      engine.current = window.createPlanner3D(host.current, config); setReady(true);
    } catch (e) { setError(e.message || '3D rendering is unavailable in this browser.'); }
    return () => { engine.current?.dispose(); engine.current = null; };
  }, []);
  React.useEffect(() => { engine.current?.update(config); });
  React.useEffect(() => { if (ctx.view === 'top') setNavigation('orbit'); }, [ctx.view]);
  const isWalk = navigation === 'walk';
  const home = () => { setNavigation('orbit'); engine.current?.preset(ctx.view === 'top' ? 'top' : 'overview'); };
  return <section ref={viewport} className={'pl-viewport' + (fallbackExpanded ? ' pl-viewport--expanded' : '')} aria-label="Store 3D planner">
    <div className="pl-webgl" ref={host}/>
    {!ready && !error && <div className="pl-viewport-message" role="status">Preparing your store…</div>}
    {error && <div className="pl-viewport-message" role="alert"><strong>3D view unavailable</strong><p>{error}</p><button className="btn btn--secondary" onClick={() => window.location.reload()}>Reload planner</button></div>}
    {ready && !error && <>
      <div className="pl-scene-top">
        <div className="pl-scene-title"><span className="pl-live-dot"/><span>{isWalk ? 'Walk through your store' : ctx.view === 'top' ? 'Floor overview' : 'Your store, in 3D'}</span></div>
        <div className="pl-scene-sub">Sample prototype · approximate scale</div>
      </div>
      <div className="pl-camera-tools" aria-label="Camera controls">
        {ctx.view !== 'top' && <div className="pl-camera-seg">
          <button className={!isWalk ? 'is-active' : ''} aria-pressed={!isWalk} onClick={home}>Overview</button>
          <button className={isWalk ? 'is-active' : ''} aria-pressed={isWalk} onClick={() => setNavigation('walk')}>Walk</button>
        </div>}
        {!isWalk && <>
          <button title="Pan with left-drag" aria-label="Pan tool" aria-pressed={navigation === 'pan'} className={navigation === 'pan' ? 'is-active' : ''} onClick={() => setNavigation(n => n === 'pan' ? 'orbit' : 'pan')}>Pan</button>
          <button title="Zoom in (+)" aria-label="Zoom in" onClick={() => engine.current.zoom(.8)}>+</button>
          <button title="Zoom out (−)" aria-label="Zoom out" onClick={() => engine.current.zoom(1.25)}>−</button>
          <button title="Fit whole store (R)" aria-label="Reset view" onClick={home}>↺</button>
        </>}
      <button className="pl-fullscreen-button" onClick={toggleFullscreen} aria-label={expanded ? 'Exit fullscreen planner' : 'Fullscreen planner'} title={expanded ? 'Exit fullscreen (Esc)' : 'Expand just the 3D planner'}><span aria-hidden="true">{expanded ? '↙' : '⛶'}</span> {expanded ? 'Exit fullscreen' : 'Fullscreen'}</button>
      </div>
      <div className="pl-scene-bottom">
        <div className="pl-scene-instructions">{isWalk ? `Drag to look · WASD / arrows to move · ${expanded ? 'Esc exits fullscreen' : 'Esc exits Walk'}` : ctx.view === 'top' ? 'Scroll to zoom · Right-drag to pan · Click a bay' : 'Drag to orbit · Scroll to zoom · Right-drag to pan'}</div>
        <div className="pl-scene-status" aria-live="polite">{hover || (selectedFixture ? `${selectedFixture.label} · ${selectedBay?.cat ? window.plannerCatName(selectedBay.cat) : 'Empty bay'}` : 'Click a shelf bay to explore its category')}</div>
      </div>
      <div className="pl-bay-tools">
        <select aria-label="Select shelf bay" value={ctx.selBay ? ctx.selBay.fixtureId + '/' + ctx.selBay.bayId : ''} onChange={e => { const o = options.find(o => o.value === e.target.value); if (o) ctx.onSelectBay(o.fixture.id, o.bay.id); }}>
          <option value="" disabled>Select a bay…</option>
          {options.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
        </select>
        {!isWalk && <button disabled={!selectedBay} onClick={() => engine.current.focus()} title="Move closer to selected bay">Focus</button>}
        {!isWalk && ctx.view !== 'top' && <button aria-pressed={fullWalls} onClick={() => setFullWalls(v => !v)}>{fullWalls ? 'Cutaway' : 'Show walls'}</button>}
      </div>
      {isWalk && <div className="pl-walk-pad" aria-label="Walk controls">
        <button aria-label="Turn left" onClick={() => engine.current.turn(-Math.PI / 8)}>↶</button>
        <button aria-label="Walk forward" onClick={() => engine.current.walkStep(1, 0)}>↑</button>
        <button aria-label="Turn right" onClick={() => engine.current.turn(Math.PI / 8)}>↷</button>
        <button aria-label="Step left" onClick={() => engine.current.walkStep(0, -1)}>←</button>
        <button aria-label="Walk backward" onClick={() => engine.current.walkStep(-1, 0)}>↓</button>
        <button aria-label="Step right" onClick={() => engine.current.walkStep(0, 1)}>→</button>
      </div>}
      {isWalk && <div className="pl-minimap" aria-label="Your position in the store">
        <svg viewBox="0 0 240 120" role="img" aria-label="Store map with your current position">
          <rect x="1" y="1" width="238" height="118" className="pl-map-room"/>
          {plan.rooms.map((r, i) => <rect key={'room' + i} x={r.x * 10} y={r.y * 10} width={r.w * 10} height={r.h * 10} className="pl-map-backroom"/>)}
          {plan.fixtures.map(f => <rect key={f.id} x={f.x * 10} y={f.y * 10} width={f.w * 10} height={f.h * 10} className="pl-map-fixture"/>)}
          <g transform={`translate(${position.x * 10},${position.z * 10}) rotate(${position.yaw * 180 / Math.PI})`}><path d="M0 -10 L-5 1 L5 1 Z" className="pl-map-heading"/><circle r="3.2" className="pl-map-you"/></g>
        </svg>
        <span>You are here</span>
      </div>}
    </>}
  </section>;
}
window.PlannerViewport = PlannerViewport;
