import { describe, expect, it } from "vitest";
import { ROLE_DEFINITIONS, ROLE_KEYS } from "@/lib/auth/permissions";
import { db } from "@/server/db";
import { createSaleFixtures, createTestUser } from "./fixtures";

describe("seed (ejecutado dos veces en el setup)", () => {
  it("roles y permisos coinciden exactamente con la matriz del código", async () => {
    for (const roleKey of ROLE_KEYS) {
      const role = await db.role.findUniqueOrThrow({
        where: { key: roleKey },
        include: { permissions: { include: { permission: true } } },
      });
      const inDb = role.permissions.map((rp) => rp.permission.key).sort();
      expect(inDb).toEqual([...ROLE_DEFINITIONS[roleKey].permissions].sort());
    }
  });

  it("es idempotente: sin duplicados de configuración", async () => {
    expect(await db.role.count()).toBe(ROLE_KEYS.length);
    const company = await db.setting.findUniqueOrThrow({ where: { key: "company" } });
    // No se inventan datos de la empresa.
    expect(company.value).toMatchObject({
      name: "CHESCO CARS",
      phone: null,
      address: null,
      ruc: null,
    });
  });
});

type SaleFixtures = Awaited<ReturnType<typeof createSaleFixtures>>;

function baseSale(f: SaleFixtures, createdById: string) {
  return {
    vehicleId: f.vehicle.id,
    customerId: f.customer.id,
    advisorId: f.advisor.id,
    finalPrice: "20000",
    currency: "USD" as const,
    soldAt: new Date(),
    vehicleStatusBeforeSale: "AVAILABLE" as const,
    createdById,
  };
}

describe("restricciones de integridad en PostgreSQL", () => {
  it("una sola venta ACTIVA por vehículo; anulada permite volver a vender", async () => {
    const f = await createSaleFixtures();
    const admin = await createTestUser("ADMIN");
    const first = await db.sale.create({ data: baseSale(f, admin.id) });
    await expect(db.sale.create({ data: baseSale(f, admin.id) })).rejects.toThrow(
      /Unique constraint|sales_one_active_per_vehicle/,
    );

    await db.sale.update({
      where: { id: first.id },
      data: {
        status: "ANNULLED",
        annulledAt: new Date(),
        annulledById: admin.id,
        annulmentReason: "Cliente desistió",
      },
    });
    await expect(db.sale.create({ data: baseSale(f, admin.id) })).resolves.toBeDefined();
  });

  it("anular exige fecha, usuario y motivo no vacío", async () => {
    const f = await createSaleFixtures();
    const admin = await createTestUser("ADMIN");
    const sale = await db.sale.create({ data: baseSale(f, admin.id) });
    await expect(
      db.sale.update({ where: { id: sale.id }, data: { status: "ANNULLED" } }),
    ).rejects.toThrow(/sales_annulment_consistency/);
    await expect(
      db.sale.update({
        where: { id: sale.id },
        data: {
          status: "ANNULLED",
          annulledAt: new Date(),
          annulledById: admin.id,
          annulmentReason: "  ",
        },
      }),
    ).rejects.toThrow(/sales_annulment_consistency/);
  });

  it("comisión del asesor: NONE no admite estado ni monto; con tipo exige estado", async () => {
    const admin = await createTestUser("ADMIN");
    const f1 = await createSaleFixtures();
    await expect(
      db.sale.create({
        data: {
          ...baseSale(f1, admin.id),
          advisorCommissionType: "NONE",
          advisorCommissionStatus: "PENDING",
        },
      }),
    ).rejects.toThrow(/sales_advisor_commission_consistency/);

    const f2 = await createSaleFixtures();
    await expect(
      db.sale.create({
        data: {
          ...baseSale(f2, admin.id),
          advisorCommissionType: "PERCENTAGE",
          advisorCommissionValue: "150",
          advisorCommissionAmount: "100",
          advisorCommissionStatus: "PENDING",
        },
      }),
    ).rejects.toThrow(/sales_advisor_commission_consistency/); // porcentaje > 100

    const f3 = await createSaleFixtures();
    await expect(
      db.sale.create({
        data: {
          ...baseSale(f3, admin.id),
          advisorCommissionType: "FIXED",
          advisorCommissionValue: "300",
          advisorCommissionAmount: "300",
          advisorCommissionStatus: "PAID",
          // falta advisorCommissionPaidAt
        },
      }),
    ).rejects.toThrow(/sales_advisor_commission_paid_at/);
  });

  it("liquidación al propietario solo en ventas consignadas y completa", async () => {
    const admin = await createTestUser("ADMIN");
    const f = await createSaleFixtures();
    await expect(
      db.sale.create({
        data: {
          ...baseSale(f, admin.id),
          ownerPayoutAmount: "19000",
          ownerPayoutStatus: "PENDING",
        },
      }),
    ).rejects.toThrow(/sales_consignment_snapshot/);
  });

  it("una sola reserva ACTIVA por vehículo", async () => {
    const f = await createSaleFixtures();
    const data = {
      vehicleId: f.vehicle.id,
      customerId: f.customer.id,
      advisorId: f.advisor.id,
      amount: "500",
      currency: "USD" as const,
      reservedAt: new Date(),
    };
    await db.reservation.create({ data });
    await expect(db.reservation.create({ data })).rejects.toThrow(
      /Unique constraint|reservations_one_active_per_vehicle/,
    );
    await expect(
      db.reservation.create({ data: { ...data, status: "CANCELLED" } }),
    ).resolves.toBeDefined();
  });

  it("valores imposibles en vehículos se rechazan", async () => {
    const f = await createSaleFixtures();
    await expect(
      db.vehicle.update({ where: { id: f.vehicle.id }, data: { mileageKm: -1 } }),
    ).rejects.toThrow(/vehicles_mileage_non_negative/);
    await expect(
      db.vehicle.update({ where: { id: f.vehicle.id }, data: { price: "-10" } }),
    ).rejects.toThrow(/vehicles_price_non_negative/);
  });
});
