// sc-app.jsx — V2 main app shell + router with iOS-native transitions
//
// Tabs: home / groups / inperson / activity / settings (no FAB)
// Push: drill-in screens. Modal: SpotFlow / CreateGroupFlow / PeekFlow / WitnessSheet / PhotoViewer

const PUSH_MS = 360;
const MODAL_MS = 400;
const EASE = 'cubic-bezier(0.32, 0.72, 0, 1)';

// ───────────────────────── Push stack ─────────────────────────
function PushStack({ stack, renderScreen }){
  const [layers, setLayers] = React.useState(
    stack.map((sc, i)=>({ key:keyFor(sc,i), screen:sc, state:'idle', dir:'forward' }))
  );
  const prevStackRef = React.useRef(stack);

  React.useEffect(()=>{
    const prev = prevStackRef.current;
    const next = stack;
    prevStackRef.current = next;
    if (prev === next) return;

    if (next.length > prev.length){
      const added = next.slice(prev.length).map((sc,i)=>({
        key: keyFor(sc, prev.length+i),
        screen: sc, state:'entering', dir:'forward',
      }));
      setLayers(curr=>[...curr.filter(l=>l.state!=='exiting'), ...added]);
      requestAnimationFrame(()=>requestAnimationFrame(()=>{
        setLayers(curr=>curr.map(l=> l.state==='entering' ? {...l, state:'idle'} : l));
      }));
    } else if (next.length < prev.length){
      setLayers(curr=>{
        const alive = curr.filter(l=>l.state!=='exiting');
        if (alive.length <= next.length) return curr;
        const keep = alive.slice(0, next.length);
        const leaving = alive.slice(next.length).map(l=>({...l, state:'exiting', dir:'back'}));
        return [...keep, ...leaving];
      });
      setTimeout(()=>{
        setLayers(curr=>curr.filter(l=>l.state!=='exiting'));
      }, PUSH_MS+20);
    } else {
      setLayers(next.map((sc,i)=>({ key:keyFor(sc,i), screen:sc, state:'idle', dir:'forward' })));
    }
  }, [stack]);

  const live = layers;
  const visualTopIdx = (() => {
    for (let i=live.length-1; i>=0; i--) if (live[i].state!=='exiting') return i;
    return -1;
  })();

  return (
    <div style={{position:'absolute', top:STATUS_H, left:0, right:0, bottom:0, overflow:'hidden'}}>
      {live.map((layer, i)=>{
        const isVisualTop = i===visualTopIdx;
        const isBehind = i < visualTopIdx;
        let transform = 'translateX(0)';
        let opacity = 1, filter = 'none', zIndex = i+1;

        if (layer.state==='entering'){ transform = 'translateX(100%)'; }
        else if (layer.state==='exiting'){ transform = 'translateX(100%)'; }
        else if (isBehind){
          const topLayer = live[visualTopIdx];
          if (topLayer && topLayer.state==='entering'){
            transform = 'translateX(0)';
          } else {
            transform = 'translateX(-28%)'; opacity = 0.72; filter = 'brightness(0.88)';
          }
        }

        const showShadow = isVisualTop && live.length>1 && layer.state==='idle';
        const pointerEvents = (layer.state==='exiting' || isBehind) ? 'none' : 'auto';

        return (
          <div key={layer.key} style={{
            position:'absolute', inset:0, zIndex,
            transform, opacity, filter, pointerEvents,
            transition: `transform ${PUSH_MS}ms ${EASE}, opacity ${PUSH_MS}ms ${EASE}, filter ${PUSH_MS}ms ${EASE}`,
            background:'var(--bg)', overflow:'auto',
            boxShadow: showShadow ? '-8px 0 24px rgba(0,0,0,0.08)' : 'none',
            willChange:'transform, opacity',
          }}>
            {renderScreen(layer.screen)}
          </div>
        );
      })}
    </div>
  );
}

function keyFor(sc, i){
  const id = sc.kind + (sc.groupId||sc.personId||sc.itemId||'');
  return `${i}-${id}`;
}

