import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { buildTestApp, loginAsAdmin, type TestApp } from "../test-server.js";

let testApp: TestApp;
let adminToken: string;

beforeAll(async () => {
  testApp = await buildTestApp();
  adminToken = await loginAsAdmin(testApp.app);
}, 30_000);

afterAll(async () => {
  await testApp.cleanup();
}, 10_000);

/* ------------------------------------------------------------------ */
/*  Helper                                                             */
/* ------------------------------------------------------------------ */

async function fetchAuditLog(
  qs = "",
): Promise<{ entries: any[]; total: number; page: number; limit: number }> {
  const res = await testApp.app.inject({
    method: "GET",
    url: `/api/v1/audit-log${qs ? `?${qs}` : ""}`,
    headers: { authorization: `Bearer ${adminToken}` },
  });
  expect(res.statusCode).toBe(200);
  return JSON.parse(res.body);
}

/* ------------------------------------------------------------------ */
/*  Event recording                                                    */
/* ------------------------------------------------------------------ */

describe("audit log event recording", () => {
  it("LOGIN_SUCCESS recorded after login", async () => {
    const body = await fetchAuditLog("action=LOGIN_SUCCESS");
    expect(body.entries.length).toBeGreaterThan(0);
    expect(body.entries[0].action).toBe("LOGIN_SUCCESS");
  });

  it("USER_CREATED recorded after register", async () => {
    await testApp.app.inject({
      method: "POST",
      url: "/api/auth/register",
      headers: { authorization: `Bearer ${adminToken}` },
      payload: {
        username: "audit_edge_user",
        password: "AuditEdge1",
        role: "user",
      },
    });

    const body = await fetchAuditLog("action=USER_CREATED");
    expect(body.entries.length).toBeGreaterThan(0);
    expect(body.entries.some((e: any) => e.action === "USER_CREATED")).toBe(true);
  });

  it("API_KEY_CREATED recorded after key creation", async () => {
    await testApp.app.inject({
      method: "POST",
      url: "/api/v1/api-keys",
      headers: { authorization: `Bearer ${adminToken}` },
      payload: { name: "audit-edge-key" },
    });

    const body = await fetchAuditLog("action=API_KEY_CREATED");
    expect(body.entries.length).toBeGreaterThan(0);
    expect(body.entries.some((e: any) => e.action === "API_KEY_CREATED")).toBe(true);
  });

  it("ROLE_CREATED recorded after role creation", async () => {
    await testApp.app.inject({
      method: "POST",
      url: "/api/v1/roles",
      headers: { authorization: `Bearer ${adminToken}` },
      payload: {
        name: "audit-edge-role",
        description: "role for audit edge test",
        permissions: ["tools:use"],
      },
    });

    const body = await fetchAuditLog("action=ROLE_CREATED");
    expect(body.entries.length).toBeGreaterThan(0);
    expect(body.entries.some((e: any) => e.action === "ROLE_CREATED")).toBe(true);
  });
});

/* ------------------------------------------------------------------ */
/*  Pagination edge cases                                              */
/* ------------------------------------------------------------------ */

describe("audit log pagination edge cases", () => {
  it("page=0 clamped to 1", async () => {
    const body = await fetchAuditLog("page=0");
    expect(body.page).toBe(1);
  });

  it("negative page clamped to 1", async () => {
    const body = await fetchAuditLog("page=-5");
    expect(body.page).toBe(1);
  });

  it("limit=500 clamped to 100", async () => {
    const body = await fetchAuditLog("limit=500");
    expect(body.limit).toBe(100);
  });

  it("limit=0 falls back to default (50)", async () => {
    const body = await fetchAuditLog("limit=0");
    expect(body.limit).toBe(50);
  });

  it("non-numeric values use defaults", async () => {
    const body = await fetchAuditLog("page=abc&limit=xyz");
    expect(body.page).toBe(1);
    expect(body.limit).toBe(50);
  });

  it("high page number returns empty entries", async () => {
    const body = await fetchAuditLog("page=99999");
    expect(body.entries).toEqual([]);
  });
});

/* ------------------------------------------------------------------ */
/*  Entry structure                                                    */
/* ------------------------------------------------------------------ */

describe("audit log entry structure", () => {
  it("each entry has id, actorUsername, action, createdAt (valid ISO date)", async () => {
    const body = await fetchAuditLog("limit=10");
    expect(body.entries.length).toBeGreaterThan(0);

    for (const entry of body.entries) {
      expect(entry).toHaveProperty("id");
      expect(typeof entry.id).toBe("string");

      expect(entry).toHaveProperty("actorUsername");
      expect(typeof entry.actorUsername).toBe("string");

      expect(entry).toHaveProperty("action");
      expect(typeof entry.action).toBe("string");

      expect(entry).toHaveProperty("createdAt");
      expect(typeof entry.createdAt).toBe("string");
      const parsed = new Date(entry.createdAt);
      expect(Number.isNaN(parsed.getTime())).toBe(false);
    }
  });
});
