/**
 * @ai-summary Tests for stations API client
 */

import axios from 'axios';
import { stationsApi } from '../../api/stations.api';
import { Station, StationSearchRequest } from '../../types/stations.types';

jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;

const mockStations: Station[] = [
  {
    placeId: 'test-1',
    name: 'Shell Station',
    address: '123 Main St',
    latitude: 37.7749,
    longitude: -122.4194,
    rating: 4.2,
    distance: 250
  }
];

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

  describe('searchStations', () => {
    it('should search for stations with valid request', async () => {
      const request: StationSearchRequest = {
        latitude: 37.7749,
        longitude: -122.4194,
        radius: 5000
      };

      mockedAxios.post.mockResolvedValue({
        data: { stations: mockStations }
      });

      const result = await stationsApi.searchStations(request);

      expect(mockedAxios.post).toHaveBeenCalledWith(
        '/api/stations/search',
        request
      );
      expect(result).toEqual(mockStations);
    });

    it('should handle search without radius', async () => {
      const request: StationSearchRequest = {
        latitude: 37.7749,
        longitude: -122.4194
      };

      mockedAxios.post.mockResolvedValue({
        data: { stations: mockStations }
      });

      await stationsApi.searchStations(request);

      expect(mockedAxios.post).toHaveBeenCalledWith(
        '/api/stations/search',
        request
      );
    });

    it('should handle API errors', async () => {
      mockedAxios.post.mockRejectedValue(new Error('Network error'));

      await expect(
        stationsApi.searchStations({
          latitude: 37.7749,
          longitude: -122.4194
        })
      ).rejects.toThrow('Network error');
    });

    it('should handle 401 unauthorized', async () => {
      mockedAxios.post.mockRejectedValue({
        response: { status: 401, data: { message: 'Unauthorized' } }
      });

      await expect(
        stationsApi.searchStations({
          latitude: 37.7749,
          longitude: -122.4194
        })
      ).rejects.toBeDefined();
    });

    it('should handle 500 server error', async () => {
      mockedAxios.post.mockRejectedValue({
        response: { status: 500, data: { message: 'Internal server error' } }
      });

      await expect(
        stationsApi.searchStations({
          latitude: 37.7749,
          longitude: -122.4194
        })
      ).rejects.toBeDefined();
    });
  });

  describe('saveStation', () => {
    it('should save a station with metadata', async () => {
      const placeId = 'test-place-id';
      const data = {
        nickname: 'Work Station',
        notes: 'Best prices',
        isFavorite: true
      };

      mockedAxios.post.mockResolvedValue({
        data: { id: '123', ...data, placeId }
      });

      const result = await stationsApi.saveStation(placeId, data);

      expect(mockedAxios.post).toHaveBeenCalledWith('/api/stations/save', {
        placeId,
        ...data
      });
      expect(result.placeId).toBe(placeId);
    });

    it('should save station without optional fields', async () => {
      const placeId = 'test-place-id';

      mockedAxios.post.mockResolvedValue({
        data: { id: '123', placeId }
      });

      await stationsApi.saveStation(placeId, {});

      expect(mockedAxios.post).toHaveBeenCalledWith('/api/stations/save', {
        placeId
      });
    });

    it('should handle save errors', async () => {
      mockedAxios.post.mockRejectedValue({
        response: { status: 404, data: { message: 'Station not found' } }
      });

      await expect(
        stationsApi.saveStation('invalid-id', {})
      ).rejects.toBeDefined();
    });
  });

  describe('getSavedStations', () => {
    it('should fetch all saved stations', async () => {
      mockedAxios.get.mockResolvedValue({
        data: mockStations
      });

      const result = await stationsApi.getSavedStations();

      expect(mockedAxios.get).toHaveBeenCalledWith('/api/stations/saved');
      expect(result).toEqual(mockStations);
    });

    it('should return empty array when no saved stations', async () => {
      mockedAxios.get.mockResolvedValue({
        data: []
      });

      const result = await stationsApi.getSavedStations();

      expect(result).toEqual([]);
    });

    it('should handle fetch errors', async () => {
      mockedAxios.get.mockRejectedValue(new Error('Network error'));

      await expect(stationsApi.getSavedStations()).rejects.toThrow(
        'Network error'
      );
    });
  });

  describe('deleteSavedStation', () => {
    it('should delete a station by placeId', async () => {
      const placeId = 'test-place-id';

      mockedAxios.delete.mockResolvedValue({ status: 204 });

      await stationsApi.deleteSavedStation(placeId);

      expect(mockedAxios.delete).toHaveBeenCalledWith(
        `/api/stations/saved/${placeId}`
      );
    });

    it('should handle 404 not found', async () => {
      mockedAxios.delete.mockRejectedValue({
        response: { status: 404, data: { message: 'Not found' } }
      });

      await expect(
        stationsApi.deleteSavedStation('invalid-id')
      ).rejects.toBeDefined();
    });

    it('should handle delete errors', async () => {
      mockedAxios.delete.mockRejectedValue(new Error('Network error'));

      await expect(
        stationsApi.deleteSavedStation('test-id')
      ).rejects.toThrow('Network error');
    });
  });

  describe('URL Construction', () => {
    it('should use correct API base path', async () => {
      mockedAxios.post.mockResolvedValue({ data: { stations: [] } });

      await stationsApi.searchStations({
        latitude: 37.7749,
        longitude: -122.4194
      });

      const callUrl = mockedAxios.post.mock.calls[0][0];
      expect(callUrl).toContain('/api/stations');
    });

    it('should construct correct saved station URL', async () => {
      mockedAxios.delete.mockResolvedValue({ status: 204 });

      await stationsApi.deleteSavedStation('test-place-id');

      const callUrl = mockedAxios.delete.mock.calls[0][0];
      expect(callUrl).toBe('/api/stations/saved/test-place-id');
    });
  });

  describe('Request Payload Validation', () => {
    it('should send correct payload for search', async () => {
      const request = {
        latitude: 37.7749,
        longitude: -122.4194,
        radius: 5000
      };

      mockedAxios.post.mockResolvedValue({ data: { stations: [] } });

      await stationsApi.searchStations(request);

      const payload = mockedAxios.post.mock.calls[0][1];
      expect(payload).toEqual(request);
    });

    it('should send correct payload for save', async () => {
      const placeId = 'test-id';
      const data = { nickname: 'Test', isFavorite: true };

      mockedAxios.post.mockResolvedValue({ data: {} });

      await stationsApi.saveStation(placeId, data);

      const payload = mockedAxios.post.mock.calls[0][1];
      expect(payload).toEqual({ placeId, ...data });
    });
  });

  describe('Response Parsing', () => {
    it('should parse search response correctly', async () => {
      const responseData = {
        stations: mockStations,
        searchLocation: { latitude: 37.7749, longitude: -122.4194 },
        searchRadius: 5000
      };

      mockedAxios.post.mockResolvedValue({ data: responseData });

      const result = await stationsApi.searchStations({
        latitude: 37.7749,
        longitude: -122.4194
      });

      expect(result).toEqual(mockStations);
    });

    it('should parse saved stations response', async () => {
      mockedAxios.get.mockResolvedValue({ data: mockStations });

      const result = await stationsApi.getSavedStations();

      expect(Array.isArray(result)).toBe(true);
      expect(result).toEqual(mockStations);
    });
  });
});
