import { eq } from "drizzle-orm";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { db, schema } from "../../../apps/api/src/db/index.js";
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);

describe("custom roles", () => {
  let customRoleId: string;

  it("lists built-in roles", async () => {
    const res = await testApp.app.inject({
      method: "GET",
      url: "/api/v1/roles",
      headers: { authorization: `Bearer ${adminToken}` },
    });
    expect(res.statusCode).toBe(200);
    const body = JSON.parse(res.body);
    expect(body.roles.length).toBeGreaterThanOrEqual(3);
    expect(body.roles.some((r: any) => r.name === "admin" && r.isBuiltin)).toBe(true);
    expect(body.roles.some((r: any) => r.name === "editor" && r.isBuiltin)).toBe(true);
    expect(body.roles.some((r: any) => r.name === "user" && r.isBuiltin)).toBe(true);
  });

  it("creates a custom role", async () => {
    const res = await testApp.app.inject({
      method: "POST",
      url: "/api/v1/roles",
      headers: { authorization: `Bearer ${adminToken}` },
      payload: {
        name: "reviewer",
        description: "Can view all files and pipelines",
        permissions: ["files:all", "pipelines:all", "settings:read"],
      },
    });
    expect(res.statusCode).toBe(201);
    const body = JSON.parse(res.body);
    expect(body.name).toBe("reviewer");
    expect(body.permissions).toEqual(["files:all", "pipelines:all", "settings:read"]);
    customRoleId = body.id;
  });

  it("cannot create duplicate role name", async () => {
    const res = await testApp.app.inject({
      method: "POST",
      url: "/api/v1/roles",
      headers: { authorization: `Bearer ${adminToken}` },
      payload: { name: "admin", permissions: ["tools:use"] },
    });
    expect(res.statusCode).toBe(409);
  });

  it("can assign custom role to user", async () => {
    await testApp.app.inject({
      method: "POST",
      url: "/api/auth/register",
      headers: { authorization: `Bearer ${adminToken}` },
      payload: { username: "customroleuser", password: "CustomRole1", role: "reviewer" },
    });
    await db
      .update(schema.users)
      .set({ mustChangePassword: false })
      .where(eq(schema.users.username, "customroleuser"));

    const loginRes = await testApp.app.inject({
      method: "POST",
      url: "/api/auth/login",
      payload: { username: "customroleuser", password: "CustomRole1" },
    });
    const body = JSON.parse(loginRes.body);
    expect(body.user.role).toBe("reviewer");
    expect(body.user.permissions).toContain("files:all");
    expect(body.user.permissions).not.toContain("tools:use");
  });

  it("updates custom role permissions", async () => {
    const res = await testApp.app.inject({
      method: "PUT",
      url: `/api/v1/roles/${customRoleId}`,
      headers: { authorization: `Bearer ${adminToken}` },
      payload: { permissions: ["files:all", "pipelines:all", "settings:read", "tools:use"] },
    });
    expect(res.statusCode).toBe(200);
  });

  it("cannot modify built-in roles", async () => {
    const listRes = await testApp.app.inject({
      method: "GET",
      url: "/api/v1/roles",
      headers: { authorization: `Bearer ${adminToken}` },
    });
    const builtinRole = JSON.parse(listRes.body).roles.find((r: any) => r.name === "admin");
    const res = await testApp.app.inject({
      method: "PUT",
      url: `/api/v1/roles/${builtinRole.id}`,
      headers: { authorization: `Bearer ${adminToken}` },
      payload: { permissions: ["tools:use"] },
    });
    expect(res.statusCode).toBe(400);
  });

  it("cannot delete built-in roles", async () => {
    const listRes = await testApp.app.inject({
      method: "GET",
      url: "/api/v1/roles",
      headers: { authorization: `Bearer ${adminToken}` },
    });
    const builtinRole = JSON.parse(listRes.body).roles.find((r: any) => r.name === "admin");
    const res = await testApp.app.inject({
      method: "DELETE",
      url: `/api/v1/roles/${builtinRole.id}`,
      headers: { authorization: `Bearer ${adminToken}` },
    });
    expect(res.statusCode).toBe(400);
  });

  it("deleting custom role reassigns users to user", async () => {
    const res = await testApp.app.inject({
      method: "DELETE",
      url: `/api/v1/roles/${customRoleId}`,
      headers: { authorization: `Bearer ${adminToken}` },
    });
    expect(res.statusCode).toBe(200);

    const loginRes = await testApp.app.inject({
      method: "POST",
      url: "/api/auth/login",
      payload: { username: "customroleuser", password: "CustomRole1" },
    });
    const body = JSON.parse(loginRes.body);
    expect(body.user.role).toBe("user");
  });

  it("requires users:manage to create roles", async () => {
    const loginRes = await testApp.app.inject({
      method: "POST",
      url: "/api/auth/login",
      payload: { username: "customroleuser", password: "CustomRole1" },
    });
    const userToken = JSON.parse(loginRes.body).token;

    const res = await testApp.app.inject({
      method: "POST",
      url: "/api/v1/roles",
      headers: { authorization: `Bearer ${userToken}` },
      payload: { name: "hacker", permissions: ["users:manage"] },
    });
    expect(res.statusCode).toBe(403);
  });
});
