import fastify, { type FastifyReply, type FastifyRequest } from "fastify";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import cors from "@fastify/cors";
import cookie from "@fastify/cookie";
import helmet from "@fastify/helmet";
import jwt from "@fastify/jwt";
import rateLimit from "@fastify/rate-limit";
import fastifyStatic from "@fastify/static";
import * as nodemailer from "nodemailer";
import Stripe from "stripe";
import type { AppConfig } from "./config.js";
import { initDb, pingDb } from "./db.js";
import type { Pool } from "mysql2/promise";
import { LEVELS_PER_DIFFICULTY as CATALOG_LEVELS } from "./level-generator.js";
import {
  AID_PACKS,
  DIFFICULTIES,
  LOCK_TIME_MS,
  MAX_FAILED_LOGINS,
  REFRESH_COOKIE_NAME,
  REFRESH_TTL,
  RESET_TOKEN_TTL_MS,
  REWARD_POINTS,
  TOKEN_TTL,
  type DifficultyKey,
} from "./server/constants.js";
import {
  clampInt,
  expiresAtFrom,
  getDayOfYear,
  hashPassword,
  hashResetToken,
  hashToken,
  isLegacyBcrypt,
  isLocked,
  randomId,
  randomTokenBase64Url,
  verifyPassword,
} from "./server/crypto-utils.js";
import {
  ensureAdminColumn,
  ensureAuthColumns,
  ensureVipNoAdsColumn,
  ensureArcadeStats,
  ensureDailyProgress,
  ensureInfiniteStats,
  ensureProgressRows,
  ensureSchema,
  ensureUserData,
  ensureUserSettings,
  ensureUserStats,
  ensureWallet,
  migrateLeaderboardScore,
  seedLevelsCatalog,
  ensureAvatarPacksSeed,
  ensureBallSkinPacksSeed,
  ensureLaunchDateSeed,
  ensureUserSettingsColumns,
} from "./server/db-schema.js";
import { computeBadges } from "./server/badges.js";
import { parseJsonArray, parseLevelPayload } from "./server/parsers.js";
// Services modulaires
import {
  fetchArcadeStats,
  fetchDailyProgress,
  fetchInfiniteStats,
  fetchProgress,
  fetchSettings,
  fetchStats,
  fetchWallet,
  fetchAppConfig,
  setAppConfigValue,
} from "./server/data-fetchers.js";
import {
  mapUserRow,
  toPublicUser,
  getUserByEmail,
  getUserById,
  createUser,
  createGuestUser,
  isAdminUser,
} from "./server/user-service.js";
import {
  cleanupRefreshTokens,
  revokeRefreshTokensForUser,
  storeRefreshToken,
  verifyRefreshToken,
  revokeRefreshToken,
  clearLoginFailures,
  recordFailedLogin,
  storeResetToken,
  consumeResetToken,
} from "./server/auth-service.js";
import {
  buildResetEmail,
  buildContactEmail,
  escapeHtml,
} from "./server/email-service.js";
import { recordAudit, fetchAdminOverview } from "./server/admin-service.js";
import {
  registerHealthRoutes,
  registerAdminRoutes,
  registerAdminPanelRoutes,
  registerAdminMonitoringRoutes,
  registerLeaderboardRoutes,
  registerLevelsRoutes,
  registerAuthRoutes,
  registerProfileRoutes,
  registerStatsRoutes,
  registerGameModeRoutes,
  registerDailyRoutes,
  registerWeeklyRoutes,
  registerChallengePackRoutes,
  registerAvatarPackRoutes,
  registerBallSkinPackRoutes,
  registerBallSkinImageRoutes,
  registerAvatarImageRoutes,
  registerPaymentsRoutes,
  registerWebhookRoutes,
  registerWalletRoutes,
  registerSettingsRoutes,
  registerProgressRoutes,
  registerContactRoutes,
  registerAdsRoutes,
  registerPrivacyRoutes,
  registerBonusRoutes,
  type RouteContext,
} from "./routes/index.js";
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const packageJson = require("../package.json") as { version: string };
import type {
  AidKind,
  ArcadeStats,
  DailyProgress,
  InfiniteStats,
  LevelStat,
  ProgressPayload,
  RecentRun,
  RefreshTokenRow,
  SettingsState,
  StatsState,
  User,
  UserRecord,
  UserRow,
  WalletPayload,
  WalletRow,
} from "./server/types.js";

