import { Routes, Route, Navigate, Outlet } from 'react-router-dom'
import { AuthProvider } from './contexts/AuthContext.jsx'
import { SiteProvider } from './contexts/SiteContext.jsx'
import MaintenanceGate from './components/MaintenanceGate.jsx'
import RequireAuth from './components/RequireAuth.jsx'
import RequirePlayer from './components/RequirePlayer.jsx'
import RoleGate from './components/RoleGate.jsx'
import { routesFor } from './modules/registry.js'
import { ModuleFeaturesProvider } from './modules/features.jsx'

// Public
import Portal from './routes/public/Portal.jsx'
import Website from './routes/public/Website.jsx'
import News from './routes/public/News.jsx'
import Screenshots from './routes/public/Screenshots.jsx'
import FiveOnFriday from './routes/public/FiveOnFriday.jsx'
import Newsletter from './routes/public/Newsletter.jsx'
import NewsletterIssue from './routes/public/NewsletterIssue.jsx'
import About from './routes/public/About.jsx'
import Status from './routes/public/Status.jsx'
import Wiki from './routes/wiki/Wiki.jsx'
import WikiArticle from './routes/wiki/WikiArticle.jsx'
import CmsPage from './routes/public/CmsPage.jsx'

// Admin
import AdminLogin from './routes/admin/AdminLogin.jsx'
import AdminLayout from './routes/admin/AdminLayout.jsx'
import Dashboard from './routes/admin/views/Dashboard.jsx'
import PostsAdmin from './routes/admin/views/PostsAdmin.jsx'
import PagesAdmin from './routes/admin/views/PagesAdmin.jsx'
import PageBuilder from './routes/admin/views/PageBuilder.jsx'
import WikiAdmin from './routes/admin/views/WikiAdmin.jsx'
import HeroEditor from './routes/admin/views/HeroEditor.jsx'
import AppearanceAdmin from './routes/admin/views/AppearanceAdmin.jsx'
import NavEditor from './routes/admin/views/NavEditor.jsx'
import SettingsAdmin from './routes/admin/views/SettingsAdmin.jsx'
import ActivityAdmin from './routes/admin/views/ActivityAdmin.jsx'
import BotActivityAdmin from './routes/admin/views/BotActivityAdmin.jsx'
import DiscordBotAdmin from './routes/admin/views/DiscordBotAdmin.jsx'
import AuthProvidersAdmin from './routes/admin/views/AuthProvidersAdmin.jsx'
import UsersAdmin from './routes/admin/views/UsersAdmin.jsx'
import UserDetail from './routes/admin/views/UserDetail.jsx'
import InvitesAdmin from './routes/admin/views/InvitesAdmin.jsx'
import ModulesAdmin from './routes/admin/views/ModulesAdmin.jsx'
import AccountAdmin from './routes/admin/views/AccountAdmin.jsx'
import Moderation from './routes/admin/views/Moderation.jsx'
import ModerationUser from './routes/admin/views/ModerationUser.jsx'
import Appeals from './routes/admin/views/Appeals.jsx'

// Player portal
import PlayerLogin from './routes/player/PlayerLogin.jsx'
import PlayerRegister from './routes/player/PlayerRegister.jsx'
import ForgotPassword from './routes/player/ForgotPassword.jsx'
import ResetPassword from './routes/player/ResetPassword.jsx'
import AcceptInvite from './routes/player/AcceptInvite.jsx'
import PlayerPortalLayout, { PlayerIndex } from './routes/player/PlayerPortalLayout.jsx'
import PlayerAccount from './routes/player/PlayerAccount.jsx'
import PlayerAppeals from './routes/player/PlayerAppeals.jsx'

