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

vi.mock('@/services/avenants.service', () => ({
  createAvenant: vi.fn(),
  listAvenants: vi.fn(),
  listAvenantsByContratBusinessId: vi.fn(),
}));

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

vi.mock('@/server/public-entity', () => ({
  omitInternalId: vi.fn((entity) => entity ? { ...entity, public: true } : entity),
  omitInternalIdList: vi.fn((items) =>
    items.map((item: any) => ({ ...item, public: true }))
  ),
  toPublicContract: vi.fn((contract) =>
    contract ? { ...contract, publicContract: true } : contract
  ),
}));

import {
  createAvenant,
  listAvenants,
  listAvenantsByContratBusinessId,
} from '@/services/avenants.service';

import {
  getContratById,
  getContratByBusinessId,
} from '@/services/contrats.service';

import {
  omitInternalId,
  omitInternalIdList,
  toPublicContract,
} from '@/server/public-entity';

import {
  getAvenantsFeed,
  createAvenantFeed,
  getAvenantsByContratIdFeed,
} from '../../../src/server/avenants.server';

describe('Unit - avenants server', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('getAvenantsFeed returns a public avenants feed', async () => {
    const avenants = [{ id: 1 }, { id: 2 }];

    vi.mocked(listAvenants).mockResolvedValue(avenants as never);

    const result = await getAvenantsFeed(25, 50);

    expect(listAvenants).toHaveBeenCalledWith(25, 50);
    expect(omitInternalIdList).toHaveBeenCalledWith(avenants);
    expect(result).toEqual({
      items: [
        { id: 1, public: true },
        { id: 2, public: true },
      ],
      count: 2,
    });
  });

  it('createAvenantFeed creates an avenant when contrat is found by technical id', async () => {
    const payload = {
      id_contrat: 1,
      avenant_type: 'Prolongation',
    };

    const contrat = {
      id: 1,
      ID_Contrat: 999,
    };

    const avenant = {
      id: 10,
      id_contrat: 999,
      avenant_type: 'Prolongation',
    };

    vi.mocked(getContratById).mockResolvedValue(contrat as never);
    vi.mocked(createAvenant).mockResolvedValue(avenant as never);

    const result = await createAvenantFeed(payload as any);

    expect(getContratById).toHaveBeenCalledWith(1);
    expect(getContratByBusinessId).not.toHaveBeenCalled();

    expect(createAvenant).toHaveBeenCalledWith({
      ...payload,
      id_contrat: 999,
    });

    expect(omitInternalId).toHaveBeenCalledWith(avenant);

    expect(result).toEqual({
      avenant: {
        id: 10,
        id_contrat: 999,
        avenant_type: 'Prolongation',
        public: true,
      },
      error: null,
    });
  });

  it('createAvenantFeed creates an avenant when contrat is found by business id', async () => {
    const payload = {
      id_contrat: 999,
      avenant_type: 'Prolongation',
    };

    const contrat = {
      id: 1,
      ID_Contrat: 999,
    };

    const avenant = {
      id: 10,
      id_contrat: 999,
      avenant_type: 'Prolongation',
    };

    vi.mocked(getContratById).mockResolvedValue(null as never);
    vi.mocked(getContratByBusinessId).mockResolvedValue(contrat as never);
    vi.mocked(createAvenant).mockResolvedValue(avenant as never);

    const result = await createAvenantFeed(payload as any);

    expect(getContratById).toHaveBeenCalledWith(999);
    expect(getContratByBusinessId).toHaveBeenCalledWith(999);

    expect(createAvenant).toHaveBeenCalledWith({
      ...payload,
      id_contrat: 999,
    });

    expect(result).toEqual({
      avenant: {
        id: 10,
        id_contrat: 999,
        avenant_type: 'Prolongation',
        public: true,
      },
      error: null,
    });
  });

  it('createAvenantFeed returns CONTRAT_NOT_FOUND when no contrat exists', async () => {
    const payload = {
      id_contrat: 999,
      avenant_type: 'Prolongation',
    };

    vi.mocked(getContratById).mockResolvedValue(null as never);
    vi.mocked(getContratByBusinessId).mockResolvedValue(null as never);

    const result = await createAvenantFeed(payload as any);

    expect(getContratById).toHaveBeenCalledWith(999);
    expect(getContratByBusinessId).toHaveBeenCalledWith(999);
    expect(createAvenant).not.toHaveBeenCalled();

    expect(result).toEqual({
      avenant: null,
      error: 'CONTRAT_NOT_FOUND',
    });
  });

  it('createAvenantFeed falls back to technical id when contrat has no business id', async () => {
    const payload = {
      id_contrat: 1,
      avenant_type: 'Prolongation',
    };

    const contrat = {
      id: 1,
      ID_Contrat: null,
    };

    const avenant = {
      id: 10,
      id_contrat: 1,
      avenant_type: 'Prolongation',
    };

    vi.mocked(getContratById).mockResolvedValue(contrat as never);
    vi.mocked(createAvenant).mockResolvedValue(avenant as never);

    const result = await createAvenantFeed(payload as any);

    expect(createAvenant).toHaveBeenCalledWith({
      ...payload,
      id_contrat: 1,
    });

    expect(result).toEqual({
      avenant: {
        id: 10,
        id_contrat: 1,
        avenant_type: 'Prolongation',
        public: true,
      },
      error: null,
    });
  });

  it('getAvenantsByContratIdFeed returns empty result when contrat does not exist', async () => {
    vi.mocked(getContratById).mockResolvedValue(null as never);

    const result = await getAvenantsByContratIdFeed(999);

    expect(getContratById).toHaveBeenCalledWith(999);
    expect(listAvenantsByContratBusinessId).not.toHaveBeenCalled();

    expect(result).toEqual({
      contrat: null,
      items: [],
      count: 0,
    });
  });

  it('getAvenantsByContratIdFeed returns avenants for contrat business id', async () => {
    const contrat = {
      id: 1,
      ID_Contrat: 999,
    };

    const avenants = [
      { id: 10, id_contrat: 999 },
      { id: 11, id_contrat: 999 },
    ];

    vi.mocked(getContratById).mockResolvedValue(contrat as never);
    vi.mocked(listAvenantsByContratBusinessId).mockResolvedValue(avenants as never);

    const result = await getAvenantsByContratIdFeed(1);

    expect(getContratById).toHaveBeenCalledWith(1);
    expect(listAvenantsByContratBusinessId).toHaveBeenCalledWith(999);
    expect(toPublicContract).toHaveBeenCalledWith(contrat);
    expect(omitInternalIdList).toHaveBeenCalledWith(avenants);

    expect(result).toEqual({
      contrat: {
        id: 1,
        ID_Contrat: 999,
        publicContract: true,
      },
      items: [
        { id: 10, id_contrat: 999, public: true },
        { id: 11, id_contrat: 999, public: true },
      ],
      count: 2,
    });
  });

  it('getAvenantsByContratIdFeed falls back to technical id when contrat has no business id', async () => {
    const contrat = {
      id: 1,
      ID_Contrat: null,
    };

    const avenants = [{ id: 10, id_contrat: 1 }];

    vi.mocked(getContratById).mockResolvedValue(contrat as never);
    vi.mocked(listAvenantsByContratBusinessId).mockResolvedValue(avenants as never);

    const result = await getAvenantsByContratIdFeed(1);

    expect(listAvenantsByContratBusinessId).toHaveBeenCalledWith(1);

    expect(result).toEqual({
      contrat: {
        id: 1,
        ID_Contrat: null,
        publicContract: true,
      },
      items: [
        { id: 10, id_contrat: 1, public: true },
      ],
      count: 1,
    });
  });
});