# frozen_string_literal: true

require 'rails_helper'

RSpec.describe LocationSearch::GeocodingService do
  let(:query) { 'Kaufland Berlin' }
  let(:service) { described_class.new(query) }

  describe '#search' do
    context 'with valid query' do
      let(:mock_geocoder_result) do
        double(
          'Geocoder::Result',
          latitude: 52.5200,
          longitude: 13.4050,
          address: 'Kaufland, Alexanderplatz 1, Berlin',
          data: {
            'type' => 'shop',
            'osm_id' => '12345',
            'place_rank' => 30,
            'importance' => 0.8
          }
        )
      end

      before do
        allow(Geocoder).to receive(:search).and_return([mock_geocoder_result])
        allow(Geocoder.config).to receive(:lookup).and_return(:photon)
      end

      it 'returns normalized geocoding results' do
        results = service.search

        expect(results).to be_an(Array)
        expect(results.first).to include(
          lat: 52.5200,
          lon: 13.4050,
          name: 'Kaufland',
          address: 'Kaufland, Alexanderplatz 1, Berlin',
          type: 'shop'
        )
      end

      it 'includes provider data' do
        results = service.search

        expect(results.first[:provider_data]).to include(
          osm_id: '12345',
          place_rank: 30,
          importance: 0.8
        )
      end

      it 'limits results to MAX_RESULTS' do
        expect(Geocoder).to receive(:search).with(query, limit: 10)

        service.search
      end
    end

    context 'with blank query' do
      let(:service) { described_class.new('') }
      
      it 'returns empty array' do
        expect(service.search).to eq([])
      end
    end

    context 'when Geocoder returns no results' do
      before do
        allow(Geocoder).to receive(:search).and_return([])
      end

      it 'returns empty array' do
        expect(service.search).to eq([])
      end
    end

    context 'when Geocoder raises an error' do
      before do
        allow(Geocoder).to receive(:search).and_raise(StandardError.new('Geocoding error'))
      end

      it 'handles error gracefully and returns empty array' do
        expect(service.search).to eq([])
      end
    end

    context 'with invalid coordinates' do
      let(:invalid_result) do
        double(
          'Geocoder::Result',
          latitude: 91.0, # Invalid latitude
          longitude: 13.4050,
          address: 'Invalid location',
          data: {}
        )
      end

      let(:valid_result) do
        double(
          'Geocoder::Result',
          latitude: 52.5200,
          longitude: 13.4050,
          address: 'Valid location',
          data: {}
        )
      end

      before do
        allow(Geocoder).to receive(:search).and_return([invalid_result, valid_result])
      end

      it 'filters out results with invalid coordinates' do
        results = service.search

        expect(results.length).to eq(1)
        expect(results.first[:lat]).to eq(52.5200)
      end
    end

    describe '#deduplicate_results' do
      let(:duplicate_results) do
        [
          {
            lat: 52.5200,
            lon: 13.4050,
            name: 'Location 1',
            address: 'Address 1',
            type: 'shop',
            provider_data: {}
          },
          {
            lat: 52.5201, # Within 100m of first location
            lon: 13.4051,
            name: 'Location 2',
            address: 'Address 2',
            type: 'shop',
            provider_data: {}
          }
        ]
      end

      let(:mock_results) do
        duplicate_results.map do |result|
          double(
            'Geocoder::Result',
            latitude: result[:lat],
            longitude: result[:lon],
            address: result[:address],
            data: { 'type' => result[:type] }
          )
        end
      end

      before do
        allow(Geocoder).to receive(:search).and_return(mock_results)
      end

      it 'removes locations within 100m of each other' do
        service = described_class.new('test')
        results = service.search

        expect(results.length).to eq(1)
        expect(results.first[:name]).to eq('Address 1')
      end
    end
  end

  describe '#provider_name' do
    before do
      allow(Geocoder.config).to receive(:lookup).and_return(:nominatim)
    end

    it 'returns the current geocoding provider name' do
      expect(service.provider_name).to eq('Nominatim')
    end
  end
end