import { beforeEach, describe, expect, it, vi } from "vitest";
import type { TrpcContext } from "./_core/context";

const listManagedUsers = vi.fn();
const createManagedUser = vi.fn();
const setManagedUserActive = vi.fn();
const updateManagedUserPassword = vi.fn();

vi.mock("./db", async (importOriginal) => ({
  ...(await importOriginal<typeof import("./db")>()),
  listManagedUsers,
  createManagedUser,
  setManagedUserActive,
  updateManagedUserPassword,
}));

const { appRouter } = await import("./routers");

function contextFor(id: number, role: "admin" | "editor"): TrpcContext {
  const now = new Date();
  return {
    user: { id, openId: `local-user:${id}`, name: `User ${id}`, email: null, loginMethod: "local", role, isActive: true, createdAt: now, updatedAt: now, lastSignedIn: now },
    req: { protocol: "https", headers: {} } as TrpcContext["req"],
    res: { clearCookie: () => undefined } as TrpcContext["res"],
  };
}

describe("user-management authorization", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    listManagedUsers.mockResolvedValue([]);
    createManagedUser.mockResolvedValue(88);
    setManagedUserActive.mockResolvedValue(true);
    updateManagedUserPassword.mockResolvedValue(true);
  });

  it("rejects every user-management operation for an editor", async () => {
    const caller = appRouter.createCaller(contextFor(77, "editor"));

    await expect(caller.userManagement.list()).rejects.toMatchObject({ code: "FORBIDDEN" });
    await expect(caller.userManagement.create({ username: "student77", name: "Student 77", password: "safe-pass-77", role: "editor" })).rejects.toMatchObject({ code: "FORBIDDEN" });
    await expect(caller.userManagement.setActive({ id: 88, isActive: false })).rejects.toMatchObject({ code: "FORBIDDEN" });
    await expect(caller.userManagement.resetPassword({ id: 88, password: "reset-pass-77" })).rejects.toMatchObject({ code: "FORBIDDEN" });

    expect(listManagedUsers).not.toHaveBeenCalled();
    expect(createManagedUser).not.toHaveBeenCalled();
    expect(setManagedUserActive).not.toHaveBeenCalled();
    expect(updateManagedUserPassword).not.toHaveBeenCalled();
  });

  it("allows the administrator to manage ordinary accounts but not to disable their own account", async () => {
    const caller = appRouter.createCaller(contextFor(1, "admin"));

    await expect(caller.userManagement.list()).resolves.toEqual([]);
    await expect(caller.userManagement.create({ username: "student88", name: "Student 88", password: "safe-pass-88", role: "editor" })).resolves.toEqual({ id: 88 });
    await expect(caller.userManagement.create({ username: "manager89", name: "Manager 89", password: "safe-pass-89", role: "admin" })).resolves.toEqual({ id: 88 });
    await expect(caller.userManagement.setActive({ id: 88, isActive: false })).resolves.toEqual({ success: true });
    await expect(caller.userManagement.resetPassword({ id: 88, password: "reset-pass-88" })).resolves.toEqual({ success: true });
    await expect(caller.userManagement.setActive({ id: 1, isActive: false })).rejects.toMatchObject({ code: "BAD_REQUEST" });

    expect(listManagedUsers).toHaveBeenCalledOnce();
    expect(createManagedUser).toHaveBeenCalledWith(expect.objectContaining({ username: "student88", name: "Student 88", role: "editor" }));
    expect(createManagedUser).toHaveBeenCalledWith(expect.objectContaining({ username: "manager89", name: "Manager 89", role: "admin" }));
    expect(setManagedUserActive).toHaveBeenCalledWith(88, false);
    expect(updateManagedUserPassword).toHaveBeenCalledWith(88, expect.any(String));
  });
});
