import { describe, it, expect, vi, beforeEach } from "vitest";
import { NextRequest } from "next/server";

const mockAdminSession = { user: { id: "admin-1", role: "admin" } };

vi.mock("@/lib/api-auth", () => ({
  requireAdmin: vi.fn(),
}));

const { mockUpdateChain, mockInsertValues } = vi.hoisted(() => {
  const mockUpdateChain = {
    set: vi.fn().mockReturnThis(),
    where: vi.fn().mockReturnThis(),
    returning: vi.fn().mockResolvedValue([{ id: "target-1", role: "moderator", tier: "free" }]),
  };
  return { mockUpdateChain, mockInsertValues: vi.fn().mockResolvedValue(undefined) };
});

vi.mock("@epicure/db", () => ({
  db: {
    update: vi.fn(() => mockUpdateChain),
    insert: vi.fn(() => ({ values: mockInsertValues })),
  },
  users: { id: "id", role: "role", tier: "tier" },
  auditLogs: {},
  eq: vi.fn((a, b) => ({ a, b, op: "eq" })),
}));

const { requireAdmin } = await import("@/lib/api-auth");
import { PATCH } from "../route";

function makeRequest(body: unknown) {
  return new NextRequest("http://localhost/api/v1/admin/users/target-1", {
    method: "PATCH",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
}

const ctx = { params: Promise.resolve({ id: "target-1" }) };

beforeEach(() => {
  vi.clearAllMocks();
  vi.mocked(requireAdmin).mockResolvedValue({ session: mockAdminSession as never, response: null });
  mockUpdateChain.returning.mockResolvedValue([{ id: "target-1", role: "moderator", tier: "free" }]);
});

describe("PATCH /api/v1/admin/users/[id]", () => {
  it("returns 403 when caller is not an admin", async () => {
    vi.mocked(requireAdmin).mockResolvedValue({
      session: null,
      response: new Response(JSON.stringify({ error: "Forbidden" }), { status: 403 }),
    } as never);

    const res = await PATCH(makeRequest({ role: "admin" }), ctx);
    expect(res.status).toBe(403);
  });

  it("returns 400 for an invalid role", async () => {
    const res = await PATCH(makeRequest({ role: "superuser" }), ctx);
    expect(res.status).toBe(400);
  });

  it("returns 400 for an invalid tier", async () => {
    const res = await PATCH(makeRequest({ tier: "enterprise" }), ctx);
    expect(res.status).toBe(400);
  });

  it("returns 404 when the target user does not exist", async () => {
    mockUpdateChain.returning.mockResolvedValue([]);
    const res = await PATCH(makeRequest({ role: "moderator" }), ctx);
    expect(res.status).toBe(404);
  });

  it("updates the user and writes an audit log", async () => {
    const res = await PATCH(makeRequest({ role: "moderator" }), ctx);
    expect(res.status).toBe(200);
    const body = await res.json() as { user: { id: string } };
    expect(body.user.id).toBe("target-1");
    expect(mockInsertValues).toHaveBeenCalledWith(
      expect.objectContaining({ action: "admin.user.update", targetId: "target-1" })
    );
  });
});
