import { describe, it, expect, vi, beforeEach } from "vitest";
import fastify from "fastify";
import { registerAdminRoutes } from "../admin-routes.js";
import { makeCtx } from "./helpers.js";

vi.mock("../../server/admin-service.js", () => ({
  fetchAdminOverview: vi.fn(async () => ({
    users: 100,
    wallets: 90,
    progressRows: 500,
    auditTotal: 2000,
    auditLast24h: 50,
    activeSessions: 5,
  })),
}));

vi.mock("../../server/data-fetchers.js", () => ({
  fetchAppConfig: vi.fn(async () => ({
    musicSrc: null,
    publicTheme: "ocean",
    publicThemeForce: false,
    ballSkin: null,
    challengePackEnabled: true,
  })),
  fetchAdminConfig: vi.fn(async () => ({
    musicSrc: null,
    publicTheme: "ocean",
    publicThemeForce: false,
    ballSkin: null,
    challengePackEnabled: true,
    launchDate: null,
    maintenanceMode: false,
    maintenanceMessage: "Mise à jour en cours. Veuillez patienter...",
  })),
  setAppConfigValue: vi.fn(),
}));

const makeAdminCtx = () => {
  const executeFn = vi
    .fn()
    .mockResolvedValueOnce([
      [
        { config_key: "maintenance_mode" },
        { config_key: "maintenance_message" },
      ],
    ])
    .mockResolvedValue([{ affectedRows: 1 }]);

  return makeCtx({
    db: { execute: executeFn } as any,
    requireAdmin: async (request: any) => {
      request.user = { sub: 1, isAdmin: true };
    },
  });
};

const makeNonAdminCtx = () => {
  return makeCtx({
    db: { execute: vi.fn().mockResolvedValue([[]]) } as any,
    requireAdmin: async (_req: any, reply: any) => {
      reply.code(403);
      throw new Error("Admin required");
    },
  });
};

describe("admin-routes", () => {
  beforeEach(() => vi.clearAllMocks());

  describe("GET /admin/overview", () => {
    it("retourne l'overview admin", async () => {
      const app = fastify();
      await registerAdminRoutes(app, makeAdminCtx());

      const res = await app.inject({ method: "GET", url: "/admin/overview" });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.users).toBe(100);
      expect(body.activeSessions).toBe(5);
    });
  });

  describe("GET /config/public", () => {
    it("retourne la configuration publique (pas besoin d'admin)", async () => {
      const app = fastify();
      await registerAdminRoutes(app, makeAdminCtx());

      const res = await app.inject({ method: "GET", url: "/config/public" });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.publicTheme).toBe("ocean");
    });
  });

  describe("GET /admin/config", () => {
    it("retourne la config admin", async () => {
      const app = fastify();
      await registerAdminRoutes(app, makeAdminCtx());

      const res = await app.inject({ method: "GET", url: "/admin/config" });
      expect(res.statusCode).toBe(200);
    });
  });

  describe("PUT /admin/config", () => {
    it("met à jour la configuration admin", async () => {
      const app = fastify();
      await registerAdminRoutes(app, makeAdminCtx());

      const res = await app.inject({
        method: "PUT",
        url: "/admin/config",
        payload: {
          publicTheme: "neon",
          challengePackEnabled: true,
        },
      });
      expect(res.statusCode).toBe(200);
    });

    it("supprime silencieusement les propriétés additionnelles (Fastify removeAdditional)", async () => {
      const app = fastify();
      await registerAdminRoutes(app, makeAdminCtx());

      const res = await app.inject({
        method: "PUT",
        url: "/admin/config",
        payload: { unknownProperty: "hack" },
      });
      // Fastify avec additionalProperties: false retire les props inconnues → body vide valide → 200
      expect(res.statusCode).toBe(200);
    });
  });

  describe("POST /admin/init-maintenance-config", () => {
    it("retourne already_exists si la config existe", async () => {
      const app = fastify();
      await registerAdminRoutes(app, makeAdminCtx());

      const res = await app.inject({
        method: "POST",
        url: "/admin/init-maintenance-config",
      });
      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.body);
      expect(body.status).toBe("already_exists");
    });
  });

  describe("GET /admin/dashboard", () => {
    it("retourne un dashboard HTML", async () => {
      const app = fastify();
      await registerAdminRoutes(app, makeAdminCtx());

      const res = await app.inject({ method: "GET", url: "/admin/dashboard" });
      expect(res.statusCode).toBe(200);
      expect(res.headers["content-type"]).toContain("text/html");
      expect(res.body).toContain("RollerLogic Admin");
    });
  });
});
