import { renderHook, act } from '@testing-library/react';
import { useCsvRateSearch } from '@/hooks/useCsvRateSearch';
import { searchCsvRatesWithOffers } from '@/lib/api/rates';
import type { CsvRateSearchRequest, CsvRateSearchResponse } from '@/types/rates';

jest.mock('@/lib/api/rates', () => ({
  searchCsvRatesWithOffers: jest.fn(),
}));

const mockSearchCsvRatesWithOffers = jest.mocked(searchCsvRatesWithOffers);

const mockRequest: CsvRateSearchRequest = {
  origin: 'FRLEH',
  destination: 'CNSHA',
  volumeCBM: 10,
  weightKG: 5000,
};

const mockResponse: CsvRateSearchResponse = {
  results: [
    {
      companyName: 'SSC Consolidation',
      companyEmail: 'bookings@ssc.com',
      originCFS: 'Le Havre',
      origin: 'FRLEH',
      portOfLoading: 'LE HAVRE',
      routing: 'Direct',
      destinationCFS: 'Shanghai',
      destination: 'CNSHA',
      destinationCountry: 'China',
      containerType: 'LCL',
      priceBreakdown: {
        freightCharge: 440,
        freightCurrency: 'USD',
        fobFixed: 173,
        fobHandling: 110,
        fobDG: 0,
        fobCurrency: 'EUR',
        fobBreakdown: {
          documentation: 55,
          isps: 18,
          handling: 110,
          solas: 15,
          customs: 85,
          ams_aci: 0,
          isf5: 0,
          dgAdmin: 0,
        },
        dgSurchargeAmount: null,
        dgSurchargeCurrency: 'EUR',
        dgSurchargeStatus: 'computed',
        totalFreight: 440,
        totalFob: 283,
        totalPriceForSorting: 723,
        primaryCurrency: 'USD',
      },
      frequency: 'Weekly',
      transitDays: 33,
      validUntil: '2026-12-31',
      dgAccepted: true,
      dgSurchargeStatus: 'computed',
      remarks: '',
      source: 'CSV',
      matchScore: 110,
      serviceLevel: 'STANDARD',
      priceMultiplier: 1.0,
      originalTransitDays: 33,
      adjustedTransitDays: 33,
    },
  ],
  totalResults: 1,
  searchedFiles: ['ssc-consolidation.csv'],
  searchedAt: '2026-05-11T10:00:00Z',
  appliedFilters: {},
};

beforeEach(() => {
  jest.clearAllMocks();
});

describe('useCsvRateSearch', () => {
  describe('initial state', () => {
    it('starts with data=null', () => {
      const { result } = renderHook(() => useCsvRateSearch());
      expect(result.current.data).toBeNull();
    });

    it('starts with loading=false', () => {
      const { result } = renderHook(() => useCsvRateSearch());
      expect(result.current.loading).toBe(false);
    });

    it('starts with error=null', () => {
      const { result } = renderHook(() => useCsvRateSearch());
      expect(result.current.error).toBeNull();
    });

    it('exposes a search function', () => {
      const { result } = renderHook(() => useCsvRateSearch());
      expect(typeof result.current.search).toBe('function');
    });

    it('exposes a reset function', () => {
      const { result } = renderHook(() => useCsvRateSearch());
      expect(typeof result.current.reset).toBe('function');
    });
  });

  describe('search — success path', () => {
    it('sets loading=true while the request is in flight', async () => {
      let resolveSearch: (v: any) => void;
      mockSearchCsvRatesWithOffers.mockReturnValue(
        new Promise(resolve => {
          resolveSearch = resolve;
        })
      );

      const { result } = renderHook(() => useCsvRateSearch());

      act(() => {
        result.current.search(mockRequest);
      });

      expect(result.current.loading).toBe(true);

      await act(async () => {
        resolveSearch!(mockResponse);
      });
    });

    it('sets data and clears loading after a successful search', async () => {
      mockSearchCsvRatesWithOffers.mockResolvedValue(mockResponse as any);

      const { result } = renderHook(() => useCsvRateSearch());

      await act(async () => {
        await result.current.search(mockRequest);
      });

      expect(result.current.loading).toBe(false);
      expect(result.current.data).toEqual(mockResponse);
      expect(result.current.error).toBeNull();
    });

    it('calls searchCsvRatesWithOffers with the given request', async () => {
      mockSearchCsvRatesWithOffers.mockResolvedValue(mockResponse as any);

      const { result } = renderHook(() => useCsvRateSearch());

      await act(async () => {
        await result.current.search(mockRequest);
      });

      expect(mockSearchCsvRatesWithOffers).toHaveBeenCalledWith(mockRequest);
    });

    it('clears a previous error when a new search starts', async () => {
      mockSearchCsvRatesWithOffers.mockRejectedValueOnce(new Error('first error'));
      mockSearchCsvRatesWithOffers.mockResolvedValueOnce(mockResponse as any);

      const { result } = renderHook(() => useCsvRateSearch());

      await act(async () => {
        await result.current.search(mockRequest);
      });
      expect(result.current.error).toBe('first error');

      await act(async () => {
        await result.current.search(mockRequest);
      });
      expect(result.current.error).toBeNull();
    });
  });

  describe('search — error path', () => {
    it('sets error and clears data when the API throws', async () => {
      mockSearchCsvRatesWithOffers.mockRejectedValue(new Error('Network error'));

      const { result } = renderHook(() => useCsvRateSearch());

      await act(async () => {
        await result.current.search(mockRequest);
      });

      expect(result.current.error).toBe('Network error');
      expect(result.current.data).toBeNull();
      expect(result.current.loading).toBe(false);
    });

    it('uses a default error message when the error has no message', async () => {
      mockSearchCsvRatesWithOffers.mockRejectedValue({});

      const { result } = renderHook(() => useCsvRateSearch());

      await act(async () => {
        await result.current.search(mockRequest);
      });

      expect(result.current.error).toBe('Erreur lors de la recherche de tarifs');
    });
  });

  describe('reset', () => {
    it('clears data, error, and loading', async () => {
      mockSearchCsvRatesWithOffers.mockResolvedValue(mockResponse as any);

      const { result } = renderHook(() => useCsvRateSearch());

      await act(async () => {
        await result.current.search(mockRequest);
      });

      act(() => {
        result.current.reset();
      });

      expect(result.current.data).toBeNull();
      expect(result.current.error).toBeNull();
      expect(result.current.loading).toBe(false);
    });

    it('can be called before any search without throwing', () => {
      const { result } = renderHook(() => useCsvRateSearch());

      expect(() => {
        act(() => result.current.reset());
      }).not.toThrow();
    });
  });
});