// ───────────────────────── Modal host ─────────────────────────
function ModalHost({ open, onDismiss, children, kind='sheet' }){
  const [mounted, setMounted] = React.useState(open);
  const [visible, setVisible] = React.useState(open);
  const ref = React.useRef(null);

  // Mount when open flips true; unmount after exit transition when false.
  React.useEffect(()=>{
    if (open){
      setMounted(true);
    } else if (mounted){
      setVisible(false);
      const t = setTimeout(()=>setMounted(false), MODAL_MS+30);
      return ()=>clearTimeout(t);
    }
  }, [open]);

  // After the off-screen DOM commits, force layout, then on the next paint
  // flip visible→true so the transition runs.
  React.useLayoutEffect(()=>{
    if (mounted && open && !visible && ref.current){
      // Force the off-screen state to actually render before transitioning
      // eslint-disable-next-line no-unused-expressions
      ref.current.offsetHeight;
      // setTimeout 16ms gives the browser one frame to paint the
      // off-screen state; rAF is unreliable here (sometimes throttled
      // when in a hidden tab / off-screen iframe).
      const id = setTimeout(()=>setVisible(true), 16);
      return ()=>clearTimeout(id);
    }
  }, [mounted, open, visible]);

  if (!mounted) return null;

  const sheet = kind==='sheet';
  const style = {
    position:'absolute', inset:0, zIndex:80,
    transition: `transform ${MODAL_MS}ms ${EASE}, opacity ${MODAL_MS}ms ${EASE}`,
    willChange:'transform, opacity',
    transform: visible ? (sheet ? 'translateY(0)' : 'scale(1)') : (sheet ? 'translateY(100%)' : 'scale(0.96)'),
    opacity: visible ? 1 : (sheet ? 1 : 0),
  };

  return (
    <div ref={ref} style={style}>
      {children}
    </div>
  );
}

