import { lazy, Suspense, useEffect } from 'react';
import { HashRouter, Navigate, Outlet, Route, Routes, useParams } from 'react-router-dom';

import { AppShell } from './components/AppShell';
import { CrashToast } from './components/CrashToast';
import { ErrorBoundary } from './components/ErrorBoundary';
import { RemoteRevokedScreen } from './components/RemoteRevokedScreen';
import { SpinnerIcon } from './components/icons';
import { UpdateToast } from './components/UpdateToast';
import { RequireAdmin, RequireAuth, RequireDevice } from './components/guards';
import { AuthProvider } from './context/AuthContext';
import { CallProvider } from './context/CallContext';
import { ConversationsProvider } from './context/ConversationsContext';
import { FriendshipsProvider } from './context/FriendshipsContext';
import { ThemeProvider } from './context/ThemeContext';
import { hydrateDrafts } from './lib/composerDraftStore';
import { AuthPage } from './pages/AuthPage';
import { ChatsEmptyState, ChatsPage } from './pages/ChatsPage';
import { ConversationPage } from './pages/ConversationPage';

// Routes rarely visited on first render are pulled out of the initial bundle.
// AuthPage stays eager because it's the first screen unauthenticated users
// see; ChatsPage + ConversationPage stay eager because every authenticated
// session renders them immediately.
const AdminPage = lazy(() => import('./pages/AdminPage').then((m) => ({ default: m.AdminPage })));
const AuthCallbackPage = lazy(() =>
  import('./pages/AuthCallbackPage').then((m) => ({ default: m.AuthCallbackPage })),
);
const DevicePage = lazy(() => import('./pages/DevicePage').then((m) => ({ default: m.DevicePage })));
const FriendsPage = lazy(() =>
  import('./pages/FriendsPage').then((m) => ({ default: m.FriendsPage })),
);
const SettingsPage = lazy(() =>
  import('./pages/SettingsPage').then((m) => ({ default: m.SettingsPage })),
);
const ChangelogPage = lazy(() =>
  import('./pages/ChangelogPage').then((m) => ({ default: m.ChangelogPage })),
);

function RouteSuspense({ children }: { children: React.ReactNode }) {
  return (
    <Suspense
      fallback={
        <div className="flex min-h-full w-full items-center justify-center bg-surface-3">
          <SpinnerIcon className="h-5 w-5 text-accent" />
        </div>
      }
    >
      {children}
    </Suspense>
  );
}

// Isolates each top-level route so a crash in one page doesn't take the whole
// shell down. The boundary auto-retries via `ErrorBoundary`'s backoff schedule.
function RouteBoundary({ scope }: { scope: string }) {
  return (
    <ErrorBoundary scope={scope}>
      <Outlet />
    </ErrorBoundary>
  );
}

// Forces a fresh `ConversationPage` instance per `:id` so React unmounts
// the previous conversation entirely on switch. Without this, the same
// component instance handles every conversation, which leaks state
// between chats (messages, scroll position, composer drafts) for one
// render frame and gives the "flicker" we're trying to remove.
// `useConversationMessages` re-hydrates from `messageMemoryCache` on the
// fresh mount so previously-visited chats still render instantly.
function ConversationRoute() {
  const { id } = useParams<{ id: string }>();
  return <ConversationPage key={id ?? '__no_id__'} />;
}

export function App() {
  useEffect(() => {
    void hydrateDrafts();
  }, []);

  return (
    <ErrorBoundary scope="root">
      <ThemeProvider>
        <AuthProvider>
          <FriendshipsProvider>
            <ConversationsProvider>
              <CallProvider>
                <HashRouter
                  future={{
                    // Opt into v7 behaviour early so the upgrade is a no-op:
                    // - `v7_startTransition` wraps navigations in startTransition
                    //   so Suspense / concurrent rendering deal with the new tree
                    // - `v7_relativeSplatPath` matches relative paths inside
                    //   splat routes against the parent splat segment (not the
                    //   full matched path). Our tree has no splat routes today
                    //   but this kills the runtime warning and future-proofs.
                    v7_startTransition: true,
                    v7_relativeSplatPath: true,
                  }}
                >
                  <Routes>
                    <Route element={<RouteBoundary scope="auth" />}>
                      <Route path="/auth" element={<AuthPage />} />
                      <Route
                        path="/auth/callback"
                        element={
                          <RouteSuspense>
                            <AuthCallbackPage />
                          </RouteSuspense>
                        }
                      />
                    </Route>
                    <Route element={<RequireAuth />}>
                      <Route element={<RouteBoundary scope="device" />}>
                        <Route
                          path="/device"
                          element={
                            <RouteSuspense>
                              <DevicePage />
                            </RouteSuspense>
                          }
                        />
                      </Route>
                      <Route element={<RequireDevice />}>
                        <Route element={<AppShell />}>
                          <Route index element={<Navigate to="/chats" replace />} />
                          <Route element={<RouteBoundary scope="chats" />}>
                            <Route path="/chats" element={<ChatsPage />}>
                              <Route index element={<ChatsEmptyState />} />
                              <Route
                                path=":id"
                                element={
                                  <ErrorBoundary scope="conversation">
                                    <ConversationRoute />
                                  </ErrorBoundary>
                                }
                              />
                            </Route>
                          </Route>
                          <Route element={<RouteBoundary scope="friends" />}>
                            <Route
                              path="/friends"
                              element={
                                <RouteSuspense>
                                  <FriendsPage />
                                </RouteSuspense>
                              }
                            />
                          </Route>
                          <Route element={<RouteBoundary scope="settings" />}>
                            <Route
                              path="/settings"
                              element={
                                <RouteSuspense>
                                  <SettingsPage />
                                </RouteSuspense>
                              }
                            />
                          </Route>
                          <Route element={<RouteBoundary scope="changelog" />}>
                            <Route
                              path="/changelog"
                              element={
                                <RouteSuspense>
                                  <ChangelogPage />
                                </RouteSuspense>
                              }
                            />
                          </Route>
                          <Route element={<RequireAdmin />}>
                            <Route element={<RouteBoundary scope="admin" />}>
                              <Route
                                path="/admin"
                                element={
                                  <RouteSuspense>
                                    <AdminPage />
                                  </RouteSuspense>
                                }
                              />
                            </Route>
                          </Route>
                        </Route>
                      </Route>
                    </Route>
                    <Route path="*" element={<Navigate to="/chats" replace />} />
                  </Routes>
                  <UpdateToast />
                  <CrashToast />
                  <RemoteRevokedScreen />
                </HashRouter>
              </CallProvider>
            </ConversationsProvider>
          </FriendshipsProvider>
        </AuthProvider>
      </ThemeProvider>
    </ErrorBoundary>
  );
}
