const express = require("express");
const { pool } = require("../db");
const { GAMEMODES, avatarUrl, normalizeGamemode } = require("../config");
const { ValidationError, validateRequest } = require("../middleware/validation");
const { sendError } = require("../utils/http");

const router = express.Router();
const PER_PAGE = 20;

const TIER_POINTS_SQL = `
CASE pr.tier
  WHEN 'HT1' THEN 60
  WHEN 'LT1' THEN 45
  WHEN 'HT2' THEN 30
  WHEN 'LT2' THEN 20
  WHEN 'HT3' THEN 10
  WHEN 'LT3' THEN 6
  WHEN 'HT4' THEN 4
  WHEN 'LT4' THEN 3
  WHEN 'HT5' THEN 2
  WHEN 'LT5' THEN 1
  ELSE 0
END`;

const GM_POINTS_SQL = `
CASE gm.tier
  WHEN 'HT1' THEN 60
  WHEN 'LT1' THEN 45
  WHEN 'HT2' THEN 30
  WHEN 'LT2' THEN 20
  WHEN 'HT3' THEN 10
  WHEN 'LT3' THEN 6
  WHEN 'HT4' THEN 4
  WHEN 'LT4' THEN 3
  WHEN 'HT5' THEN 2
  WHEN 'LT5' THEN 1
  ELSE 0
END`;

const schema = {
  params: {
    fields: {
      gamemode: {
        type: "string",
        required: true,
        minLength: 3,
        maxLength: 32,
        toLowerCase: true,
      },
    },
    allowUnknown: false,
  },
  query: {
    fields: {
      page: { type: "int", min: 1, max: 50000 },
      perPage: { type: "int", min: 1, max: 500 },
    },
    allowUnknown: false,
  },
};

function validateOrReply(req, res) {
  try {
    return validateRequest(req, schema);
  } catch (error) {
    if (error instanceof ValidationError) {
      sendError(res, 400, "VALIDATION_ERROR", error.message, error.details);
      return null;
    }

    sendError(res, 400, "VALIDATION_ERROR", "Invalid request payload.");
    return null;
  }
}

function buildDefaultTiers() {
  return Object.fromEntries(GAMEMODES.map((mode) => [mode, null]));
}

router.get("/rankings/:gamemode", async (req, res) => {
  const validated = validateOrReply(req, res);
  if (!validated) {
    return;
  }

  try {
    const gamemode = normalizeGamemode(validated.params.gamemode);
    const page = validated.query.page || 1;
    const perPage = validated.query.perPage || PER_PAGE;

    if (!GAMEMODES.includes(gamemode)) {
      return sendError(res, 400, "VALIDATION_ERROR", "Invalid gamemode");
    }

    const [countRows] = await pool.query(
      "SELECT COUNT(*) AS total FROM player_ranks WHERE gamemode = ?",
      [gamemode]
    );

    const total = Number(countRows[0]?.total || 0);
    const totalPages = Math.max(Math.ceil(total / perPage), 1);
    const start = (page - 1) * perPage;

    if (total === 0) {
      return res.json({
        gamemode,
        data: [],
        pagination: { page, perPage, total, totalPages },
      });
    }

    const [pageRows] = await pool.query(
      `SELECT
         p.id,
         p.username,
         p.region,
         gm.tier AS gm_tier,
         COALESCE(SUM(${TIER_POINTS_SQL}), 0) AS total_points,
         ${GM_POINTS_SQL} AS gamemode_points
       FROM players p
       JOIN player_ranks gm ON gm.player_id = p.id AND gm.gamemode = ?
       LEFT JOIN player_ranks pr ON pr.player_id = p.id
       GROUP BY p.id, p.username, p.region, gm.tier
       ORDER BY gamemode_points DESC, total_points DESC, p.username ASC
       LIMIT ? OFFSET ?`,
      [gamemode, perPage, start]
    );

    const playerIds = pageRows.map((row) => row.id);
    const [allTierRows] = playerIds.length
      ? await pool.query(
          "SELECT player_id, gamemode, tier FROM player_ranks WHERE player_id IN (?)",
          [playerIds]
        )
      : [[]];

    const byId = new Map();
    for (let i = 0; i < pageRows.length; i += 1) {
      const row = pageRows[i];
      byId.set(row.id, {
        id: row.id,
        username: row.username,
        region: row.region,
        avatarUrl: avatarUrl(row.username),
        tiers: buildDefaultTiers(),
        totalPoints: Number(row.total_points || 0),
        gamemodePoints: Number(row.gamemode_points || 0),
        position: start + i + 1,
      });
    }

    for (const row of allTierRows) {
      const target = byId.get(row.player_id);
      if (!target) {
        continue;
      }
      target.tiers[row.gamemode] = row.tier;
    }

    return res.json({
      gamemode,
      data: Array.from(byId.values()),
      pagination: {
        page,
        perPage,
        total,
        totalPages,
      },
    });
  } catch (error) {
    console.error("GET /api/rankings/:gamemode failed:", error?.message || error);
    return sendError(res, 500, "RANKINGS_FETCH_FAILED", "Failed to fetch rankings");
  }
});

module.exports = router;