export default function App() {
  return (
    <AuthProvider>
      <SiteProvider>
        {/* Inside the auth and site contexts, because a feature provider is a
            hook that may well read either — a live-status one does, indirectly,
            by asking an endpoint whose answer depends on the session. Outside the
            routes, so the nav in every layout is filtered by the same gate and
            the provider hooks are called once for the whole app rather than
            once per screen. */}
        <ModuleFeaturesProvider>
          <Routes>
            {/* Landing hero — always public, even in maintenance mode. The hero is
                itself the pre-launch "coming soon" page, so it sits outside the
                MaintenanceGate and every visitor sees it regardless of auth/site mode. */}
            <Route path="/" element={<Portal />} />

            {/* Rest of the public site — gated by maintenance mode (admins preview through it) */}
            <Route
              element={
                <MaintenanceGate>
                  <Outlet />
                </MaintenanceGate>
              }
            >
              <Route path="/site" element={<Website />} />
              <Route path="/site/news" element={<News />} />
              <Route path="/site/screenshots" element={<Screenshots />} />
              <Route path="/site/five-on-friday" element={<FiveOnFriday />} />
              <Route path="/site/newsletter" element={<Newsletter />} />
              <Route path="/site/newsletter/:id" element={<NewsletterIssue />} />
              <Route path="/site/about" element={<About />} />
              <Route path="/site/status" element={<Status />} />
              <Route path="/wiki" element={<Wiki />} />
              <Route path="/wiki/:slug" element={<WikiArticle />} />
              {/* Installed modules' public pages, namespaced `/<id>/…` — the
                  registry prefixes the segment, so a module cannot spell its way
                  out of it (docs/website/MODULE_API.md §3.3). Declared before the
                  CMS catch-all below: React Router ranks a static segment over a
                  dynamic one, so the order is not what saves us, but keeping the
                  two adjacent makes the relationship visible to whoever adds the
                  next route here. */}
              {routesFor('public').map((r) => (
                <Route key={r.path} path={`/${r.path}`} element={r.element} />
              ))}
              {/* CMS pages: top-level /:slug, matched only after the named routes
                  above (React Router ranks static routes over this dynamic one). */}
              <Route path="/:slug" element={<CmsPage />} />
            </Route>

            {/* Draft-preview link (token-gated). Outside the maintenance gate so a
                preview link works regardless of site mode. */}
            <Route path="/preview/:id/:token" element={<CmsPage preview />} />

            {/* Admin */}
            <Route path="/admin/login" element={<AdminLogin />} />
            <Route
              path="/admin"
              element={
                <RequireAuth>
                  <AdminLayout />
                </RequireAuth>
              }
            >
              <Route index element={<Dashboard />} />
              <Route path="posts" element={<PostsAdmin />} />
              <Route path="pages" element={<PagesAdmin />} />
              <Route path="pages/new" element={<PageBuilder />} />
              <Route path="pages/:id" element={<PageBuilder />} />
              <Route path="wiki" element={<WikiAdmin />} />
              <Route path="hero" element={<HeroEditor />} />
              {/* Theme editing writes an admin-only settings key; the route sits
                  behind the same RoleGate as the sidebar entry that reaches it,
                  and PUT/DELETE /admin/settings is admin-only server-side too. */}
              <Route
                path="appearance"
                element={
                  <RoleGate roles={['admin']}>
                    <AppearanceAdmin />
                  </RoleGate>
                }
              />
              {/* Same reasoning as Appearance: the nav overrides are an admin-only
                  settings key, so the route carries the same RoleGate as the
                  sidebar entry that reaches it. */}
              <Route
                path="navigation"
                element={
                  <RoleGate roles={['admin']}>
                    <NavEditor />
                  </RoleGate>
                }
              />
              <Route path="settings" element={<SettingsAdmin />} />
              <Route
                path="moderation"
                element={
                  <RoleGate roles={['admin', 'moderator']}>
                    <Outlet />
                  </RoleGate>
                }
              >
                <Route index element={<Moderation />} />
                <Route path="user/:discordId" element={<ModerationUser />} />
                <Route path="appeals" element={<Appeals />} />
              </Route>
              <Route path="activity" element={<ActivityAdmin />} />
              <Route path="bot-activity" element={<BotActivityAdmin />} />
              <Route path="discord-bot" element={<DiscordBotAdmin />} />
              <Route path="auth-providers" element={<AuthProvidersAdmin />} />
              <Route path="users" element={<UsersAdmin />} />
              <Route path="users/:id" element={<UserDetail />} />
              <Route path="invites" element={<InvitesAdmin />} />
              {/* Core's own screen, and it has to be: it is how a module reaches
                  the volume in the first place. Declared here with the rest of
                  core's routes, above the module-supplied ones below. */}
              <Route path="modules" element={<ModulesAdmin />} />
              <Route path="account" element={<AccountAdmin />} />
              {/* Installed modules' admin pages, at /admin/<id>/…, already inside
                  RequireAuth + AdminLayout. A module cannot supply its own auth
                  wrapper — only an optional { roles }, which core applies as the
                  same RoleGate its own routes above use, so the sidebar and the
                  route table cannot disagree about who may see what. Before the
                  `*` redirect, which would otherwise swallow every one of them. */}
              {routesFor('admin').map((r) => (
                <Route
                  key={r.path}
                  path={r.path}
                  element={r.gate ? <RoleGate roles={r.gate.roles}>{r.element}</RoleGate> : r.element}
                />
              ))}
              <Route path="*" element={<Navigate to="/admin" replace />} />
            </Route>

            {/* Player portal */}
            <Route path="/account/login" element={<PlayerLogin />} />
            <Route path="/account/register" element={<PlayerRegister />} />
            <Route path="/account/forgot" element={<ForgotPassword />} />
            <Route path="/account/reset/:token" element={<ResetPassword />} />
            <Route path="/invite/:token" element={<AcceptInvite />} />
            <Route
              element={
                <RequirePlayer>
                  <PlayerPortalLayout />
                </RequirePlayer>
              }
            >
              {/* The portal index resolves to the first nav row this viewer can
                  reach rather than naming a page: `PlayerCharacters` was a UO
                  page and left with the client half (MODULE_SYSTEM.md §2.7.1).
                  With the UO module installed that is still Characters. */}
              <Route path="/player" element={<PlayerIndex />} />
              <Route path="/account" element={<PlayerAccount />} />
              <Route path="/account/appeals" element={<PlayerAppeals />} />
              {/* Installed modules' player-portal pages, at /player/<id>/…. This
                  group's own routes are absolute (its layout route has no path),
                  so the prefix is written here rather than inherited — the one
                  place the three areas do not read alike. */}
              {routesFor('player').map((r) => (
                <Route
                  key={r.path}
                  path={`/player/${r.path}`}
                  element={r.gate ? <RoleGate roles={r.gate.roles}>{r.element}</RoleGate> : r.element}
                />
              ))}
            </Route>

            <Route path="*" element={<Navigate to="/" replace />} />
          </Routes>
        </ModuleFeaturesProvider>
      </SiteProvider>
    </AuthProvider>
  )
}
