const { Client } = require('@googlemaps/google-maps-services-js');
const logger = require('../utils/logger');

class GoogleMapsService {
  constructor() {
    this.client = new Client({});
    this.apiKey = process.env.GOOGLE_MAPS_API_KEY;
    
    if (!this.apiKey) {
      logger.error('Google Maps API key not configured in environment variables');
    } else {
      logger.info('Google Maps service initialized');
    }
  }

  /**
   * Get autocomplete predictions for places
   */
  async getPlacesAutocomplete(input, options = {}) {
    if (!this.apiKey) {
      throw new Error('Google Maps API key not configured');
    }

    if (!input || input.trim().length < 2) {
      return { predictions: [] };
    }

    try {
      const params = {
        key: this.apiKey,
        input: input.trim(),
        types: options.types || 'address',
        language: options.language || 'en',
        ...options
      };

      // Add session token if provided
      if (options.sessionToken) {
        params.sessiontoken = options.sessionToken;
      }

      // Add component restrictions (e.g., country)
      if (options.componentRestrictions) {
        params.components = Object.entries(options.componentRestrictions)
          .map(([key, value]) => `${key}:${value}`)
          .join('|');
      }

      const response = await this.client.placeAutocomplete({
        params,
        timeout: 5000
      });

      if (response.data.status === 'OK') {
        return {
          predictions: response.data.predictions.map(prediction => ({
            placeId: prediction.place_id,
            description: prediction.description,
            types: prediction.types,
            mainText: prediction.structured_formatting.main_text,
            secondaryText: prediction.structured_formatting.secondary_text || ''
          }))
        };
      } else {
        logger.error('Places Autocomplete API error', { status: response.data.status, errorMessage: response.data.error_message });
        return {
          predictions: [],
          error: this.getErrorMessage(response.data.status),
          status: response.data.status
        };
      }
    } catch (error) {
      logger.error('Places Autocomplete service error', { error });
      throw new Error('Failed to fetch place predictions');
    }
  }

  /**
   * Get detailed information about a place
   */
  async getPlaceDetails(placeId, options = {}) {
    if (!this.apiKey) {
      throw new Error('Google Maps API key not configured');
    }

    if (!placeId) {
      throw new Error('Place ID is required');
    }

    try {
      const params = {
        key: this.apiKey,
        place_id: placeId,
        fields: [
          'address_components',
          'formatted_address',
          'geometry',
          'place_id'
        ],
        language: options.language || 'en'
      };

      // Add session token if provided
      if (options.sessionToken) {
        params.sessiontoken = options.sessionToken;
      }

      const response = await this.client.placeDetails({
        params,
        timeout: 5000
      });

      if (response.data.status === 'OK' && response.data.result) {
        const place = response.data.result;
        const addressComponents = {};

        // Parse address components
        if (place.address_components) {
          place.address_components.forEach(component => {
            const types = component.types;
            
            if (types.includes('street_number')) {
              addressComponents.streetNumber = component.long_name;
            } else if (types.includes('route')) {
              addressComponents.route = component.long_name;
            } else if (types.includes('locality')) {
              addressComponents.locality = component.long_name;
            } else if (types.includes('administrative_area_level_1')) {
              addressComponents.administrativeAreaLevel1 = component.short_name;
              addressComponents.administrativeAreaLevel1Long = component.long_name;
            } else if (types.includes('postal_code')) {
              addressComponents.postalCode = component.long_name;
            } else if (types.includes('country')) {
              addressComponents.country = component.short_name;
            }
          });
        }

        return {
          placeId: place.place_id,
          formattedAddress: place.formatted_address,
          addressComponents,
          geometry: {
            latitude: place.geometry?.location?.lat || 0,
            longitude: place.geometry?.location?.lng || 0
          }
        };
      } else {
        logger.error('Place Details API error', { status: response.data.status, errorMessage: response.data.error_message });
        throw new Error(this.getErrorMessage(response.data.status));
      }
    } catch (error) {
      logger.error('Place Details service error', { error });
      throw error;
    }
  }

  /**
   * Geocode an address to get coordinates
   */
  async geocodeAddress(address, options = {}) {
    if (!this.apiKey) {
      throw new Error('Google Maps API key not configured');
    }

    if (!address || !address.trim()) {
      throw new Error('Address is required for geocoding');
    }

    try {
      const params = {
        key: this.apiKey,
        address: address.trim(),
        language: options.language || 'en'
      };

      // Add component restrictions (e.g., country)
      if (options.componentRestrictions) {
        params.components = Object.entries(options.componentRestrictions)
          .map(([key, value]) => `${key}:${value}`)
          .join('|');
      }

      // Add bounds if provided
      if (options.bounds) {
        params.bounds = options.bounds;
      }

      const response = await this.client.geocode({
        params,
        timeout: 5000
      });

      if (response.data.status === 'OK' && response.data.results.length > 0) {
        const result = response.data.results[0];
        
        return {
          latitude: result.geometry.location.lat,
          longitude: result.geometry.location.lng,
          formattedAddress: result.formatted_address,
          placeId: result.place_id
        };
      } else {
        logger.error('Geocoding API error', { status: response.data.status, errorMessage: response.data.error_message });
        return {
          error: this.getErrorMessage(response.data.status),
          status: response.data.status
        };
      }
    } catch (error) {
      logger.error('Geocoding service error', { error });
      throw new Error('Failed to geocode address');
    }
  }

  /**
   * Get human-readable error message for Google Maps API status codes
   */
  getErrorMessage(status) {
    const errorMessages = {
      'ZERO_RESULTS': 'No results found for this query',
      'OVER_QUERY_LIMIT': 'API quota exceeded. Please try again later',
      'REQUEST_DENIED': 'API request denied. Check API key configuration',
      'INVALID_REQUEST': 'Invalid request parameters',
      'UNKNOWN_ERROR': 'Server error. Please try again',
      'NOT_FOUND': 'The specified place was not found'
    };

    return errorMessages[status] || `Google Maps API error: ${status}`;
  }


  /**
   * Check if the service is properly configured
   */
  isConfigured() {
    return !!this.apiKey;
  }
}

// Export singleton instance
module.exports = new GoogleMapsService();