import {
  getEpciLogo,
  findEpciWithLogo,
  getEpciRefId,
  getEpciList,
  getEpci,
  getEpciStats,
  getEpciEvents,
  getEpciActeurs,
  getEpciActeursCarte,
} from './epciService';
import http from '@/http';

// Mock http
const mockedHttp = http as jest.Mocked<typeof http>;

describe('epciService', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe('getEpciLogo', () => {
    it('shouldReturnLogoUrlWhenLogoExists', async () => {
      // Arrange
      mockedHttp.get.mockResolvedValue({ status: 200,
        data: {url: 'https://example.com/logo.png'}
      });

      // Act
      const result = await getEpciLogo(1);

      // Assert
      expect(result).toBe('https://example.com/logo.png');
      expect(mockedHttp.get).toHaveBeenCalledWith(
        '/media/geolocale_subdivision_autre/1/Logo'
      );
    });

    it('shouldReturnNullWhenImageIsPlaceholder', async () => {
      // Arrange
      mockedHttp.get.mockResolvedValue({ status: 200,
        data: {url: 'https://dummyimage.com/150'}
      });

      // Act
      const result = await getEpciLogo(1);

      // Assert
      expect(result).toBeNull();
    });

    it('shouldReturnNullOnError', async () => {
      // Arrange
      mockedHttp.get.mockRejectedValue(new Error('Network error'));

      // Act
      const result = await getEpciLogo(1);

      // Assert
      expect(result).toBeNull();
    });
  });

  describe('findEpciWithLogo', () => {
    it('shouldReturnFirstEpciWithLogoPartenaire', () => {
      // Arrange
      const subAutres = [
        {suaId: 1, suaNom: 'EPCI 1', suaLogoPartenaire: 0},
        {suaId: 2, suaNom: 'EPCI 2', suaLogoPartenaire: 1}
      ];

      // Act
      const result = findEpciWithLogo(subAutres);

      // Assert
      expect(result).toEqual(subAutres[1]);
    });

    it('shouldReturnNullWhenNoLogoPartenaire', () => {
      // Arrange
      const subAutres = [
        {suaId: 1, suaNom: 'EPCI 1', suaLogoPartenaire: 0}
      ];

      // Act
      const result = findEpciWithLogo(subAutres);

      // Assert
      expect(result).toBeNull();
    });

    it('shouldReturnNullForEmptyOrUndefinedInput', () => {
      // Act & Assert
      expect(findEpciWithLogo(undefined)).toBeNull();
      expect(findEpciWithLogo([])).toBeNull();
    });
  });

  describe('getEpciRefId', () => {
    it('shouldReturnRefIdWhenEpciReferentielExists', async () => {
      // Arrange
      mockedHttp.get.mockResolvedValue({ status: 200,
        data: {'hydra:member': [{refId: 42}]}
      });

      // Act
      const result = await getEpciRefId();

      // Assert
      expect(result).toBe(42);
      expect(mockedHttp.get).toHaveBeenCalledWith(
        expect.stringContaining('refNom=EPCI')
      );
    });

    it('shouldThrowWhenEpciReferentielNotFound', async () => {
      // Arrange
      mockedHttp.get.mockResolvedValue({ status: 200,
        data: {'hydra:member': []}
      });

      // Act & Assert
      await expect(getEpciRefId()).rejects.toThrow(
        'EPCI referentiel not found'
      );
    });
  });

  describe('getEpciList', () => {
    it('shouldReturnEpciList', async () => {
      // Arrange
      const mockEpciList = [{suaId: 1, suaNom: 'Test'}];

      mockedHttp.get
        // 1st call: getEpciRefId
        .mockResolvedValueOnce({ status: 200,
          data: {'hydra:member': [{refId: 42}]}
        })
        // 2nd call: getEpciList
        .mockResolvedValueOnce({ status: 200,
          data: {'hydra:member': mockEpciList}
        });

      // Act
      const result = await getEpciList();

      // Assert
      expect(result).toEqual(mockEpciList);
      expect(mockedHttp.get).toHaveBeenCalledTimes(2);
      const secondCallUrl = mockedHttp.get.mock.calls[1][0];
      expect(secondCallUrl).toContain('suaRef=42');
    });
  });

  describe('getEpci', () => {
    it('shouldReturnEpciWhenItExists', async () => {
      // Arrange
      const mockEpci = {suaId: 1, suaNom: 'Rennes Métropole'};
      mockedHttp.get.mockResolvedValue({status: 200, data: mockEpci});

      // Act
      const result = await getEpci('1');

      // Assert
      expect(result).toEqual(mockEpci);
      expect(mockedHttp.get).toHaveBeenCalledWith(
        '/geolocale_subdivision_autres/1'
      );
    });

    it('shouldThrowWhenIdIsEmpty', async () => {
      // Act & Assert
      await expect(getEpci('')).rejects.toThrow('EPCI id is required');
      expect(mockedHttp.get).not.toHaveBeenCalled();
    });

    it('shouldThrowWhenEpciNotFound404', async () => {
      // Arrange
      mockedHttp.get.mockRejectedValue({
        response: {status: 404}
      });

      // Act & Assert
      await expect(getEpci('999')).rejects.toThrow('EPCI not found');
    });
  });

  describe('getEpciStats', () => {
    it('shouldReturnStats', async () => {
      // Arrange
      const mockStats = {
        communes: 10,
        acteurs: 5,
        evenements: 3,
        evenementsN1: 2,
        lieux: 4
      };
      mockedHttp.get.mockResolvedValue({status: 200, data: mockStats});

      // Act
      const result = await getEpciStats('1');

      // Assert
      expect(result).toEqual(mockStats);
      expect(mockedHttp.get).toHaveBeenCalledWith(
        '/geolocale_subdivision_autres/1/stats'
      );
    });

    it('shouldThrowOnStats404', async () => {
      // Arrange
      mockedHttp.get.mockRejectedValue({
        response: {status: 404}
      });

      // Act & Assert
      await expect(getEpciStats('999')).rejects.toThrow('Stats not found');
    });
  });

  describe('getEpciEvents', () => {
    it('shouldAskTheUpcomingValidEventsOfTheEpciCities', async () => {
      jest.useFakeTimers().setSystemTime(new Date('2026-09-20T10:00:00Z'));
      mockedHttp.get.mockResolvedValue({status: 200, data: {'hydra:member': [{eveId: 1}]}});

      const result = await getEpciEvents('3');

      expect(result).toEqual([{eveId: 1}]);
      expect(mockedHttp.get).toHaveBeenCalledWith(
        '/evenements?page=1&itemsPerPage=4&eveVil.subAutres=3&eveValide=1&eveDate%5Bafter%5D=2026-09-20'
      );
      jest.useRealTimers();
    });

    it('shouldReturnNoEventWhenTheApiFails', async () => {
      mockedHttp.get.mockRejectedValue(new Error('boom'));

      expect(await getEpciEvents('3')).toEqual([]);
    });
  });

  describe('getEpciActeurs', () => {
    it('shouldFetchAPageOfActorsOfTheEpci', async () => {
      const page = {total: 1, page: 2, itemsPerPage: 12, items: [{aenId: 1}]};
      mockedHttp.get.mockResolvedValue({status: 200, data: page});

      expect(await getEpciActeurs('3', 2)).toEqual(page);
      expect(mockedHttp.get).toHaveBeenCalledWith('/geolocale_subdivision_autres/3/acteurs?page=2&itemsPerPage=12');
    });

    it('shouldReturnAnEmptyPageWhenTheApiFails', async () => {
      mockedHttp.get.mockRejectedValue(new Error('boom'));

      expect(await getEpciActeurs('3')).toEqual({total: 0, page: 1, itemsPerPage: 12, items: []});
    });
  });

  describe('getEpciActeursCarte', () => {
    it('shouldFetchTheGeolocatedActiveActors', async () => {
      const carte = {suaId: 3, centre: {geoX: -4.1, geoY: 48, zoom: 10}, acteurs: [{aenId: 1}]};
      mockedHttp.get.mockResolvedValue({status: 200, data: carte});

      expect(await getEpciActeursCarte('3')).toEqual(carte);
      expect(mockedHttp.get).toHaveBeenCalledWith('/geolocale_subdivision_autres/3/acteurs-carte');
    });

    it('shouldReturnAnEmptyMapWhenTheApiFails', async () => {
      mockedHttp.get.mockRejectedValue(new Error('boom'));

      expect(await getEpciActeursCarte('3')).toEqual({suaId: 3, centre: null, acteurs: []});
    });
  });
});
