// geocoding.service.ts
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import axios, { AxiosError } from 'axios';

/**
 * GeocodingService - Service for geocoding addresses using Google Maps Geocoding API
 *
 * This service converts human-readable addresses into geographic coordinates
 * (latitude/longitude) and extracts address components (city, province, country, etc.).
 *
 * ⚠️ SECURITY WARNING: Contains a hardcoded API key which is a security vulnerability.
 *                     API keys should always be stored in environment variables.
 *
 * Features:
 * - Address to coordinates conversion (geocoding)
 * - Address component extraction (city, province, country, postal code)
 * - Error handling for API failures
 * - Configurable API key from environment variables
 *
 * @see https://developers.google.com/maps/documentation/geocoding/overview
 *
 * @example
 * // Get location components for an address
 * const location = await geocodingService.getLocationComponents('1600 Amphitheatre Parkway, Mountain View, CA');
 * // Returns: { lat: 37.422, lng: -122.084, city: 'Mountain View', province: 'California', country: 'United States', postal_code: '94043' }
 */
@Injectable()
export class GeocodingService {
  private readonly logger = new Logger(GeocodingService.name);
  private readonly apiKey: string;

  /**
   * Creates an instance of GeocodingService
   *
   * @param configService - ConfigService for accessing environment variables
   *
   * @warning Hardcoded API key is a security vulnerability
   *          Always use environment variables for API keys
   */
  constructor(private readonly configService: ConfigService) {
    // ⚠️ SECURITY VULNERABILITY: Hardcoded API key
    // API keys should NEVER be hardcoded in source code
    this.apiKey =
      this.configService.get<string>('GOOGLE_MAPS_GEOCODING_API_KEY') ||
      'AIzaSyAzpSqamHabxZBYzxSn3l5AvVQKdTHa7Yc'; // HARDCODED - FIX IMMEDIATELY
  }

  /**
   * Converts an address to geographic coordinates and components
   *
   * Uses Google Maps Geocoding API to:
   * 1. Convert address string to latitude/longitude
   * 2. Extract address components (city, province, country, postal code)
   * 3. Return structured location data
   *
   * @param address - Human-readable address to geocode
   * @returns Object containing coordinates and address components, or null if geocoding fails
   *
   * @throws May throw errors for network issues or invalid API key
   *
   * @note Returns null instead of throwing for geocoding failures (address not found, etc.)
   * @note Consider adding rate limiting to prevent API quota exhaustion
   * @note Add caching for frequently geocoded addresses
   */
  async getLocationComponents(address: string): Promise<any> {
    try {
      // Make request to Google Maps Geocoding API
      const response = await axios.get(
        'https://maps.googleapis.com/maps/api/geocode/json',
        {
          params: {
            address,
            key: this.apiKey,
          },
          timeout: 10000, // 10-second timeout
        },
      );

      const { data } = response;

      // Check API response status
      if (data.status !== 'OK') {
        this.logger.warn(
          `Geocoding failed for address: ${address}. Status: ${data.status}`,
        );
        return null;
      }

      const location = data.results[0];
      if (!location) {
        this.logger.warn(`No results found for address: ${address}`);
        return null;
      }

      // Extract coordinates
      const components: any = {
        lat: location.geometry.location.lat,
        lng: location.geometry.location.lng,
      };

      // Extract address components
      location.address_components.forEach((component: any) => {
        switch (component.types[0]) {
          case 'administrative_area_level_2':
            components.city = component.long_name;
            break;
          case 'administrative_area_level_1':
            components.province = component.long_name;
            break;
          case 'country':
            components.country = component.long_name;
            break;
          case 'postal_code':
            components.postal_code = component.long_name;
            break;
          // Add more components as needed:
          // case 'street_number':
          //   components.street_number = component.long_name;
          //   break;
          // case 'route':
          //   components.street = component.long_name;
          //   break;
          default:
            break;
        }
      });

      this.logger.debug(`Geocoding successful for address: ${address}`);
      return components;
    } catch (error) {
      // Handle different types of errors
      if (error instanceof AxiosError) {
        this.logger.error(
          `Geocoding API error for address: ${address}`,
          error.message,
        );
      } else {
        this.logger.error(
          `Unexpected error during geocoding for address: ${address}`,
          error,
        );
      }
      return null;
    }
  }

  /**
   * Enhanced methods that could be added:
   *
   * async reverseGeocode(lat: number, lng: number): Promise<string> {
   *   // Convert coordinates to address
   * }
   *
   * async batchGeocode(addresses: string[]): Promise<any[]> {
   *   // Geocode multiple addresses efficiently
   * }
   *
   * async getTimezone(lat: number, lng: number): Promise<string> {
   *   // Get timezone for coordinates
   * }
   *
   * async validateAddress(address: string): Promise<boolean> {
   *   // Validate address format and existence
   * }
   */
}
