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

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

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

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

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

describe("/api/team/invite - POST", () => {
  beforeEach(() => vi.clearAllMocks());

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

  it("returns 400 when email is empty", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "u1" } } as any);
    const res = await POST(makeReq({ email: "" }));
    expect(res.status).toBe(400);
  });

  it("returns 400 for invalid role", async () => {
    vi.mocked(auth).mockResolvedValue({ user: { id: "u1" } } as any);
    const res = await POST(makeReq({ email: "test@test.com", role: "superuser" }));
    expect(res.status).toBe(400);
  });

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

    const res = await POST(makeReq({ email: "test@test.com" }));
    expect(res.status).toBe(403);
  });

  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 POST(makeReq({ email: "test@test.com" }));
    expect(res.status).toBe(403);
  });

  it("returns 409 when team is at capacity", 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.team.findUnique).mockResolvedValue({
      id: "t1",
      maxSeats: 5,
      _count: { members: 5 },
    } as any);

    const res = await POST(makeReq({ email: "test@test.com" }));
    expect(res.status).toBe(409);
    const data = await res.json();
    expect(data.error).toContain("capacity");
  });

  it("returns 409 when user is already a member", 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.team.findUnique).mockResolvedValue({ id: "t1", maxSeats: 15, _count: { members: 2 } } as any);
    vi.mocked(db.user.findFirst).mockResolvedValue({ id: "u2" } as any);

    const res = await POST(makeReq({ email: "existing@test.com" }));
    expect(res.status).toBe(409);
  });

  it("returns 409 when invite already pending", 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.team.findUnique).mockResolvedValue({ id: "t1", maxSeats: 15, _count: { members: 2 } } as any);
    vi.mocked(db.user.findFirst).mockResolvedValue(null as any);
    vi.mocked(db.teamInvite.findFirst).mockResolvedValue({ id: "inv1" } as any);

    const res = await POST(makeReq({ email: "pending@test.com" }));
    expect(res.status).toBe(409);
  });

  it("creates invite 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.team.findUnique).mockResolvedValue({ id: "t1", maxSeats: 15, _count: { members: 2 } } as any);
    vi.mocked(db.user.findFirst).mockResolvedValue(null as any);
    vi.mocked(db.teamInvite.findFirst).mockResolvedValue(null as any);
    vi.mocked(db.teamInvite.create).mockResolvedValue({
      id: "inv1",
      email: "new@test.com",
      role: "developer",
      expiresAt: new Date("2026-03-01"),
    } as any);

    const res = await POST(makeReq({ email: "new@test.com", role: "developer" }));
    const data = await res.json();
    expect(res.status).toBe(201);
    expect(data.invite.email).toBe("new@test.com");
    expect(data.invite.role).toBe("developer");
  });
});

describe("/api/team/invite - DELETE (revoke)", () => {
  beforeEach(() => vi.clearAllMocks());

  it("returns 401 when not authenticated", async () => {
    vi.mocked(auth).mockResolvedValue(null as any);
    const req = new Request("http://localhost/api/team/invite", {
      method: "DELETE",
      body: JSON.stringify({ inviteId: "inv1" }),
    });
    const res = await DELETE(req);
    expect(res.status).toBe(401);
  });

  it("returns 403 when 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 req = new Request("http://localhost/api/team/invite", {
      method: "DELETE",
      body: JSON.stringify({ inviteId: "inv1" }),
    });
    const res = await DELETE(req);
    expect(res.status).toBe(403);
  });

  it("revokes invite 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.teamInvite.delete).mockResolvedValue({} as any);

    const req = new Request("http://localhost/api/team/invite", {
      method: "DELETE",
      body: JSON.stringify({ inviteId: "inv1" }),
    });
    const res = await DELETE(req);
    expect(res.status).toBe(200);
  });
});
