import { describe, it, expect, vi, beforeEach } from 'vitest';

vi.mock('@/lib/prisma', () => ({
  prisma: {
    contrats: {
      create: vi.fn(),
      findMany: vi.fn(),
      findUnique: vi.fn(),
      findFirst: vi.fn(),
      update: vi.fn(),
      delete: vi.fn()
    },
  },
}));

import * as contratService from '@/services/contrats.service';
import { prisma } from '@/lib/prisma';

type CreateContratPayload = Parameters<typeof contratService.createContrat>[0] & {
  ID_Contrat: number;
  Id_Salarie: number;
};

type UpdateContratPayload = Parameters<typeof contratService.updateContratById>[1];

describe('Unit — services/contrats', () => {
  beforeEach(() => {
    vi.resetAllMocks();
  });
  it('listContrats: should return the contracts list with pagination', async () => {
    const limit = 25;
    const offset = 50;
    const expectedContrats = [
      { id: 1, ID_Contrat: 5524, Id_Salarie: 955, TCS: 'CDD Tps Complet' },
      { id: 2, ID_Contrat: 5525, Id_Salarie: 956, TCS: 'CDI' },
    ];

    vi.mocked(prisma.contrats.findMany).mockResolvedValue(expectedContrats as never);

    const res = await contratService.listContrats(limit, offset);

    expect(prisma.contrats.findMany).toHaveBeenCalledWith({
      orderBy: [{ id: 'asc' }],
      take: limit,
      skip: offset,
      select: {
        id: true,
        ID_Contrat: true,
        Id_Salarie: true,
        TCS: true,
        MotifContrat: true,
        DAE: true,
        FPE: true,
        ETB: true,
      },
    });
    expect(res).toEqual(expectedContrats);
  });

  it('listContrats: should return an empty array when no contract exists', async () => {
    const limit = 10;
    const offset = 0;

    vi.mocked(prisma.contrats.findMany).mockResolvedValue([] as never);

    const res = await contratService.listContrats(limit, offset);

    expect(prisma.contrats.findMany).toHaveBeenCalledWith({
      orderBy: [{ id: 'asc' }],
      take: limit,
      skip: offset,
      select: {
        id: true,
        ID_Contrat: true,
        Id_Salarie: true,
        TCS: true,
        MotifContrat: true,
        DAE: true,
        FPE: true,
        ETB: true,
      },
    });
    expect(res).toEqual([]);
  });
  it('getContratById: Should return the contrat for the given ID ', async () => {
    const id = 1;
    const expectedContrat = { id: 1, ID_Contrat: 5524, Id_Salarie: 955, TCS: "CDD Tps Complet" };

    vi.mocked(prisma.contrats.findUnique).mockResolvedValue(expectedContrat as never);

    const res = await contratService.getContratById(id);

    expect(prisma.contrats.findUnique).toHaveBeenCalledWith({
      where: { id: id },
    });
    expect(res).toEqual(expectedContrat);
  });
  it('getContratById: should return null when contrat does not exist', async () => {
    const id = 404;

    vi.mocked(prisma.contrats.findUnique).mockResolvedValue(null);

    const res = await contratService.getContratById(id);

    expect(prisma.contrats.findUnique).toHaveBeenCalledWith({
      where: { id: id },
    });
    expect(res).toBeNull();
  });
  it('getContratByBusinessId: Should return the contrat for the given ID_Contrat ', async () => {
    const ID_Contrat = 5524;
    const expectedContrat = { id: 1, ID_Contrat: 5524, Id_Salarie: 955, TCS: "CDD Tps Complet" };

    vi.mocked(prisma.contrats.findFirst).mockResolvedValue(expectedContrat as never);

    const res = await contratService.getContratByBusinessId(ID_Contrat);

    expect(prisma.contrats.findFirst).toHaveBeenCalledWith({
      where: { ID_Contrat: ID_Contrat },
    });
    expect(res).toEqual(expectedContrat);
  });
  it('getContratByBusinessId: should return null when contrat does not exist', async () => {
    const ID_Contrat = 5524;

    vi.mocked(prisma.contrats.findFirst).mockResolvedValue(null);

    const res = await contratService.getContratByBusinessId(ID_Contrat);

    expect(prisma.contrats.findFirst).toHaveBeenCalledWith({
      where: {
        ID_Contrat: ID_Contrat
      },
    });
    expect(res).toBeNull();
  });
  it('hasContratForSalarie: should return true when a contrat exists for the given salarie', async () => {
    const cos = 955;

    vi.mocked(prisma.contrats.findFirst).mockResolvedValue({ id: 12 } as never);

    const res = await contratService.hasContratForSalarie(cos);

    expect(prisma.contrats.findFirst).toHaveBeenCalledWith({
      where: { Id_Salarie: cos },
      select: { id: true },
    });
    expect(res).toBe(true);
  });

  it('hasContratForSalarie: should return false when no contrat exists for the given salarie', async () => {
    const cos = 12345;

    vi.mocked(prisma.contrats.findFirst).mockResolvedValue(null);

    const res = await contratService.hasContratForSalarie(cos);

    expect(prisma.contrats.findFirst).toHaveBeenCalledWith({
      where: { Id_Salarie: cos },
      select: { id: true },
    });
    expect(res).toBe(false);
  });
  it('createContrat: should call prisma.create and return created contrat', async () => {
    const payload: CreateContratPayload = { id_salarie: 501, ID_Contrat: 66, Id_Salarie: 501, TCS: 'M', MotifContrat: 'oui', DAE: new Date(), FPE: new Date(), ETB: 'a@b' };
    const expectedData = {
      ID_Contrat: 66,
      Id_Salarie: 501,
      TCS: 'M',
      MotifContrat: 'oui',
      DAE: payload.DAE,
      FPE: payload.FPE,
      ETB: 'a@b',
    };
    const created = { id: 1, ...payload };

    vi.mocked(prisma.contrats.create).mockResolvedValue(created as never);

    const res = await contratService.createContrat(payload);

    expect(prisma.contrats.create).toHaveBeenCalledWith({ data: expectedData });
    expect(res).toEqual(created);
  });

  it('updateContratById: should update contrat when ID exists', async () => {
    const id = 1;
    const payload: UpdateContratPayload = { TCS: 'Updated' };
    const updatedContrat = { id: id, ...payload };

    vi.mocked(prisma.contrats.findUnique).mockResolvedValue({ id: id } as never);
    vi.mocked(prisma.contrats.update).mockResolvedValue(updatedContrat as never);

    const res = await contratService.updateContratById(id, payload);

    expect(prisma.contrats.findUnique).toHaveBeenCalledWith({
      where: { id: id },
    });
    expect(prisma.contrats.update).toHaveBeenCalledWith({
      where: { id: id },
      data: payload,
    });
    expect(res).toEqual(updatedContrat);
  });
  it('updateContratById: should return null when ID does not exist', async () => {
    const id = 99999;
    const payload: UpdateContratPayload = { TCS: 'Nope' };

    vi.mocked(prisma.contrats.findUnique).mockResolvedValue(null);

    const res = await contratService.updateContratById(id, payload);

    expect(prisma.contrats.findUnique).toHaveBeenCalledWith({
      where: { id: id },
    });
    expect(prisma.contrats.update).not.toHaveBeenCalled();
    expect(res).toBeNull();
  });
  it('deleteContratById: should delete and return contrat ', async () => {
    const id = 1;
    const deletedContrat = { id: id, TCS: 'Delete' };

    vi.mocked(prisma.contrats.findUnique).mockResolvedValue({ id: id } as never);
    vi.mocked(prisma.contrats.delete).mockResolvedValue(deletedContrat as never);

    const res = await contratService.deleteContratById(id);

    expect(prisma.contrats.findUnique).toHaveBeenCalledWith({
      where: { id: id },
    });
    expect(prisma.contrats.delete).toHaveBeenCalledWith({
      where: { id: id },
    });
    expect(res).toEqual(deletedContrat);
  });
  it('deleteContratById: should return null when ID does not exist', async () => {
    const id = 55555;

    vi.mocked(prisma.contrats.findUnique).mockResolvedValue(null);

    const res = await contratService.deleteContratById(id);

    expect(prisma.contrats.findUnique).toHaveBeenCalledWith({
      where: { id: id },
    });
    expect(prisma.contrats.delete).not.toHaveBeenCalled();
    expect(res).toBeNull();
  });
});
