import { describe, it, expect, beforeEach, vi } from "vitest";
import { PATCH, DELETE } from "@/app/api/team/members/route";
import { db } from "@/lib/db";

vi.mock("@/lib/db", () => ({
  db: {
    user: { findUnique: vi.fn(), findFirst: vi.fn(), update: vi.fn(), count: vi.fn() },
    team: { findUnique: vi.fn() },
  },
}));

vi.mock("@/lib/auth", () => ({ auth: vi.fn() }));
vi.mock("@/lib/logger", () => ({
  logger: { info: vi.fn(), error: vi.fn(), warn: vi.fn() },
}));

import { auth } from "@/lib/auth";

function makeReq(method: string, body: unknown) {
  return new Request("http://localhost/api/team/members", {
    method,
    body: JSON.stringify(body),
  });
}

describe("/api/team/members - PATCH (update role)", () => {
  beforeEach(() => vi.clearAllMocks());

  it("returns 401 when not authenticated", async () => {
    vi.mocked(auth).mockResolvedValue(null as any);
    const res = await PATCH(makeReq("PATCH", { memberId: "u2", role: "viewer" }));
    expect(res.status).toBe(401);
  });

  it("returns 400 for invalid role", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "u1" } } as any);
    const res = await PATCH(makeReq("PATCH", { memberId: "u2", role: "superadmin" }));
    expect(res.status).toBe(400);
  });

  it("returns 403 when user is not admin", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "u1" } } as any);
    vi.mocked(db.user.findUnique).mockResolvedValue({ teamId: "t1", teamRole: "developer" } as any);

    const res = await PATCH(makeReq("PATCH", { memberId: "u2", role: "viewer" }));
    expect(res.status).toBe(403);
  });

  it("prevents removing last admin", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "u1" } } as any);
    vi.mocked(db.user.findUnique).mockResolvedValue({ teamId: "t1", teamRole: "admin" } as any);
    vi.mocked(db.user.count).mockResolvedValue(1);

    const res = await PATCH(makeReq("PATCH", { memberId: "u1", role: "developer" }));
    expect(res.status).toBe(409);
    const data = await res.json();
    expect(data.error).toContain("last admin");
  });

  it("updates role successfully", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "u1" } } as any);
    vi.mocked(db.user.findUnique).mockResolvedValue({ teamId: "t1", teamRole: "admin" } as any);
    vi.mocked(db.user.findFirst).mockResolvedValue({ id: "u2", teamId: "t1" } as any);
    vi.mocked(db.user.update).mockResolvedValue({} as any);

    const res = await PATCH(makeReq("PATCH", { memberId: "u2", role: "viewer" }));
    const data = await res.json();
    expect(res.status).toBe(200);
    expect(data.role).toBe("viewer");
  });
});

describe("/api/team/members - DELETE (remove member)", () => {
  beforeEach(() => vi.clearAllMocks());

  it("returns 401 when not authenticated", async () => {
    vi.mocked(auth).mockResolvedValue(null as any);
    const res = await DELETE(makeReq("DELETE", { memberId: "u2" }));
    expect(res.status).toBe(401);
  });

  it("returns 403 when non-admin tries to remove someone else", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "u1" } } as any);
    vi.mocked(db.user.findUnique).mockResolvedValue({ teamId: "t1", teamRole: "developer" } as any);

    const res = await DELETE(makeReq("DELETE", { memberId: "u2" }));
    expect(res.status).toBe(403);
  });

  it("returns 409 when trying to remove team owner", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "u1" } } as any);
    vi.mocked(db.user.findUnique).mockResolvedValue({ teamId: "t1", teamRole: "admin" } as any);
    vi.mocked(db.user.findFirst).mockResolvedValue({ id: "u2", teamId: "t1" } as any);
    vi.mocked(db.team.findUnique).mockResolvedValue({ ownerId: "u2" } as any);

    const res = await DELETE(makeReq("DELETE", { memberId: "u2" }));
    expect(res.status).toBe(409);
    const data = await res.json();
    expect(data.error).toContain("owner");
  });

  it("removes member successfully", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "u1" } } as any);
    vi.mocked(db.user.findUnique).mockResolvedValue({ teamId: "t1", teamRole: "admin" } as any);
    vi.mocked(db.user.findFirst).mockResolvedValue({ id: "u2", teamId: "t1" } as any);
    vi.mocked(db.team.findUnique).mockResolvedValue({ ownerId: "u1" } as any);
    vi.mocked(db.user.update).mockResolvedValue({} as any);

    const res = await DELETE(makeReq("DELETE", { memberId: "u2" }));
    expect(res.status).toBe(200);
    expect(db.user.update).toHaveBeenCalledWith(
      expect.objectContaining({
        where: { id: "u2" },
        data: { teamId: null, teamRole: null },
      }),
    );
  });

  it("allows member to remove themselves", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "u2" } } as any);
    vi.mocked(db.user.findUnique).mockResolvedValue({ teamId: "t1", teamRole: "developer" } as any);
    vi.mocked(db.user.findFirst).mockResolvedValue({ id: "u2", teamId: "t1" } as any);
    vi.mocked(db.team.findUnique).mockResolvedValue({ ownerId: "u1" } as any);
    vi.mocked(db.user.update).mockResolvedValue({} as any);

    const res = await DELETE(makeReq("DELETE", { memberId: "u2" }));
    expect(res.status).toBe(200);
  });
});
