import { describe, it, expect, vi, beforeEach } from "vitest";
import fastify from "fastify";
import { registerGameModeRoutes } from "../gamemode-routes.js";
import { makeCtx } from "./helpers.js";

vi.mock("../../server/db-schema.js", () => ({
  ensureArcadeStats: vi.fn(),
  ensureInfiniteStats: vi.fn(),
}));

vi.mock("../../server/data-fetchers.js", () => ({
  fetchArcadeStats: vi.fn(async () => ({
    bestScore: 100,
    bestLevels: 10,
    lastScore: 50,
    lastLevels: 5,
    lastPlayedAt: 0,
  })),
  fetchInfiniteStats: vi.fn(async () => ({
    bestLevels: 20,
    lastLevels: 8,
    lastPlayedAt: 0,
  })),
}));

vi.mock("../../server/admin-service.js", () => ({
  recordAudit: vi.fn(),
}));

const makeAuthCtx = (userId = 42) => {
  const executeFn = vi.fn().mockResolvedValue([{ affectedRows: 1 }]);

  return makeCtx({
    db: { execute: executeFn } as any,
    requireAuth: async (request: any) => {
      request.user = { sub: userId };
    },
  });
};

describe("gamemode-routes", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  // ────────────── ARCADE ──────────────

  describe("GET /arcade/:userId", () => {
    it("retourne les records arcade", async () => {
      const app = fastify();
      registerGameModeRoutes(app, makeAuthCtx(42));

      const res = await app.inject({ method: "GET", url: "/arcade/42" });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.stats).toBeDefined();
    });

    it("retourne 403 si userId ne correspond pas", async () => {
      const app = fastify();
      registerGameModeRoutes(app, makeAuthCtx(42));

      const res = await app.inject({ method: "GET", url: "/arcade/99" });
      expect(res.statusCode).toBe(403);
    });

    it("retourne 400 pour userId non numérique", async () => {
      const app = fastify();
      registerGameModeRoutes(app, makeAuthCtx(42));

      const res = await app.inject({ method: "GET", url: "/arcade/abc" });
      expect(res.statusCode).toBe(400);
    });
  });

  describe("POST /arcade/:userId/record", () => {
    it("enregistre un score arcade valide", async () => {
      const app = fastify();
      registerGameModeRoutes(app, makeAuthCtx(42));

      const res = await app.inject({
        method: "POST",
        url: "/arcade/42/record",
        payload: { score: 250, levels: 8 },
      });
      expect(res.statusCode).toBe(200);
    });

    it("refuse un score négatif", async () => {
      const app = fastify();
      registerGameModeRoutes(app, makeAuthCtx(42));

      const res = await app.inject({
        method: "POST",
        url: "/arcade/42/record",
        payload: { score: -10, levels: 5 },
      });
      expect(res.statusCode).toBe(400);
    });

    it("refuse sans score", async () => {
      const app = fastify();
      registerGameModeRoutes(app, makeAuthCtx(42));

      const res = await app.inject({
        method: "POST",
        url: "/arcade/42/record",
        payload: { levels: 5 },
      });
      expect(res.statusCode).toBe(400);
    });

    it("refuse si userId ne correspond pas", async () => {
      const app = fastify();
      registerGameModeRoutes(app, makeAuthCtx(42));

      const res = await app.inject({
        method: "POST",
        url: "/arcade/99/record",
        payload: { score: 100, levels: 5 },
      });
      expect(res.statusCode).toBe(403);
    });
  });

  // ────────────── INFINITE ──────────────

  describe("GET /infinite/:userId", () => {
    it("retourne les records infini", async () => {
      const app = fastify();
      registerGameModeRoutes(app, makeAuthCtx(42));

      const res = await app.inject({ method: "GET", url: "/infinite/42" });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.stats).toBeDefined();
    });

    it("retourne 403 si userId ne correspond pas", async () => {
      const app = fastify();
      registerGameModeRoutes(app, makeAuthCtx(42));

      const res = await app.inject({ method: "GET", url: "/infinite/99" });
      expect(res.statusCode).toBe(403);
    });
  });

  describe("POST /infinite/:userId/record", () => {
    it("enregistre un record infini valide", async () => {
      const app = fastify();
      registerGameModeRoutes(app, makeAuthCtx(42));

      const res = await app.inject({
        method: "POST",
        url: "/infinite/42/record",
        payload: { levels: 25 },
      });
      expect(res.statusCode).toBe(200);
    });

    it("refuse sans levels", async () => {
      const app = fastify();
      registerGameModeRoutes(app, makeAuthCtx(42));

      const res = await app.inject({
        method: "POST",
        url: "/infinite/42/record",
        payload: {},
      });
      expect(res.statusCode).toBe(400);
    });

    it("refuse un levels négatif", async () => {
      const app = fastify();
      registerGameModeRoutes(app, makeAuthCtx(42));

      const res = await app.inject({
        method: "POST",
        url: "/infinite/42/record",
        payload: { levels: -1 },
      });
      expect(res.statusCode).toBe(400);
    });

    it("refuse si userId ne correspond pas", async () => {
      const app = fastify();
      registerGameModeRoutes(app, makeAuthCtx(42));

      const res = await app.inject({
        method: "POST",
        url: "/infinite/99/record",
        payload: { levels: 10 },
      });
      expect(res.statusCode).toBe(403);
    });
  });
});
