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

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

vi.mock('@/services/contrats.service', () => ({
    hasContratForSalarie: vi.fn(),
}));

import * as employeService from '@/services/employes.service';
import { prisma } from '@/lib/prisma';
import { hasContratForSalarie } from '@/services/contrats.service';
type CreateEmployePayload = Parameters<typeof employeService.createEmploye>[0];

describe('Unit — services/employes.service', () => {
    beforeEach(() => {
        vi.resetAllMocks();
    });
    it('listEmployes: should return the employes list with pagination', async () => {
        const limit = 25;
        const offset = 50;
        const expectedEmployes = [
            { id: 1, COS: 5524, PRE: 'Alice' },
            { id: 2, COS: 42, PRE: 'Bob' },
        ];

        vi.mocked(prisma.employes.findMany).mockResolvedValue(expectedEmployes as never);

        const res = await employeService.listEmployes(limit, offset);

        expect(prisma.employes.findMany).toHaveBeenCalledWith({
            orderBy: [{ id: 'asc' }],
            take: limit,
            skip: offset,
            select: {
                id: true,
                COS: true,
                TIT: true,
                NSA: true,
                NJF: true,
                PRE: true,
                VIL: true,
                Actif: true,
                EmailE2e: true,
            },
        });
        expect(res).toEqual(expectedEmployes);
    });
    it('createEmploye: should call prisma.create and return created employe', async () => {
        const payload: CreateEmployePayload = { COS: 123, PRE: 'Jean', TIT: 'M', NSA: '000', NJF: '2020-01-01', VIL: 'Paris', EmailE2e: 'a@b' };
        const created = { id: 1, ...payload };

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

        const res = await employeService.createEmploye(payload);

        expect(prisma.employes.create).toHaveBeenCalledWith({ data: payload });
        expect(res).toEqual(created);
    });

    it('deleteEmployeByCos: should throw error with code P2003 when dependant contrats exist', async () => {
        vi.mocked(prisma.employes.findUnique).mockResolvedValue({ COS: 123 } as never);
        vi.mocked(hasContratForSalarie).mockResolvedValue(true as never);

        await expect(employeService.deleteEmployeByCos(123)).rejects.toMatchObject({
            code: 'P2003',
            message: 'Cannot delete employe with dependent contrats',
        });
    });

    it('deleteEmployeByCos: should return null when COS does not exist', async () => {
        const cos = 55555;

        vi.mocked(prisma.employes.findUnique).mockResolvedValue(null as never);

        const res = await employeService.deleteEmployeByCos(cos);

        expect(prisma.employes.findUnique).toHaveBeenCalledWith({
            where: { COS: cos },
        });
        expect(hasContratForSalarie).not.toHaveBeenCalled();
        expect(prisma.employes.delete).not.toHaveBeenCalled();
        expect(res).toBeNull();
    });

    it('deleteEmployeByCos: should delete and return employe when no dependant contrat exists', async () => {
        const cos = 2468;
        const deletedEmploye = { id: 77, COS: cos, PRE: 'Martin' };

        vi.mocked(prisma.employes.findUnique).mockResolvedValue({ id: 77, COS: cos } as never);
        vi.mocked(hasContratForSalarie).mockResolvedValue(false as never);
        vi.mocked(prisma.employes.delete).mockResolvedValue(deletedEmploye as never);

        const res = await employeService.deleteEmployeByCos(cos);

        expect(prisma.employes.findUnique).toHaveBeenCalledWith({
            where: { COS: cos },
        });
        expect(hasContratForSalarie).toHaveBeenCalledWith(cos);
        expect(prisma.employes.delete).toHaveBeenCalledWith({
            where: { COS: cos },
        });
        expect(res).toEqual(deletedEmploye);
    });
    it('getEmployeByCos: should return the employe for the given COS', async () => {
        const cos = 1937;
        const expectedEmploye = { id: 10, COS: cos, PRE: 'Alice' };

        vi.mocked(prisma.employes.findUnique).mockResolvedValue(expectedEmploye as never);

        const res = await employeService.getEmployeByCos(cos);

        expect(prisma.employes.findUnique).toHaveBeenCalledWith({
            where: { COS: cos },
        });
        expect(res).toEqual(expectedEmploye);
    });

    it('getEmployeByCos: should return null when employe does not exist', async () => {
        const cos = 404;

        vi.mocked(prisma.employes.findUnique).mockResolvedValue(null as never);

        const res = await employeService.getEmployeByCos(cos);

        expect(prisma.employes.findUnique).toHaveBeenCalledWith({
            where: { COS: cos },
        });
        expect(res).toBeNull();
    });
    it('updateEmployeByCos: should update employe when COS exists', async () => {
        const cos = 1987;
        const payload = { PRE: 'Updated', VIL: 'Lyon' } as Partial<CreateEmployePayload>;
        const updatedEmploye = { id: 22, COS: cos, ...payload };

        vi.mocked(prisma.employes.findUnique).mockResolvedValue({ id: 22, COS: cos } as never);
        vi.mocked(prisma.employes.update).mockResolvedValue(updatedEmploye as never);

        const res = await employeService.updateEmployeByCos(cos, payload);

        expect(prisma.employes.findUnique).toHaveBeenCalledWith({
            where: { COS: cos },
        });
        expect(prisma.employes.update).toHaveBeenCalledWith({
            where: { COS: cos },
            data: payload,
        });
        expect(res).toEqual(updatedEmploye);
    });

    it('updateEmployeByCos: should return null when COS does not exist', async () => {
        const cos = 99999;
        const payload = { PRE: 'Nope' } as Partial<CreateEmployePayload>;

        vi.mocked(prisma.employes.findUnique).mockResolvedValue(null as never);

        const res = await employeService.updateEmployeByCos(cos, payload);

        expect(prisma.employes.findUnique).toHaveBeenCalledWith({
            where: { COS: cos },
        });
        expect(prisma.employes.update).not.toHaveBeenCalled();
        expect(res).toBeNull();
    });
});
