// Top-level component. Owns routing, the session lifecycle, and the
// background catalog refresh; every screen reads the data it needs off
// the props it gets here.

const ROUTES = ['inbox', 'submit', 'decided', 'reports', 'audit', 'stats', 'blocks', 'me'];

function useRoute(initial = 'submit') {
  const read = () => {
    const h = (window.location.hash || '').replace(/^#\/?/, '');
    return ROUTES.includes(h) ? h : initial;
  };
  const [route, setRouteState] = React.useState(read());
  React.useEffect(() => {
    const onHash = () => setRouteState(read());
    window.addEventListener('hashchange', onHash);
    return () => window.removeEventListener('hashchange', onHash);
  }, []);
  const setRoute = (r) => { window.location.hash = '#/' + r; };
  return [route, setRoute];
}

function ApprovalApp() {
  const dark = true;
  const [me, setMe] = React.useState(undefined);  // undefined = loading, null = unauth
  const [submissions, setSubmissions] = React.useState([]);
  const [route, setRoute] = useRoute('submit');

  // Re-fetches everything the app reads from the backend: identity (so a
  // newly-saved artist profile flips `hasArtistProfile`), roster, full
  // submissions list. Called on initial mount and after any action that
  // could change one of those — profile save, vote, accept, etc.
  const refresh = React.useCallback(async () => {
    const [meRes, players, subs] = await Promise.all([
      Api.me().catch(() => null),
      Api.players(),
      Api.submissions(),
    ]);
    if (meRes) {
      Store.set({ me: meRes.player });
      setMe(meRes.player);
    }
    Store.set({
      players: players.players,
      verifiedSet: new Set(players.verifiedIdentifiers || []),
      submissions: subs,
    });
    setSubmissions(subs);
  }, []);

  React.useEffect(() => {
    (async () => {
      const meRes = await Api.me().catch(() => null);
      if (!meRes) { setMe(null); return; }
      Store.set({ me: meRes.player });
      setMe(meRes.player);
      await refresh();
    })();
  }, [refresh]);

  // Send the user away from screens their identity can't access.
  React.useEffect(() => {
    if (!me) return;
    if ((route === 'inbox' || route === 'decided') && !canVote(me)) setRoute(canSubmit(me) ? 'submit' : 'me');
    if (route === 'submit' && !canSubmit(me)) setRoute(canVote(me) ? 'inbox' : 'me');
    if ((route === 'blocks' || route === 'stats') && !canForceAccept(me)) setRoute(canVote(me) ? 'inbox' : 'me');
    // Reports: mods see + admins act. Everyone else gets bounced.
    if (route === 'reports' && me.group !== 'admin' && me.group !== 'mod') setRoute(canVote(me) ? 'inbox' : 'me');
    // Audit log: admin-only — exposes actor IDs, IPs and failed-login traces.
    if (route === 'audit'   && me.group !== 'admin') setRoute(canVote(me) ? 'inbox' : 'me');
  }, [me, route]);

  if (me === undefined) {
    return (
      <ApprovalBg dark={dark}>
        <LoadingScreen/>
        <ToastContainer/>
      </ApprovalBg>
    );
  }

  if (me === null) {
    return (
      <ApprovalBg dark={dark}>
        <LoginScreen dark={dark}/>
        <ToastContainer/>
      </ApprovalBg>
    );
  }

  return (
    <ApprovalBg dark={dark}>
      <Header
        me={me} route={route} setRoute={setRoute} dark={dark}
        onLogout={async () => {
          await Api.logout();
          setMe(null);
          AudioService.stop();
          toast.info('Abgemeldet.');
        }}
      />
      {route === 'inbox'   && <ReviewQueue submissions={submissions} me={me} refresh={refresh} dark={dark}/>}
      {route === 'decided' && <DecidedList submissions={submissions} me={me} refresh={refresh} dark={dark}/>}
      {route === 'submit'  && <SubmitScreen me={me} setRoute={setRoute} refresh={refresh} dark={dark}/>}
      {route === 'reports' && <ReportsScreen me={me} dark={dark}/>}
      {route === 'audit'   && <AuditScreen   me={me} dark={dark}/>}
      {route === 'stats'   && <StatsScreen me={me} dark={dark}/>}
      {route === 'blocks'  && <BlocksScreen me={me} dark={dark}/>}
      {route === 'me'      && <ProfileScreen me={me} submissions={submissions} refresh={refresh} dark={dark}/>}
      <ToastContainer/>
    </ApprovalBg>
  );
}

// Spinner-and-text panel for the brief moment between page load and the first
// successful /api/me response. Doesn't show login state — that's the next
// render after we've decided.
function LoadingScreen() {
  return (
    <div style={{
      minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center',
      flexDirection: 'column', gap: 16,
    }}>
      <div style={{
        width: 40, height: 40, borderRadius: '50%',
        border: '3px solid rgba(255,255,255,0.1)',
        borderTopColor: '#ff4d8d',
        animation: 'spin 0.7s linear infinite',
      }}/>
      <div style={{ fontSize: 13, opacity: 0.5, letterSpacing: 0.5 }}>
        Storytime · Musik lädt…
      </div>
    </div>
  );
}

window.ApprovalApp = ApprovalApp;
