import { beforeEach, describe, expect, it, vi } from "vitest";
import { GET as getExpenseTypes, POST as createExpenseType } from "../expense-types/route";
import { PUT as updateExpenseType, DELETE as deleteExpenseType } from "../expense-types/[id]/route";
import { GET as getPaymentMethods, POST as createPaymentMethod } from "../payment-methods/route";
import { PUT as updatePaymentMethod, DELETE as deletePaymentMethod } from "../payment-methods/[id]/route";
import { getServerSession } from "next-auth";
import { prisma } from "@/lib/prisma";

vi.mock("next-auth", () => ({
  getServerSession: vi.fn(),
}));

vi.mock("../../../auth/[...nextauth]/route", () => ({
  authOptions: {},
}));

vi.mock("@/lib/prisma", () => ({
  prisma: {
    expenseType: {
      findMany: vi.fn(),
      findUnique: vi.fn(),
      create: vi.fn(),
      update: vi.fn(),
      delete: vi.fn(),
      count: vi.fn(),
    },
    paymentMethod: {
      findMany: vi.fn(),
      findUnique: vi.fn(),
      create: vi.fn(),
      update: vi.fn(),
      delete: vi.fn(),
      count: vi.fn(),
    },
    category: {
      count: vi.fn(),
    },
  },
}));

describe("Category config API", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("GET expense-types retorna 401 sin sesión", async () => {
    vi.mocked(getServerSession).mockResolvedValue(null);
    const response = await getExpenseTypes();
    expect(response.status).toBe(401);
  });

  it("POST expense-types crea registro para ADMIN", async () => {
    vi.mocked(getServerSession).mockResolvedValue({ user: { role: "ADMIN" } } as any);
    vi.mocked(prisma.expenseType.findUnique).mockResolvedValue(null);
    vi.mocked(prisma.expenseType.create).mockResolvedValue({ id: "e1", name: "Viáticos" } as any);

    const response = await createExpenseType({
      json: vi.fn().mockResolvedValue({ name: "  Viáticos  " }),
    } as any);
    const data = await response.json();

    expect(response.status).toBe(201);
    expect(data.name).toBe("Viáticos");
    expect(prisma.expenseType.create).toHaveBeenCalledWith({ data: { name: "Viáticos" } });
  });

  it("POST payment-methods retorna 403 para USER", async () => {
    vi.mocked(getServerSession).mockResolvedValue({ user: { role: "USER" } } as any);
    const response = await createPaymentMethod({
      json: vi.fn().mockResolvedValue({ name: "Transferencia" }),
    } as any);
    expect(response.status).toBe(403);
  });

  it("PUT payment-methods actualiza nombre", async () => {
    vi.mocked(getServerSession).mockResolvedValue({ user: { role: "ADMIN" } } as any);
    vi.mocked(prisma.paymentMethod.findUnique).mockResolvedValue(null);
    vi.mocked(prisma.paymentMethod.update).mockResolvedValue({ id: "p1", name: "Tarjeta" } as any);

    const response = await updatePaymentMethod(
      { json: vi.fn().mockResolvedValue({ name: "Tarjeta" }) } as any,
      { params: Promise.resolve({ id: "p1" }) }
    );

    expect(response.status).toBe(200);
    expect(prisma.paymentMethod.update).toHaveBeenCalledWith({
      where: { id: "p1" },
      data: { name: "Tarjeta" },
    });
  });

  it("PUT expense-types retorna 400 por duplicado", async () => {
    vi.mocked(getServerSession).mockResolvedValue({ user: { role: "ADMIN" } } as any);
    vi.mocked(prisma.expenseType.findUnique).mockResolvedValue({ id: "another", name: "Servicios" } as any);

    const response = await updateExpenseType(
      { json: vi.fn().mockResolvedValue({ name: "Servicios" }) } as any,
      { params: Promise.resolve({ id: "e1" }) }
    );
    const data = await response.json();

    expect(response.status).toBe(400);
    expect(data.error).toContain("Ya existe");
  });

  it("DELETE expense-types bloquea si está en uso", async () => {
    vi.mocked(getServerSession).mockResolvedValue({ user: { role: "ADMIN" } } as any);
    vi.mocked(prisma.expenseType.findUnique).mockResolvedValue({ id: "e1", name: "Viáticos" } as any);
    vi.mocked(prisma.category.count).mockResolvedValue(2 as any);

    const response = await deleteExpenseType(
      {} as any,
      { params: Promise.resolve({ id: "e1" }) }
    );

    expect(response.status).toBe(409);
  });

  it("DELETE payment-methods elimina cuando no está en uso", async () => {
    vi.mocked(getServerSession).mockResolvedValue({ user: { role: "ADMIN" } } as any);
    vi.mocked(prisma.paymentMethod.findUnique).mockResolvedValue({ id: "p1", name: "Efectivo" } as any);
    vi.mocked(prisma.category.count).mockResolvedValue(0 as any);

    const response = await deletePaymentMethod(
      {} as any,
      { params: Promise.resolve({ id: "p1" }) }
    );
    const data = await response.json();

    expect(response.status).toBe(200);
    expect(data.success).toBe(true);
    expect(prisma.paymentMethod.delete).toHaveBeenCalledWith({ where: { id: "p1" } });
  });

  it("GET payment-methods retorna listado", async () => {
    vi.mocked(getServerSession).mockResolvedValue({ user: { role: "ADMIN" } } as any);
    vi.mocked(prisma.paymentMethod.findMany).mockResolvedValue([{ id: "p1", name: "Efectivo" }] as any);

    const response = await getPaymentMethods();
    const data = await response.json();

    expect(response.status).toBe(200);
    expect(data).toHaveLength(1);
  });
});
