import axios from 'axios';
import { googleMapsConfig } from '../config/googleMaps';

/**
 * Calculate distance using Google Maps Distance Matrix API
 * @param lat1 Latitude of first point
 * @param lng1 Longitude of first point
 * @param lat2 Latitude of second point
 * @param lng2 Longitude of second point
 * @returns Distance in kilometers and duration in seconds
 */
export const calculateDistanceWithGoogleMaps = async (
    lat1: number,
    lng1: number,
    lat2: number,
    lng2: number
): Promise<{ distance: number; duration: number; status: string }> => {
    try {
        const origins = `${lat1},${lng1}`;
        const destinations = `${lat2},${lng2}`;
        
        const response = await axios.get(`${googleMapsConfig.baseUrl}/distancematrix/json`, {
            params: {
                origins,
                destinations,
                key: googleMapsConfig.apiKey,
                units: 'metric', // Get distance in kilometers
            },
        });

        if (response.data.status === 'OK' && response.data.rows[0]?.elements[0]?.status === 'OK') {
            const element = response.data.rows[0].elements[0];
            const distanceInMeters = element.distance.value;
            const durationInSeconds = element.duration.value;
            const distanceInKm = Math.round((distanceInMeters / 1000) * 100) / 100;
            
            return {
                distance: distanceInKm,
                duration: durationInSeconds,
                status: 'OK',
            };
        } else {
            throw new Error('Google Maps API returned no results');
        }
    } catch (error) {
        console.error('Error calling Google Maps API:', error);
        throw error;
    }
};

/**
 * Calculate distance between two points using Haversine formula (Fallback method)
 * @param lat1 Latitude of first point
 * @param lng1 Longitude of first point
 * @param lat2 Latitude of second point
 * @param lng2 Longitude of second point
 * @returns Distance in kilometers
 */
export const calculateDistanceHaversine = (
    lat1: number,
    lng1: number,
    lat2: number,
    lng2: number
): number => {
    const R = 6371; // Earth's radius in kilometers
    const dLat = (lat2 - lat1) * (Math.PI / 180);
    const dLng = (lng2 - lng1) * (Math.PI / 180);
    const a =
        Math.sin(dLat / 2) * Math.sin(dLat / 2) +
        Math.cos(lat1 * (Math.PI / 180)) *
            Math.cos(lat2 * (Math.PI / 180)) *
            Math.sin(dLng / 2) *
            Math.sin(dLng / 2);
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    const distance = R * c;
    return Math.round(distance * 100) / 100; // Round to 2 decimal places
};

/**
 * Calculate mileage based on vehicle MPG and distance
 * @param vehicleMPG Vehicle's miles per gallon
 * @param distance Distance in kilometers
 * @returns Mileage string
 */
export const calculateMileage = (vehicleMPG: string, distance: number): string => {
    
    const mpg = parseFloat(vehicleMPG) || 20; // Default to 20 MPG if not available
    const distanceInMiles = distance * 0.621371; // Convert km to miles
    const calculatedMileage = (distanceInMiles / mpg).toFixed(1);
    return `${calculatedMileage} Gallons`;
};

/**
 * Calculate gas required based on distance and vehicle MPG
 * @param vehicleMPG Vehicle's miles per gallon
 * @param distance Distance in kilometers
 * @returns Gas required string
 */
export const calculateGasRequired = (vehicleMPG: string, distance: number): string => {
    const mpg = parseFloat(vehicleMPG) || 20; // Default to 20 MPG if not available
    const distanceInMiles = distance * 0.621371; // Convert km to miles
    const gasRequired = (distanceInMiles / mpg).toFixed(1);
    return `${gasRequired} Gallons`;
};

/**
 * Format distance for display
 * @param distance Distance in kilometers
 * @returns Formatted distance string
 */
export const formatDistance = (distance: number): string => {
    const miles = distance * 0.621371;
    return `${Math.round(miles)} Miles`;
};

/**
 * Calculate all trip metrics based on coordinates and vehicle
 * Uses Google Maps Distance Matrix API with Haversine fallback
 * @param startLat Start latitude
 * @param startLng Start longitude
 * @param endLat End latitude
 * @param endLng End longitude
 * @param vehicleMPG Vehicle's MPG
 * @returns Object with calculated metrics
 */
export const calculateTripMetrics = async (
    startLat: number,
    startLng: number,
    endLat: number,
    endLng: number,
    vehicleMPG: string
) => {
    let distance: number;
    let duration: number = 0;
    let source: string = 'haversine';

    try {
        // Try Google Maps API first
        const googleMapsResult = await calculateDistanceWithGoogleMaps(
            startLat,
            startLng,
            endLat,
            endLng
        );
        distance = googleMapsResult.distance;
        duration = googleMapsResult.duration;
        source = 'google_maps';
        console.log('✅ Using Google Maps Distance Matrix API');
    } catch (error) {
        // Fallback to Haversine formula
        console.warn('⚠️ Google Maps API failed, using Haversine formula as fallback');
        distance = calculateDistanceHaversine(startLat, startLng, endLat, endLng);
    }

    const mileage = calculateMileage(vehicleMPG, distance);
    const gasRequired = calculateGasRequired(vehicleMPG, distance);
    const formattedDistance = formatDistance(distance);

    return {
        calculatedDistance: distance,
        calculatedMileage: mileage,
        calculatedGasRequired: gasRequired,
        formattedDistance: formattedDistance,
        duration: duration,
        durationFormatted: formatDuration(duration),
        source: source,
    };
};

/**
 * Format duration in seconds to readable format
 * @param seconds Duration in seconds
 * @returns Formatted duration string
 */
export const formatDuration = (seconds: number): string => {
    if (seconds === 0) return 'N/A';
    const hours = Math.floor(seconds / 3600);
    const minutes = Math.floor((seconds % 3600) / 60);
    if (hours > 0) {
        return `${hours}h ${minutes}m`;
    }
    return `${minutes}m`;
}; 