import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
import type { RouteContext } from "./types.js";
import { ensureArcadeStats, ensureInfiniteStats } from "../server/db-schema.js";
import {
  fetchArcadeStats,
  fetchInfiniteStats,
} from "../server/data-fetchers.js";
import { recordAudit } from "../server/admin-service.js";

const paramsUserIdSchema = {
  type: "object" as const,
  required: ["userId"],
  properties: { userId: { type: "string", pattern: "^[0-9]+$" } },
};

export const registerGameModeRoutes = (
  app: FastifyInstance,
  ctx: RouteContext,
) => {
  const { db, requireAuth } = ctx;

  // ========== ARCADE ==========

  // GET /arcade/:userId
  app.get(
    "/arcade/:userId",
    {
      preHandler: requireAuth,
      schema: { params: paramsUserIdSchema },
    },
    async (request, reply) => {
      const userId = Number((request.params as { userId: string }).userId);
      const tokenUserId = request.user?.sub;
      if (!Number.isFinite(userId)) {
        reply.code(400);
        return { error: "userId invalide" };
      }
      if (!tokenUserId || tokenUserId !== userId) {
        reply.code(403);
        return { error: "Acces refuse" };
      }
      const stats = await fetchArcadeStats(db, userId);
      return { stats };
    },
  );

  // POST /arcade/:userId/record
  app.post(
    "/arcade/:userId/record",
    {
      preHandler: requireAuth,
      schema: {
        params: paramsUserIdSchema,
        body: {
          type: "object",
          required: ["score", "levels"],
          additionalProperties: false,
          properties: {
            score: { type: "integer", minimum: 0 },
            levels: { type: "integer", minimum: 0 },
          },
        },
      },
    },
    async (request, reply) => {
      const userId = Number((request.params as { userId: string }).userId);
      const body = request.body as
        | { score?: number; levels?: number }
        | undefined;
      const tokenUserId = request.user?.sub;
      const score = Number(body?.score);
      const levels = Number(body?.levels);
      if (!Number.isFinite(userId)) {
        reply.code(400);
        return { error: "userId invalide" };
      }
      if (!tokenUserId || tokenUserId !== userId) {
        reply.code(403);
        return { error: "Acces refuse" };
      }
      if (
        !Number.isFinite(score) ||
        score < 0 ||
        !Number.isFinite(levels) ||
        levels < 0
      ) {
        reply.code(400);
        return { error: "Payload invalide" };
      }
      await ensureArcadeStats(db, userId);
      const current = await fetchArcadeStats(db, userId);
      const bestScore = score > current.bestScore ? score : current.bestScore;
      const bestLevels =
        score > current.bestScore ? levels : current.bestLevels;
      await db.execute(
        "UPDATE arcade_stats SET last_score = ?, last_levels = ?, last_played_at = NOW(), best_score = ?, best_levels = ? WHERE user_id = ?",
        [score, levels, bestScore, bestLevels, userId],
      );
      await recordAudit(db, {
        userId,
        action: "arcade.record",
        meta: { score, levels },
        ip: request.ip,
        userAgent: request.headers["user-agent"]?.toString() ?? null,
        installationId: request.headers["x-installation-id"]?.toString() ?? null,
      });
      const stats = await fetchArcadeStats(db, userId);
      return { stats };
    },
  );

  // ========== INFINITE ==========

  // GET /infinite/:userId
  app.get(
    "/infinite/:userId",
    {
      preHandler: requireAuth,
      schema: { params: paramsUserIdSchema },
    },
    async (request, reply) => {
      const userId = Number((request.params as { userId: string }).userId);
      const tokenUserId = request.user?.sub;
      if (!Number.isFinite(userId)) {
        reply.code(400);
        return { error: "userId invalide" };
      }
      if (!tokenUserId || tokenUserId !== userId) {
        reply.code(403);
        return { error: "Acces refuse" };
      }
      const stats = await fetchInfiniteStats(db, userId);
      return { stats };
    },
  );

  // POST /infinite/:userId/record
  app.post(
    "/infinite/:userId/record",
    {
      preHandler: requireAuth,
      schema: {
        params: paramsUserIdSchema,
        body: {
          type: "object",
          required: ["levels"],
          additionalProperties: false,
          properties: {
            levels: { type: "integer", minimum: 0 },
          },
        },
      },
    },
    async (request, reply) => {
      const userId = Number((request.params as { userId: string }).userId);
      const body = request.body as { levels?: number } | undefined;
      const tokenUserId = request.user?.sub;
      const levels = Number(body?.levels);
      if (!Number.isFinite(userId)) {
        reply.code(400);
        return { error: "userId invalide" };
      }
      if (!tokenUserId || tokenUserId !== userId) {
        reply.code(403);
        return { error: "Acces refuse" };
      }
      if (!Number.isFinite(levels) || levels < 0) {
        reply.code(400);
        return { error: "Payload invalide" };
      }
      await ensureInfiniteStats(db, userId);
      const current = await fetchInfiniteStats(db, userId);
      const bestLevels =
        levels > current.bestLevels ? levels : current.bestLevels;
      await db.execute(
        "UPDATE infinite_stats SET last_levels = ?, last_played_at = NOW(), best_levels = ? WHERE user_id = ?",
        [levels, bestLevels, userId],
      );
      await recordAudit(db, {
        userId,
        action: "infinite.record",
        meta: { levels },
        ip: request.ip,
        userAgent: request.headers["user-agent"]?.toString() ?? null,
        installationId: request.headers["x-installation-id"]?.toString() ?? null,
      });
      const stats = await fetchInfiniteStats(db, userId);
      return { stats };
    },
  );
};
