/**
 * @ai-summary Unit tests for Google Maps client
 */

import axios from 'axios';
import { GoogleMapsClient } from '../../external/google-maps/google-maps.client';
import {
  mockGoogleNearbySearchResponse,
  mockGoogleErrorResponse,
  mockGoogleApiErrorResponse
} from '../fixtures/mock-google-response';
import { searchCoordinates } from '../fixtures/mock-stations';

jest.mock('axios');
jest.mock('../../../../core/config/redis');
jest.mock('../../../../core/logging/logger');

describe('GoogleMapsClient', () => {
  let client: GoogleMapsClient;
  let mockAxios: jest.Mocked<typeof axios>;

  beforeEach(() => {
    jest.clearAllMocks();
    mockAxios = axios as jest.Mocked<typeof axios>;
    client = new GoogleMapsClient();
  });

  describe('searchNearbyStations', () => {
    it('should search for nearby gas stations', async () => {
      mockAxios.get.mockResolvedValue({
        data: mockGoogleNearbySearchResponse
      });

      const result = await client.searchNearbyStations(
        searchCoordinates.sanFrancisco.latitude,
        searchCoordinates.sanFrancisco.longitude,
        5000
      );

      expect(result).toHaveLength(2);
      expect(result[0]?.name).toBe('Shell Gas Station - Downtown');
      expect(mockAxios.get).toHaveBeenCalled();
    });

    it('should handle API errors gracefully', async () => {
      mockAxios.get.mockResolvedValue({
        data: mockGoogleApiErrorResponse
      });

      const result = await client.searchNearbyStations(
        searchCoordinates.sanFrancisco.latitude,
        searchCoordinates.sanFrancisco.longitude
      );

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

    it('should handle zero results', async () => {
      mockAxios.get.mockResolvedValue({
        data: mockGoogleErrorResponse
      });

      const result = await client.searchNearbyStations(
        searchCoordinates.seattle.latitude,
        searchCoordinates.seattle.longitude
      );

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

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

      const result = await client.searchNearbyStations(
        searchCoordinates.sanFrancisco.latitude,
        searchCoordinates.sanFrancisco.longitude
      );

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

    it('should calculate distance from reference point', async () => {
      mockAxios.get.mockResolvedValue({
        data: mockGoogleNearbySearchResponse
      });

      const result = await client.searchNearbyStations(
        searchCoordinates.sanFrancisco.latitude,
        searchCoordinates.sanFrancisco.longitude
      );

      expect(result[0]?.distance).toBeGreaterThan(0);
    });

    it('should format API response correctly', async () => {
      mockAxios.get.mockResolvedValue({
        data: mockGoogleNearbySearchResponse
      });

      const result = await client.searchNearbyStations(
        searchCoordinates.sanFrancisco.latitude,
        searchCoordinates.sanFrancisco.longitude
      );

      expect(result[0]).toHaveProperty('placeId');
      expect(result[0]).toHaveProperty('name');
      expect(result[0]).toHaveProperty('address');
      expect(result[0]).toHaveProperty('latitude');
      expect(result[0]).toHaveProperty('longitude');
      expect(result[0]).toHaveProperty('rating');
    });

    it('should respect custom radius parameter', async () => {
      mockAxios.get.mockResolvedValue({
        data: mockGoogleNearbySearchResponse
      });

      await client.searchNearbyStations(
        searchCoordinates.sanFrancisco.latitude,
        searchCoordinates.sanFrancisco.longitude,
        2000
      );

      const callArgs = mockAxios.get.mock.calls[0]?.[1];
      expect(callArgs?.params?.radius).toBe(2000);
    });
  });

  describe('caching', () => {
    it('should cache search results', async () => {
      mockAxios.get.mockResolvedValue({
        data: mockGoogleNearbySearchResponse
      });

      // First call should hit API
      const result1 = await client.searchNearbyStations(
        searchCoordinates.sanFrancisco.latitude,
        searchCoordinates.sanFrancisco.longitude
      );

      // Second call should return cached result
      const result2 = await client.searchNearbyStations(
        searchCoordinates.sanFrancisco.latitude,
        searchCoordinates.sanFrancisco.longitude
      );

      expect(result1).toEqual(result2);
    });

    it('should use different cache keys for different coordinates', async () => {
      mockAxios.get.mockResolvedValue({
        data: mockGoogleNearbySearchResponse
      });

      await client.searchNearbyStations(
        searchCoordinates.sanFrancisco.latitude,
        searchCoordinates.sanFrancisco.longitude
      );

      await client.searchNearbyStations(
        searchCoordinates.losAngeles.latitude,
        searchCoordinates.losAngeles.longitude
      );

      expect(mockAxios.get).toHaveBeenCalledTimes(2);
    });
  });
});
