import { renderHook } from '@testing-library/react';
import { useAddressAutocomplete, usStates } from '../../hooks/useAddressAutocomplete';
import { PlaceDetails } from '../../services/placesService';

describe('useAddressAutocomplete', () => {
  describe('usStates', () => {
    it('contains all 50 US states', () => {
      expect(usStates).toHaveLength(50);
    });

    it('includes common states', () => {
      expect(usStates).toContain('California');
      expect(usStates).toContain('New York');
      expect(usStates).toContain('Texas');
      expect(usStates).toContain('Florida');
    });

    it('states are in alphabetical order', () => {
      const sorted = [...usStates].sort();
      expect(usStates).toEqual(sorted);
    });
  });

  describe('parsePlace', () => {
    const { result } = renderHook(() => useAddressAutocomplete());

    it('parses a complete place correctly', () => {
      const place: PlaceDetails = {
        formattedAddress: '123 Main Street, Los Angeles, CA 90210, USA',
        addressComponents: {
          streetNumber: '123',
          route: 'Main Street',
          locality: 'Los Angeles',
          administrativeAreaLevel1: 'CA',
          administrativeAreaLevel1Long: 'California',
          postalCode: '90210',
          country: 'US',
        },
        geometry: {
          latitude: 34.0522,
          longitude: -118.2437,
        },
        placeId: 'test-place-id',
      };

      const parsed = result.current.parsePlace(place);

      expect(parsed).not.toBeNull();
      expect(parsed!.address1).toBe('123 Main Street');
      expect(parsed!.city).toBe('Los Angeles');
      expect(parsed!.state).toBe('California');
      expect(parsed!.zipCode).toBe('90210');
      expect(parsed!.country).toBe('US');
      expect(parsed!.latitude).toBe(34.0522);
      expect(parsed!.longitude).toBe(-118.2437);
    });

    it('converts state codes to full names', () => {
      const place: PlaceDetails = {
        formattedAddress: '456 Oak Ave, New York, NY 10001, USA',
        addressComponents: {
          streetNumber: '456',
          route: 'Oak Ave',
          locality: 'New York',
          administrativeAreaLevel1: 'NY',
          administrativeAreaLevel1Long: 'New York',
          postalCode: '10001',
          country: 'US',
        },
        geometry: {
          latitude: 40.7128,
          longitude: -74.006,
        },
        placeId: 'test-place-id-2',
      };

      const parsed = result.current.parsePlace(place);

      expect(parsed).not.toBeNull();
      expect(parsed!.state).toBe('New York');
    });

    it('uses formatted address when street number and route are missing', () => {
      const place: PlaceDetails = {
        formattedAddress: 'Some Place, Austin, TX 78701, USA',
        addressComponents: {
          locality: 'Austin',
          administrativeAreaLevel1: 'TX',
          administrativeAreaLevel1Long: 'Texas',
          postalCode: '78701',
          country: 'US',
        },
        geometry: {
          latitude: 30.2672,
          longitude: -97.7431,
        },
        placeId: 'test-place-id-3',
      };

      const parsed = result.current.parsePlace(place);

      expect(parsed).not.toBeNull();
      expect(parsed!.address1).toBe('Some Place, Austin, TX 78701, USA');
    });

    it('handles missing postal code gracefully', () => {
      const place: PlaceDetails = {
        formattedAddress: '789 Pine St, Seattle, WA, USA',
        addressComponents: {
          streetNumber: '789',
          route: 'Pine St',
          locality: 'Seattle',
          administrativeAreaLevel1: 'WA',
          administrativeAreaLevel1Long: 'Washington',
          country: 'US',
        },
        geometry: {
          latitude: 47.6062,
          longitude: -122.3321,
        },
        placeId: 'test-place-id-4',
      };

      const parsed = result.current.parsePlace(place);

      expect(parsed).not.toBeNull();
      expect(parsed!.zipCode).toBe('');
      expect(parsed!.state).toBe('Washington');
    });

    it('handles missing city gracefully', () => {
      const place: PlaceDetails = {
        formattedAddress: '100 Rural Road, CO 80000, USA',
        addressComponents: {
          streetNumber: '100',
          route: 'Rural Road',
          administrativeAreaLevel1: 'CO',
          administrativeAreaLevel1Long: 'Colorado',
          postalCode: '80000',
          country: 'US',
        },
        geometry: {
          latitude: 39.5501,
          longitude: -105.7821,
        },
        placeId: 'test-place-id-5',
      };

      const parsed = result.current.parsePlace(place);

      expect(parsed).not.toBeNull();
      expect(parsed!.city).toBe('');
    });

    it('sets state to empty string for unknown states', () => {
      const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();

      const place: PlaceDetails = {
        formattedAddress: '123 Street, City, XX 12345, USA',
        addressComponents: {
          streetNumber: '123',
          route: 'Street',
          locality: 'City',
          administrativeAreaLevel1: 'XX',
          administrativeAreaLevel1Long: 'Unknown State',
          postalCode: '12345',
          country: 'US',
        },
        geometry: {
          latitude: 0,
          longitude: 0,
        },
        placeId: 'test-place-id-6',
      };

      const parsed = result.current.parsePlace(place);

      expect(parsed).not.toBeNull();
      expect(parsed!.state).toBe('');
      expect(consoleSpy).toHaveBeenCalledWith(
        expect.stringContaining('State not found in dropdown options')
      );

      consoleSpy.mockRestore();
    });

    it('handles DC (District of Columbia) correctly', () => {
      const place: PlaceDetails = {
        formattedAddress: '1600 Pennsylvania Ave, Washington, DC 20500, USA',
        addressComponents: {
          streetNumber: '1600',
          route: 'Pennsylvania Ave',
          locality: 'Washington',
          administrativeAreaLevel1: 'DC',
          administrativeAreaLevel1Long: 'District of Columbia',
          postalCode: '20500',
          country: 'US',
        },
        geometry: {
          latitude: 38.8977,
          longitude: -77.0365,
        },
        placeId: 'test-place-id-dc',
      };

      const parsed = result.current.parsePlace(place);

      // DC is not in the 50 states list, so state should be empty
      expect(parsed).not.toBeNull();
      expect(parsed!.city).toBe('Washington');
    });

    it('uses long state name from administrativeAreaLevel1Long when available', () => {
      const place: PlaceDetails = {
        formattedAddress: '500 Beach Blvd, Miami, FL 33101, USA',
        addressComponents: {
          streetNumber: '500',
          route: 'Beach Blvd',
          locality: 'Miami',
          administrativeAreaLevel1: 'FL',
          administrativeAreaLevel1Long: 'Florida',
          postalCode: '33101',
          country: 'US',
        },
        geometry: {
          latitude: 25.7617,
          longitude: -80.1918,
        },
        placeId: 'test-place-id-fl',
      };

      const parsed = result.current.parsePlace(place);

      expect(parsed).not.toBeNull();
      expect(parsed!.state).toBe('Florida');
    });

    it('returns null and logs error when parsing fails', () => {
      const consoleSpy = jest.spyOn(console, 'error').mockImplementation();

      // Pass an object that will cause an error when accessing nested properties
      const invalidPlace = {
        formattedAddress: 'Test',
        addressComponents: null,
        geometry: { latitude: 0, longitude: 0 },
        placeId: 'test',
      } as unknown as PlaceDetails;

      const parsed = result.current.parsePlace(invalidPlace);

      expect(parsed).toBeNull();
      expect(consoleSpy).toHaveBeenCalledWith(
        'Error parsing place details:',
        expect.any(Error)
      );

      consoleSpy.mockRestore();
    });

    it('handles only street number without route', () => {
      const place: PlaceDetails = {
        formattedAddress: '42, Chicago, IL 60601, USA',
        addressComponents: {
          streetNumber: '42',
          locality: 'Chicago',
          administrativeAreaLevel1: 'IL',
          administrativeAreaLevel1Long: 'Illinois',
          postalCode: '60601',
          country: 'US',
        },
        geometry: {
          latitude: 41.8781,
          longitude: -87.6298,
        },
        placeId: 'test-place-id-number-only',
      };

      const parsed = result.current.parsePlace(place);

      expect(parsed).not.toBeNull();
      expect(parsed!.address1).toBe('42');
    });

    it('handles only route without street number', () => {
      const place: PlaceDetails = {
        formattedAddress: 'Main Street, Boston, MA 02101, USA',
        addressComponents: {
          route: 'Main Street',
          locality: 'Boston',
          administrativeAreaLevel1: 'MA',
          administrativeAreaLevel1Long: 'Massachusetts',
          postalCode: '02101',
          country: 'US',
        },
        geometry: {
          latitude: 42.3601,
          longitude: -71.0589,
        },
        placeId: 'test-place-id-route-only',
      };

      const parsed = result.current.parsePlace(place);

      expect(parsed).not.toBeNull();
      expect(parsed!.address1).toBe('Main Street');
    });

    it('defaults country to US when not provided', () => {
      const place: PlaceDetails = {
        formattedAddress: '999 Test St, Denver, CO 80202',
        addressComponents: {
          streetNumber: '999',
          route: 'Test St',
          locality: 'Denver',
          administrativeAreaLevel1: 'CO',
          administrativeAreaLevel1Long: 'Colorado',
          postalCode: '80202',
        },
        geometry: {
          latitude: 39.7392,
          longitude: -104.9903,
        },
        placeId: 'test-place-id-no-country',
      };

      const parsed = result.current.parsePlace(place);

      expect(parsed).not.toBeNull();
      expect(parsed!.country).toBe('US');
    });
  });

  describe('hook stability', () => {
    it('returns stable parsePlace function', () => {
      const { result, rerender } = renderHook(() => useAddressAutocomplete());

      const firstParsePlace = result.current.parsePlace;

      rerender();

      const secondParsePlace = result.current.parsePlace;

      expect(firstParsePlace).toBe(secondParsePlace);
    });
  });
});
