const axios = require('axios');
const Setting = require('../models/setting.model');
const ErrorHandler = require('../utils/ErrorHandler');

/**
 * Map Service
 * Handles Geocoding and Distance calculations using Google Maps API
 */

/**
 * Get coordinates (lat, lng) for a given address or city name
 * @param {string} address - The address or city name to geocode
 * @returns {Promise<{lat: number, lng: number}>}
 */
exports.getCoordinates = async (address) => {
    try {
        const settings = await Setting.getSettingsWithDecryption();
        const apiKey = settings.googleMap?.unrestrictedKey;
        let coords = { lat: 0, lng: 0 };

        // 1. Try Google Maps if key exists
        if (apiKey) {
            try {
                const response = await axios.get('https://maps.googleapis.com/maps/api/geocode/json', {
                    params: { address: address, key: apiKey }
                });
                if (response.data.status === 'OK' && response.data.results.length > 0) {
                    const { lat, lng } = response.data.results[0].geometry.location;
                    return { lat, lng };
                }
            } catch (googleErr) {
                console.warn('Google Maps Geocoding failed, trying fallback...', googleErr.message);
            }
        }

        // 2. Fallback to OpenStreetMap (Nominatim)
        console.log(`Using Nominatim fallback for: ${address}`);
        const nominatimResponse = await axios.get('https://nominatim.openstreetmap.org/search', {
            params: {
                q: address,
                format: 'json',
                limit: 1
            },
            headers: {
                'User-Agent': 'CabBookingApp/1.0'
            }
        });

        if (nominatimResponse.data && nominatimResponse.data.length > 0) {
            return {
                lat: parseFloat(nominatimResponse.data[0].lat),
                lng: parseFloat(nominatimResponse.data[0].lon)
            };
        }

        console.warn(`All geocoding providers failed for "${address}"`);
        return { lat: 0, lng: 0 };

    } catch (error) {
        console.error('Error in getCoordinates:', error.message);
        return { lat: 0, lng: 0 };
    }
};

/**
 * Get distance and duration between two points using Google Routes API
 * @param {string|object} origin - Origin address string or {lat, lng}
 * @param {string|object} destination - Destination address string or {lat, lng}
 * @returns {Promise<{distance: number, duration: number}>}
 */
exports.getDistanceAndDuration = async (origin, destination) => {
    try {
        const settings = await Setting.getSettingsWithDecryption();
        const apiKey = settings.googleMap?.unrestrictedKey;


        if (!apiKey) {
            console.warn('Google Maps API key not configured in settings');
            return { distance: 0, duration: 0 };
        }

        // Parse coordinates
        const originLat = typeof origin === 'object' ? origin.lat : parseFloat(origin.split(',')[0]);
        const originLng = typeof origin === 'object' ? origin.lng : parseFloat(origin.split(',')[1]);
        const destLat = typeof destination === 'object' ? destination.lat : parseFloat(destination.split(',')[0]);
        const destLng = typeof destination === 'object' ? destination.lng : parseFloat(destination.split(',')[1]);

        // Use Google Routes API (new API)
        const requestBody = {
            origin: {
                location: {
                    latLng: {
                        latitude: originLat,
                        longitude: originLng
                    }
                }
            },
            destination: {
                location: {
                    latLng: {
                        latitude: destLat,
                        longitude: destLng
                    }
                }
            },
            travelMode: "DRIVE",
            routingPreference: "TRAFFIC_AWARE",
            computeAlternativeRoutes: false,
            languageCode: "en-US",
            units: "METRIC"
        };

        const response = await axios.post(
            `https://maps.mapthrust.io/directions/v2:computeRoutes`,
            requestBody,
            {
                headers: {
                    'Content-Type': 'application/json',
                    'X-Goog-Api-Key': apiKey,
                    'X-Goog-FieldMask': 'routes.duration,routes.distanceMeters'
                }
            }
        );

        // console.log("Routes API response:", JSON.stringify(response.data, null, 2));

        if (response.data && response.data.routes && response.data.routes.length > 0) {
            const route = response.data.routes[0];
            const distanceMeters = route.distanceMeters || 0;
            // Duration is returned as a string like "1234s" (seconds)
            const durationStr = route.duration || "0s";
            const durationSeconds = parseInt(durationStr.replace('s', '')) || 0;

            return {
                distance: Math.round(distanceMeters / 1000), // convert to km
                duration: Math.round(durationSeconds / 60)   // convert to minutes
            };
        }

        console.warn('Routes API returned no routes');
        return { distance: 0, duration: 0 };
    } catch (error) {
        console.error('Error in getDistanceAndDuration:', error.response?.data || error.message);

        // If Routes API fails, try fallback to OSRM (Open Source Routing Machine)
        try {
            console.log('Trying OSRM fallback...');
            const originLat = typeof origin === 'object' ? origin.lat : parseFloat(origin.split(',')[0]);
            const originLng = typeof origin === 'object' ? origin.lng : parseFloat(origin.split(',')[1]);
            const destLat = typeof destination === 'object' ? destination.lat : parseFloat(destination.split(',')[0]);
            const destLng = typeof destination === 'object' ? destination.lng : parseFloat(destination.split(',')[1]);

            const osrmResponse = await axios.get(
                `https://router.project-osrm.org/route/v1/driving/${originLng},${originLat};${destLng},${destLat}`,
                { params: { overview: false } }
            );

            if (osrmResponse.data && osrmResponse.data.routes && osrmResponse.data.routes.length > 0) {
                const route = osrmResponse.data.routes[0];
                return {
                    distance: Math.round(route.distance / 1000), // convert to km
                    duration: Math.round(route.duration / 60)    // convert to minutes
                };
            }
        } catch (osrmError) {
            console.error('OSRM fallback also failed:', osrmError.message);
        }

        return { distance: 0, duration: 0 };
    }
};
