/**
 * Routes du leaderboard
 * /leaderboard/*
 */
import type { FastifyInstance } from "fastify";
import type { RouteContext } from "./types.js";
import { LEVELS_PER_DIFFICULTY as CATALOG_LEVELS } from "../level-generator.js";
import { DIFFICULTIES, type DifficultyKey } from "../server/constants.js";

const difficultySchema = {
  type: "string",
  enum: DIFFICULTIES,
} as const;

export const registerLeaderboardRoutes = async (
  app: FastifyInstance,
  ctx: RouteContext,
): Promise<void> => {
  const { db, requireAuth } = ctx;

  // GET /leaderboard/global - Top 100 global
  app.get(
    "/leaderboard/global",
    { preHandler: requireAuth },
    async (_request, reply) => {
      const [rows] = await db.execute(`
      SELECT 
        le.user_id,
        u.display_name as username,
        u.avatar,
        SUM(le.score) as score,
        COUNT(*) as levels_completed,
        MIN(le.time_ms) as time_ms,
        MIN(le.moves) as moves,
        MAX(le.created_at) as last_played_at
      FROM leaderboard_entries le
      JOIN users u ON le.user_id = u.id
      GROUP BY le.user_id, u.display_name, u.avatar
      ORDER BY score DESC
      LIMIT 100
    `);
      return reply.send({ leaderboard: rows });
    },
  );

  // GET /leaderboard/:difficulty - Top 100 par difficulté
  app.get(
    "/leaderboard/:difficulty",
    {
      preHandler: requireAuth,
      schema: {
        params: {
          type: "object",
          required: ["difficulty"],
          properties: { difficulty: difficultySchema },
        },
      },
    },
    async (request, reply) => {
      const { difficulty } = request.params as { difficulty: DifficultyKey };
      if (!DIFFICULTIES.includes(difficulty)) {
        reply.code(400);
        return { error: "Difficulte invalide" };
      }
      const [rows] = await db.execute(
        `
        SELECT 
          le.user_id,
          u.display_name as username,
          u.avatar,
          SUM(le.score) as score,
          COUNT(*) as levels_completed,
          MIN(le.time_ms) as time_ms,
          MIN(le.moves) as moves,
          MAX(le.created_at) as last_played_at
        FROM leaderboard_entries le
        JOIN users u ON le.user_id = u.id
        WHERE le.difficulty = ?
        GROUP BY le.user_id, u.display_name, u.avatar
        ORDER BY score DESC
        LIMIT 100
      `,
        [difficulty],
      );
      return reply.send({ leaderboard: rows, difficulty });
    },
  );

  // GET /leaderboard/:difficulty/:level - Top 50 d'un niveau
  app.get(
    "/leaderboard/:difficulty/:levelIndex",
    {
      preHandler: requireAuth,
      schema: {
        params: {
          type: "object",
          required: ["difficulty", "levelIndex"],
          properties: {
            difficulty: difficultySchema,
            levelIndex: {
              type: "integer",
              minimum: 0,
              maximum: CATALOG_LEVELS - 1,
            },
          },
        },
      },
    },
    async (request, reply) => {
      const { difficulty, levelIndex } = request.params as {
        difficulty: DifficultyKey;
        levelIndex: number;
      };
      if (!DIFFICULTIES.includes(difficulty)) {
        reply.code(400);
        return { error: "Difficulte invalide" };
      }
      const [rows] = await db.execute(
        `
        SELECT 
          le.user_id,
          u.display_name as username,
          u.avatar,
          le.score,
          le.time_ms,
          le.moves,
          le.created_at
        FROM leaderboard_entries le
        JOIN users u ON le.user_id = u.id
        WHERE le.difficulty = ? AND le.level_index = ?
        ORDER BY le.score DESC
        LIMIT 50
      `,
        [difficulty, levelIndex],
      );
      return reply.send({ leaderboard: rows, difficulty, levelIndex });
    },
  );

  // GET /leaderboard/rank/:userId - Rang d'un joueur
  app.get(
    "/leaderboard/rank/:userId",
    { preHandler: requireAuth },
    async (request, reply) => {
      const { userId } = request.params as { userId: string };
      const uid = Number(userId);

      const [globalRankRows] = await db.execute(
        `
        SELECT COUNT(*) + 1 as user_rank FROM (
          SELECT user_id, SUM(score) as total FROM leaderboard_entries GROUP BY user_id
        ) rankings WHERE total > (
          SELECT COALESCE(SUM(score), 0) FROM leaderboard_entries WHERE user_id = ?
        )
      `,
        [uid],
      );

      const [totalPlayersRows] = await db.execute(
        `SELECT COUNT(DISTINCT user_id) as total FROM leaderboard_entries`,
      );

      const [userScoreRows] = await db.execute(
        `SELECT SUM(score) as total_score, COUNT(*) as levels FROM leaderboard_entries WHERE user_id = ?`,
        [uid],
      );

      const globalRank =
        Array.isArray(globalRankRows) && globalRankRows[0]
          ? (globalRankRows[0] as { user_rank: number }).user_rank
          : 0;
      const totalPlayers =
        Array.isArray(totalPlayersRows) && totalPlayersRows[0]
          ? (totalPlayersRows[0] as { total: number }).total
          : 0;
      const userScore =
        Array.isArray(userScoreRows) && userScoreRows[0]
          ? (userScoreRows[0] as { total_score: number; levels: number })
          : { total_score: 0, levels: 0 };

      return reply.send({
        global_rank: globalRank,
        total_players: totalPlayers,
        total_score: userScore.total_score || 0,
        entries_count: userScore.levels || 0,
      });
    },
  );

  // POST /leaderboard/submit - Soumettre un score
  app.post(
    "/leaderboard/submit",
    {
      preHandler: requireAuth,
      config: { rateLimit: { max: 60, timeWindow: "1 minute" } },
      schema: {
        body: {
          type: "object",
          required: ["difficulty", "levelIndex", "timeMs", "moves"],
          properties: {
            difficulty: difficultySchema,
            levelIndex: {
              type: "integer",
              minimum: 0,
              maximum: CATALOG_LEVELS - 1,
            },
            timeMs: { type: "integer", minimum: 2000 },
            moves: { type: "integer", minimum: 1 },
          },
        },
      },
    },
    async (request, reply) => {
      const userId = request.user.sub;
      const { difficulty, levelIndex, timeMs, moves } = request.body as {
        difficulty: DifficultyKey;
        levelIndex: number;
        timeMs: number;
        moves: number;
      };

      await db.execute(
        `
        INSERT INTO leaderboard_entries (user_id, difficulty, level_index, time_ms, moves)
        VALUES (?, ?, ?, ?, ?)
        ON DUPLICATE KEY UPDATE 
          time_ms = IF(10000 - VALUES(time_ms) - VALUES(moves) * 10 > score, VALUES(time_ms), time_ms),
          moves = IF(10000 - VALUES(time_ms) - VALUES(moves) * 10 > score, VALUES(moves), moves)
      `,
        [userId, difficulty, levelIndex, timeMs, moves],
      );

      const [rankRows] = await db.execute(
        `
        SELECT COUNT(*) + 1 as user_rank FROM leaderboard_entries 
        WHERE difficulty = ? AND level_index = ? AND score > (
          SELECT score FROM leaderboard_entries WHERE user_id = ? AND difficulty = ? AND level_index = ?
        )
      `,
        [difficulty, levelIndex, userId, difficulty, levelIndex],
      );

      const rank =
        Array.isArray(rankRows) && rankRows[0]
          ? (rankRows[0] as { user_rank: number }).user_rank
          : 1;

      return reply.send({ success: true, rank });
    },
  );
};