declare module "@fastify/jwt" {
  interface FastifyJWT {
    payload: {
      sub: number;
      email: string;
      typ?: "access" | "refresh";
      ev?: boolean;
    };
    user: { sub: number; email: string; typ?: "access" | "refresh"; ev?: boolean };
  }
}
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const buildServer = async (config: AppConfig) => {
  const app = fastify({
    logger: {
      level: config.appEnv === "production" ? "info" : "debug",
    },
    // Derrière Traefik (reverse proxy): nécessaire pour request.ip, rate limiting, logs, etc.
    trustProxy: true,
  });
  const db = initDb(config);
  const mailer =
    config.smtp.host && config.smtp.port && config.smtp.fromEmail
      ? nodemailer.createTransport({
          host: config.smtp.host,
          port: config.smtp.port,
          secure: config.smtp.secure,
          auth:
            config.smtp.user && config.smtp.password
              ? { user: config.smtp.user, pass: config.smtp.password }
              : undefined,
        })
      : null;

  await ensureSchema(db);
  await ensureAdminColumn(db);
  await ensureVipNoAdsColumn(db);
  await ensureAuthColumns(db);
  await ensureUserSettingsColumns(db);
  await ensureAvatarPacksSeed(db);
  await ensureBallSkinPacksSeed(db);
  await ensureLaunchDateSeed(db);
  await migrateLeaderboardScore(db);
  await seedLevelsCatalog(db);

  await app.register(cookie);

  await app.register(helmet, {
    // CSP géré côté Traefik pour le frontend, ici on protège uniquement l'API
    contentSecurityPolicy: false,
    // HSTS géré par Traefik (terminaison TLS)
    hsts: false,
    // Headers de sécurité de base
    xContentTypeOptions: true,
    xFrameOptions: { action: "deny" as const },
    referrerPolicy: { policy: "strict-origin-when-cross-origin" as const },
  });

  await app.register(cors, {
    origin:
      config.appEnv === "production"
        ? [
            "https://rollerlogic.com",
            "https://www.rollerlogic.com",
            "https://api.rollerlogic.com",
            "https://admin.rollerlogic.com",
            "https://sysop.rollerlogic.com",
            "https://localhost",
            "capacitor://localhost",
            "http://localhost",
            "http://localhost:3000",
            "http://localhost:5173",
            "http://127.0.0.1:3000",
            "http://127.0.0.1:5173",
          ]
        : [
            "http://localhost:5173",
            "http://localhost:3000",
            "http://127.0.0.1:5173",
            "http://127.0.0.1:3000",
            "capacitor://localhost",
            "http://localhost",
          ],
    // Note: credentials=false puisqu'on utilise Bearer token au lieu de cookies
    // pour les requêtes cross-domain.
    //
    // Cependant, le flow admin utilise un refresh token en cookie httpOnly,
    // donc il faut autoriser les credentials pour que le navigateur envoie/stocke ce cookie.
    credentials: true,
    methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
  });

  await app.register(jwt, {
    secret: config.auth.jwtSecret,
  });

  await app.register(rateLimit, {
    max: 300,
    timeWindow: "1 minute",
    // Exclure les fichiers statiques du rate limiting
    allowList: (request) => {
      const url = request.url;
      // Ne pas limiter les assets, images, avatars, sons, etc.
      if (
        url.startsWith("/assets/") ||
        url.startsWith("/avatars/") ||
        url.startsWith("/api/avatars/") ||
        url.startsWith("/images/") ||
        url.startsWith("/api/ball-skins/") ||
        url.startsWith("/sons/") ||
        url.endsWith(".js") ||
        url.endsWith(".css") ||
        url.endsWith(".png") ||
        url.endsWith(".jpg") ||
        url.endsWith(".svg") ||
        url.endsWith(".gif") ||
        url.endsWith(".webp") ||
        url.endsWith(".mp3") ||
        url.endsWith(".ogg") ||
        url.endsWith(".woff") ||
        url.endsWith(".woff2") ||
        url === "/" ||
        url === "/index.html"
      ) {
        return true;
      }
      return false;
    },
  });

  app.addHook("onRequest", (request, reply, done) => {
    const urlPath = request.url.split("?", 1)[0] ?? "";
    if (
      urlPath.startsWith("/images/billes/") &&
      urlPath !== "/images/billes/bille_base.png"
    ) {
      reply.code(404).send({ error: "Not found" });
      return;
    }
    done();
  });

  const publicRoot = path.join(__dirname, "..", "public");
  if (fs.existsSync(publicRoot)) {
    await app.register(fastifyStatic, {
      root: publicRoot,
      prefix: "/",
      decorateReply: true,
      setHeaders: (res, servedPath) => {
        const filename = path.basename(servedPath);
        if (filename === "index.html") {
          res.setHeader(
            "Cache-Control",
            "no-store, no-cache, must-revalidate, max-age=0",
          );
          return;
        }
        if (servedPath.includes(`${path.sep}assets${path.sep}`)) {
          res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
          return;
        }
        res.setHeader("Cache-Control", "public, max-age=300");
      },
    });
  }

  type MaintenanceState = {
    enabled: boolean;
    message: string;
    maintenanceEnd: string | null;
    launchDate: string | null;
    fetchedAtMs: number;
  };

  let maintenanceCache: MaintenanceState | null = null;
  let maintenancePending: Promise<MaintenanceState> | null = null;
  const MAINTENANCE_CACHE_TTL_MS = 1000;

  const fetchMaintenanceState = async (): Promise<MaintenanceState> => {
    const now = Date.now();
    if (
      maintenanceCache &&
      now - maintenanceCache.fetchedAtMs < MAINTENANCE_CACHE_TTL_MS
    ) {
      return maintenanceCache;
    }
    if (maintenancePending) {
      return maintenancePending;
    }
    maintenancePending = (async () => {
      try {
        const [rows] = await db.execute(
          "SELECT config_key, config_value FROM app_config WHERE config_key IN ('maintenance_mode', 'maintenance_message', 'maintenance_end', 'launch_date')",
        );
        let enabled = false;
        let message = "Mise à jour en cours. Veuillez patienter...";
        let maintenanceEnd: string | null = null;
        let launchDate: string | null = null;
        if (Array.isArray(rows)) {
          for (const row of rows) {
            const item = row as { config_key?: string; config_value?: string };
            const key = item.config_key;
            const rawValue = (item.config_value ?? "").trim().toLowerCase();
            if (key === "maintenance_mode") {
              enabled = rawValue === "1" || rawValue === "true";
            }
            if (key === "maintenance_message" && item.config_value) {
              message = item.config_value;
            }
            if (key === "maintenance_end" && item.config_value) {
              maintenanceEnd = item.config_value;
            }
            if (
              key === "launch_date" &&
              item.config_value &&
              item.config_value !== "unset"
            ) {
              launchDate = item.config_value;
            }
          }
        }
        maintenanceCache = {
          enabled,
          message,
          maintenanceEnd,
          launchDate,
          fetchedAtMs: now,
        };
        return maintenanceCache;
      } catch {
        maintenanceCache = {
          enabled: false,
          message: "",
          maintenanceEnd: null,
          launchDate: null,
          fetchedAtMs: now,
        };
        return maintenanceCache;
      } finally {
        maintenancePending = null;
      }
    })();
    return maintenancePending;
  };

  const isAllowedDuringMaintenance = (pathname: string) => {
    if (
      pathname === "/health" ||
      pathname === "/version" ||
      pathname === "/maintenance" ||
      pathname === "/api/health" ||
      pathname === "/api/version" ||
      pathname === "/api/maintenance"
    ) {
      return true;
    }
    // Admin endpoints keep working during maintenance
    if (pathname === "/admin" || pathname.startsWith("/admin/")) return true;
    if (pathname === "/config/public") return true;
    if (
      pathname === "/auth/login" ||
      pathname === "/auth/refresh" ||
      pathname === "/auth/verify-email" ||
      pathname === "/api/auth/login" ||
      pathname === "/api/auth/refresh" ||
      pathname === "/api/auth/verify-email"
    ) {
      return true;
    }
    // Stripe webhook must keep working
    if (pathname.startsWith("/payments/stripe/webhook")) return true;
    return false;
  };

  app.addHook("onRequest", async (request, reply) => {
    const rawUrl = request.raw.url ?? request.url;
    const pathname = rawUrl.split("?")[0] || "/";

    if (isAllowedDuringMaintenance(pathname)) return;

    const state = await fetchMaintenanceState();
    if (!state.enabled) return;

    reply.code(503);
    reply.header("Retry-After", "300");

    const acceptsHtml = request.headers.accept?.includes("text/html");
    if (request.method === "GET" && acceptsHtml) {
      reply.type("text/html; charset=utf-8");
      return reply.send(`<!doctype html>
<html lang="fr">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>RollerLogic - Maintenance</title>
    <style>
      :root { color-scheme: light; font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; }
      body { margin: 0; padding: 48px 20px; background: #0b1220; color: #e2e8f0; }
      .card { max-width: 720px; margin: 0 auto; background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.14); border-radius: 16px; padding: 20px; }
      h1 { margin: 0 0 8px; font-size: 22px; }
      p { margin: 0; color: rgba(226,232,240,0.85); line-height: 1.5; }
      small { display:block; margin-top: 14px; opacity: 0.75; }
    </style>
  </head>
  <body>
    <div class="card">
      <h1>Maintenance en cours</h1>
      <p>${state.message.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;")}</p>
      <small>Réessayez dans quelques minutes.</small>
    </div>
  </body>
</html>`);
    }

    return reply.send({
      error: "Maintenance",
      maintenance: true,
      message: state.message,
      maintenanceEnd: state.maintenanceEnd,
    });
  });

  app.setErrorHandler((error, request, reply) => {
    request.log.error({ err: error }, "Unhandled error");
    const err = error as {
      validation?: unknown;
      statusCode?: number;
      message?: string;
    };
    if (err.validation) {
      reply.code(400);
      return reply.send({ error: "Payload invalide" });
    }
    const status = typeof err.statusCode === "number" ? err.statusCode : 500;
    const message =
      config.appEnv === "production"
        ? "Erreur serveur"
        : (err.message ?? "Erreur serveur");
    reply.code(status);
    return reply.send({ error: message });
  });

  app.setNotFoundHandler((request, reply) => {
    // Pour les requêtes de navigateur (SPA routing), servir index.html
    const acceptsHtml = request.headers.accept?.includes("text/html");
    const isApiRequest = request.url.startsWith("/api/");

    if (request.method === "GET" && acceptsHtml && !isApiRequest) {
      // Servir index.html pour les routes SPA
      return reply.sendFile("index.html");
    }

    // Pour les requêtes API qui n'existent pas
    reply.code(404);
    return reply.send({ error: "Route inconnue" });
  });

  app.addHook("onResponse", (request, reply, done) => {
    const elapsedTime = (request as { elapsedTime?: number }).elapsedTime;
    const elapsed: number = typeof elapsedTime === "number" ? elapsedTime : 0;
    const payload = {
      method: request.method,
      url: request.url,
      statusCode: reply.statusCode,
      responseTime: elapsed,
    };
    if (reply.statusCode >= 500) {
      request.log.error(payload, "request_failed");
    } else if (elapsed > 800) {
      request.log.warn(payload, "request_slow");
    } else {
      request.log.info(payload, "request_completed");
    }
    done();
  });

  const onlinePresenceWindowMsRaw = Number(
    process.env.ADMIN_ONLINE_PRESENCE_WINDOW_MS ?? "600000",
  );
  const onlinePresenceWindowMs =
    Number.isFinite(onlinePresenceWindowMsRaw) && onlinePresenceWindowMsRaw > 0
      ? Math.round(onlinePresenceWindowMsRaw)
      : 600000;
  const onlinePresence = new Map<number, number>();

  const pruneOnlinePresence = (now: number) => {
    const cutoff = now - onlinePresenceWindowMs;
    for (const [userId, lastSeenAt] of onlinePresence.entries()) {
      if (lastSeenAt < cutoff) {
        onlinePresence.delete(userId);
      }
    }
  };

  const markUserOnline = (userId: number | null | undefined) => {
    if (!Number.isInteger(userId) || Number(userId) <= 0) {
      return;
    }
    const now = Date.now();
    onlinePresence.set(Number(userId), now);
    if (onlinePresence.size > 2000) {
      pruneOnlinePresence(now);
    }
  };

  const getOnlineUserIds = (): number[] => {
    const now = Date.now();
    pruneOnlinePresence(now);
    return Array.from(onlinePresence.keys());
  };

  const getUserLastSeen = (userId: number): number | null => {
    const now = Date.now();
    const lastSeenAt = onlinePresence.get(userId);
    if (typeof lastSeenAt !== "number") {
      return null;
    }
    if (lastSeenAt < now - onlinePresenceWindowMs) {
      onlinePresence.delete(userId);
      return null;
    }
    return lastSeenAt;
  };

  const isAllowedForUnverified = (pathname: string) => {
    const normalized = pathname.startsWith("/api/")
      ? pathname.slice(4)
      : pathname;
    return (
      normalized === "/auth/me" ||
      normalized === "/auth/logout" ||
      normalized === "/auth/refresh" ||
      normalized === "/auth/resend-verification" ||
      normalized === "/account"
    );
  };

  const verifyAccessToken = async (
    request: FastifyRequest,
    reply: FastifyReply,
  ) => {
    try {
      await request.jwtVerify();
    } catch {
      reply.code(401);
      reply.send({ error: "Authentification requise" });
      return false;
    }
    if (request.user?.typ && request.user.typ !== "access") {
      reply.code(401);
      reply.send({ error: "Authentification requise" });
      return false;
    }
    if (request.user?.ev === false) {
      const routePath = request.routeOptions.url ?? request.url.split("?")[0];
      if (!isAllowedForUnverified(routePath)) {
        const userId = Number(request.user?.sub ?? 0);
        if (Number.isInteger(userId) && userId > 0) {
          const user = await getUserById(db, userId);
          if (user?.emailVerified) {
            request.user.ev = true;
            return true;
          }
        }
        reply.code(403);
        reply.send({
          error:
            "Email non verifie. Verifiez votre boite mail pour activer le jeu.",
        });
        return false;
      }
    }
    return true;
  };

  const requireAuth = async (request: FastifyRequest, reply: FastifyReply) => {
    const ok = await verifyAccessToken(request, reply);
    if (!ok) {
      return reply;
    }
    markUserOnline(Number(request.user?.sub));
  };

  const requireAdmin = async (request: FastifyRequest, reply: FastifyReply) => {
    const ok = await verifyAccessToken(request, reply);
    if (!ok) {
      return;
    }
    const userId = request.user?.sub;
    markUserOnline(Number(userId));
    if (!userId) {
      reply.code(401);
      return reply.send({ error: "Authentification requise" });
    }
    const admin = await isAdminUser(db, userId);
    if (!admin) {
      reply.code(403);
      return reply.send({ error: "Acces refuse" });
    }
  };

  const signAccessToken = (user: {
    id: number;
    email: string;
    emailVerified?: boolean;
  }) =>
    app.jwt.sign(
      {
        sub: user.id,
        email: user.email,
        typ: "access",
        ev: user.emailVerified !== false,
      },
      { expiresIn: TOKEN_TTL },
    );
  const signRefreshToken = (user: {
    id: number;
    email: string;
    emailVerified?: boolean;
  }) =>
    app.jwt.sign(
      {
        sub: user.id,
        email: user.email,
        typ: "refresh",
        ev: user.emailVerified !== false,
      },
      { expiresIn: REFRESH_TTL },
    );

  const refreshCookieOptions = () => ({
    httpOnly: true,
    secure: config.cookies.secure,
    sameSite: config.cookies.sameSite,
    path: "/",
    maxAge: 7 * 24 * 60 * 60, // 7 jours en secondes
    ...(config.cookies.domain ? { domain: config.cookies.domain } : {}),
  });

  const setRefreshCookie = (reply: FastifyReply, token: string) => {
    reply.setCookie(REFRESH_COOKIE_NAME, token, refreshCookieOptions());
  };

  const clearRefreshCookie = (reply: FastifyReply) => {
    reply.clearCookie(REFRESH_COOKIE_NAME, refreshCookieOptions());
  };

  const getRefreshCookie = (request: FastifyRequest) => {
    const raw = request.cookies?.[REFRESH_COOKIE_NAME];
    if (typeof raw === "string" && raw.trim()) {
      return raw.trim();
    }
    return undefined;
  };

  const paramsUserIdSchema = {
    type: "object",
    required: ["userId"],
    additionalProperties: false,
    properties: {
      userId: { type: "string", pattern: "^[0-9]+$" },
    },
  } as const;

  const bodyUserIdSchema = {
    type: "object",
    required: ["userId"],
    additionalProperties: false,
    properties: {
      userId: { type: "integer", minimum: 1 },
    },
  } as const;

  const difficultySchema = {
    type: "string",
    enum: DIFFICULTIES,
  } as const;

  const aidKindSchema = {
    type: "string",
    enum: ["hint", "undo", "replay"],
  } as const;

  const packIdSchema = {
    type: "string",
    enum: AID_PACKS.map((pack) => pack.id),
  } as const;

  app.addHook("onClose", async () => {
    await db.end();
  });

  // Initialiser Stripe
  const stripeSecretKey = config.payments?.stripeSecretKey;
  const stripe = stripeSecretKey
    ? new Stripe(stripeSecretKey, { apiVersion: "2026-01-28.clover" })
    : null;

  // Contexte partagé pour les routes modulaires
  const routeContext: RouteContext = {
    db,
    config,
    mailer,
    signAccessToken,
    signRefreshToken,
    setRefreshCookie,
    getRefreshCookie,
    clearRefreshCookie,
    requireAuth,
    requireAdmin,
    packageVersion: packageJson.version,
    appEnv: config.appEnv,
    getOnlineUserIds,
    getUserLastSeen,
    onlinePresenceWindowMs,
  };

  // Enregistrer les webhooks SANS préfixe /api (Stripe envoie à /payments/stripe/webhook)
  await registerWebhookRoutes(
    app,
    db,
    stripe,
    config.payments?.stripeWebhookSecret,
  );

  // Enregistrement des routes modulaires avec préfixe /api
  await app.register(
    async (apiApp) => {
      await registerHealthRoutes(apiApp, routeContext);
      await registerAdminRoutes(apiApp, routeContext);
      await registerAdminPanelRoutes(apiApp, routeContext);
      await registerAdminMonitoringRoutes(apiApp, routeContext);
      await registerLeaderboardRoutes(apiApp, routeContext);
      await registerLevelsRoutes(apiApp, routeContext);
      registerAuthRoutes(apiApp, routeContext);
      registerProfileRoutes(apiApp, routeContext);
      registerStatsRoutes(apiApp, routeContext);
      registerGameModeRoutes(apiApp, routeContext);
      registerDailyRoutes(apiApp, routeContext);
      registerWeeklyRoutes(apiApp, routeContext);
      registerChallengePackRoutes(apiApp, routeContext);
      registerAvatarPackRoutes(apiApp, routeContext);
      registerBallSkinPackRoutes(apiApp, routeContext);
      registerBallSkinImageRoutes(apiApp, routeContext);
      registerAvatarImageRoutes(apiApp, routeContext);
      registerPaymentsRoutes(apiApp, routeContext);
      registerWalletRoutes(apiApp, routeContext);
      registerSettingsRoutes(apiApp, routeContext);
      registerProgressRoutes(apiApp, routeContext);
      registerContactRoutes(apiApp, routeContext);
      registerPrivacyRoutes(apiApp, routeContext);
      registerAdsRoutes(apiApp, routeContext);
      registerBonusRoutes(apiApp, routeContext);
    },
    { prefix: "/api" },
  );

  // Compatibilite legacy: anciens clients admin sans prefixe /api.
  // Permet d'eviter les 404 sur /auth/login et /admin/stats le temps que
  // toutes les instances du panel soient mises a jour.
  await app.register(async (legacyApp) => {
    await registerHealthRoutes(legacyApp, routeContext);
    await registerAdminRoutes(legacyApp, routeContext);
    await registerAdminPanelRoutes(legacyApp, routeContext);
    await registerAdminMonitoringRoutes(legacyApp, routeContext);
    await registerLeaderboardRoutes(legacyApp, routeContext);
    registerAuthRoutes(legacyApp, routeContext);
  });

  return app;
};

export { buildServer };