// ───────────────────────── Phone app ─────────────────────────
function PhoneApp({ viewerId }){
  const s = useStore();
  const [stack, setStack] = React.useState([{ kind:'home' }]);
  const [tab, setTab] = React.useState('home');

  const [spotOpen, setSpotOpen] = React.useState(false);
  const [spotPreset, setSpotPreset] = React.useState(null);
  const [createOpen, setCreateOpen] = React.useState(false);
  const [peekOpen, setPeekOpen] = React.useState(false);
  const [peekPreset, setPeekPreset] = React.useState(null);
  const [photoView, setPhotoView] = React.useState(null);
  const [photoOpen, setPhotoOpen] = React.useState(false);

  // Watch for incoming peek session — auto-open WitnessSheet on the OTHER device
  // Witness sheet appears on any device that is NOT the host while a peek is live.
  const incomingPeek = s.peekSession && s.peekSession.hostId !== viewerId;

  function push(screen){ setStack(st=>[...st, screen]); }
  function pop(){ setStack(st=> st.length>1 ? st.slice(0,-1) : st); }
  function replace(kind){ setStack([{kind}]); setTab(kind); }

  function onTab(id){ replace(id); }

  const openGroup          = (gid)=> push({kind:'group', groupId:gid});
  const openHead           = (pid)=> push({kind:'head', personId:pid});
  const openItem           = (iid)=> push({kind:'item', itemId:iid});
  const openSettingsGroup  = (gid)=> push({kind:'group-settings', groupId:gid});
  const openAppSettings    = ()=>    push({kind:'app-settings'});

  function openSpot(presetItemId){ setSpotPreset(typeof presetItemId==='string'?presetItemId:null); setSpotOpen(true); }
  function openPeek(presetItemId){ setPeekPreset(typeof presetItemId==='string'?presetItemId:null); setPeekOpen(true); }

  function openPhoto(sp, g){ setPhotoView({spot:sp, group:g}); setPhotoOpen(true); }
  function closePhoto(){ setPhotoOpen(false); setTimeout(()=>setPhotoView(null), MODAL_MS+20); }

  function confirmSpot(activityId, yes){
    SC.set(st=>({ ...st, activity: st.activity.map(a=> a.id===activityId ? {...a, kind:yes?'crown':'rejected'} : a) }));
  }

  function renderScreen(top){
    if (top.kind==='home'){
      return <HomeScreen viewerId={viewerId}
               onOpenGroup={openGroup}
               onOpenItem={openItem}
               onOpenHead={openHead}
               onTab={onTab}
               onPeekHere={openPeek}/>;
    }
    if (top.kind==='groups'){
      return <GroupsListScreen viewerId={viewerId}
               onOpenGroup={openGroup}
               onCreate={()=>setCreateOpen(true)}/>;
    }
    if (top.kind==='inperson'){
      return <InPersonScreen viewerId={viewerId}
               onOpenItem={openItem}
               onPeek={openPeek}/>;
    }
    if (top.kind==='activity'){
      return <ActivityScreen viewerId={viewerId} onOpenGroup={openGroup} onConfirm={confirmSpot}/>;
    }
    if (top.kind==='settings'){
      return <ProfileScreen viewerId={viewerId} onSettings={openAppSettings} onOpenGroup={openGroup}/>;
    }
    if (top.kind==='group'){
      return <GroupDetailScreen
                groupId={top.groupId} viewerId={viewerId}
                onBack={pop} onOpenPhoto={openPhoto} onOpenHead={openHead}
                onSettings={()=>openSettingsGroup(top.groupId)}
                onSpot={()=>openSpot()}/>;
    }
    if (top.kind==='item'){
      return <ItemDetailScreen itemId={top.itemId} viewerId={viewerId}
                onBack={pop} onOpenHead={openHead} onPeek={openPeek}/>;
    }
    if (top.kind==='head'){
      return <HeadToHead viewerId={viewerId} opponentId={top.personId} onBack={pop}/>;
    }
    if (top.kind==='group-settings'){
      return <GroupSettingsScreen groupId={top.groupId} viewerId={viewerId} onBack={pop}/>;
    }
    if (top.kind==='app-settings'){
      return <AppSettingsScreen viewerId={viewerId} onBack={pop}/>;
    }
    return null;
  }

  const showTabs = stack.length===1;

  return (
    <div style={{position:'absolute', inset:0, overflow:'hidden', background:'var(--bg)'}}>
      <Screen topInset={false}>
        <PushStack stack={stack} renderScreen={renderScreen}/>
      </Screen>

      <div style={{
        position:'absolute', left:0, right:0, bottom:0, zIndex:40,
        transform: showTabs ? 'translateY(0)' : 'translateY(110%)',
        opacity: showTabs ? 1 : 0,
        transition: `transform 260ms ${EASE}, opacity 260ms ${EASE}`,
        pointerEvents: showTabs ? 'auto' : 'none',
      }}>
        <TabBar active={tab} onChange={onTab}/>
      </div>

      <ModalHost open={spotOpen} onDismiss={()=>setSpotOpen(false)} kind="sheet">
        <SpotFlow viewerId={viewerId} initialItemId={spotPreset} onClose={()=>setSpotOpen(false)}/>
      </ModalHost>

      <ModalHost open={createOpen} onDismiss={()=>setCreateOpen(false)} kind="sheet">
        <CreateGroupFlow viewerId={viewerId}
          onClose={()=>setCreateOpen(false)}
          onCreated={(gid)=>{ setCreateOpen(false); setTimeout(()=>push({kind:'group', groupId:gid}), MODAL_MS); }}/>
      </ModalHost>

      <ModalHost open={peekOpen} onDismiss={()=>setPeekOpen(false)} kind="sheet">
        <PeekFlow viewerId={viewerId} presetItemId={peekPreset} onClose={()=>setPeekOpen(false)}/>
      </ModalHost>

      {/* Witness sheet auto-opens on invited devices when host starts a peek */}
      <ModalHost open={!!incomingPeek} onDismiss={()=>{}} kind="sheet">
        <WitnessSheet viewerId={viewerId} onClose={()=>{}}/>
      </ModalHost>

      <ModalHost open={photoOpen} onDismiss={closePhoto} kind="fade">
        {photoView && <PhotoViewer spot={photoView.spot} group={photoView.group} onClose={closePhoto}/>}
      </ModalHost>
    </div>
  );
}

// ───────────────────────── Mounts ─────────────────────────
function SingleDevice({ viewerId }){
  const [, force] = React.useReducer(x=>x+1, 0);
  React.useEffect(()=>{
    const h = ()=>force();
    window.addEventListener('tweaks-changed', h);
    return ()=>window.removeEventListener('tweaks-changed', h);
  },[]);
  return (
    <IOSDevice width={390} height={830}>
      <PhoneApp viewerId={viewerId}/>
    </IOSDevice>
  );
}

const rootA = ReactDOM.createRoot(document.getElementById('device-a'));
const rootB = ReactDOM.createRoot(document.getElementById('device-b'));
rootA.render(<SingleDevice viewerId="emil"/>);
rootB.render(<SingleDevice viewerId="ayaka"/>);
