import { ref, shallowRef } from 'vue';
import type { BusStop } from '@/types';
import { useMapState } from './useMapState';

// Fórmula Haversine para distancia en línea recta (km)
function getHaversineDistance(lat1: number, lon1: number, lat2: number, lon2: number): number {
    const R = 6371; // Radio de la Tierra en km
    const dLat = (lat2 - lat1) * (Math.PI / 180);
    const dLon = (lon2 - lon1) * (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(dLon / 2) * Math.sin(dLon / 2);
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    return R * c;
}

// ── SINGLETON: estado a nivel de módulo para que todas las instancias compartan
//    la misma referencia a la polilínea de caminata, igual que useMapState.
//    Sin esto, MapView limpia "su" instancia pero la polyline pertenece a la
//    instancia de useFlujoPrincipal → líneas punteadas que no desaparecen. ──
const paradaCercana = ref<BusStop | null>(null);
const distanciaMetros = ref<number>(0);
const duracionCaminata = ref<number>(0);
const caminandoPolyline = shallowRef<google.maps.Polyline | null>(null);

export function useParadaCercana() {
    const { registrarPolyline } = useMapState();

    const limpiarCaminata = () => {
        if (caminandoPolyline.value) {
            caminandoPolyline.value.setMap(null);
            caminandoPolyline.value = null;
        }
        paradaCercana.value = null;
        distanciaMetros.value = 0;
        duracionCaminata.value = 0;
    };

    const encontrarParadaCercana = async (
        ubicacionUsuario: { lat: number; lng: number },
        paradas: BusStop[],
        map: google.maps.Map | undefined,
        cancelToken?: { cancelled: boolean }
    ) => {
        if (!paradas || paradas.length === 0 || !ubicacionUsuario) return;
        limpiarCaminata();

        // 1. Pre-filtar (Haversine) - Top 5 más cercanas
        const paradasConDistLineal = paradas.map(p => ({
            parada: p,
            distancia: getHaversineDistance(ubicacionUsuario.lat, ubicacionUsuario.lng, p.latitude, p.longitude)
        }));

        paradasConDistLineal.sort((a, b) => a.distancia - b.distancia);
        const top5 = paradasConDistLineal.slice(0, 5).map(item => item.parada);

        // 2. Usar Routes API para encontrar la más cercana por calles reales
        let mejorParada: BusStop | null = null;
        let minimaDistanciaCalles = Infinity;
        let mejorDuracion = 0;
        let mejorRutaPuntos: google.maps.LatLng[] = [];

        try {
            const { DirectionsService } = await google.maps.importLibrary("routes") as any;
            const directionsService = new DirectionsService();

            const routePromises = top5.map(async (stop) => {
                try {
                    const response = await new Promise<google.maps.DirectionsResult>((resolve, reject) => {
                        directionsService.route({
                            origin: { lat: ubicacionUsuario.lat, lng: ubicacionUsuario.lng },
                            destination: { lat: stop.latitude, lng: stop.longitude },
                            travelMode: google.maps.TravelMode.WALKING
                        }, (result: google.maps.DirectionsResult | null, status: google.maps.DirectionsStatus) => {
                            if (status === google.maps.DirectionsStatus.OK && result) {
                                resolve(result);
                            } else {
                                reject(new Error(`Directions failed: ${status}`));
                            }
                        });
                    });

                    if (response.routes && response.routes.length > 0) {
                        const route = response.routes[0];
                        const leg = route?.legs[0];
                        return {
                            stop,
                            distance: leg?.distance?.value || Infinity,
                            duration: leg?.duration?.value || 0,
                            points: route?.overview_path || []
                        };
                    }
                } catch (e) {
                    console.warn('Error calculando ruta a parada', stop.name, e);
                }
                return null;
            });

            const results = await Promise.all(routePromises);

            for (const res of results) {
                if (res && res.distance < minimaDistanciaCalles) {
                    minimaDistanciaCalles = res.distance;
                    mejorDuracion = res.duration;
                    mejorParada = res.stop;
                    mejorRutaPuntos = res.points;
                }
            }
        } catch (e) {
            console.error('Error cargando Directions API en useParadaCercana', e);
        }

        // GUARD: Si se canceló durante las peticiones HTTP, abortar antes de dibujar y asignar
        if (cancelToken?.cancelled) {
            console.log('SIB | Operación encontrarParadaCercana cancelada');
            return;
        }

        // 3. Fallback a la más cercana lineal si falla API
        if (!mejorParada) {
            mejorParada = top5[0] || null;
            minimaDistanciaCalles = (paradasConDistLineal[0]?.distancia || 0) * 1000;
            mejorDuracion = (minimaDistanciaCalles / 1000) / 5 * 60 * 60; // asumiendo caminata a 5km/h
        }

        paradaCercana.value = mejorParada;
        distanciaMetros.value = minimaDistanciaCalles;
        duracionCaminata.value = mejorDuracion; // en segundos

        // 4. Dibujar polilínea de caminata punteada azul
        if (map && mejorRutaPuntos.length > 0 && !cancelToken?.cancelled) {
            caminandoPolyline.value = new google.maps.Polyline({
                path: mejorRutaPuntos,
                strokeColor: '#F59E0B',
                strokeOpacity: 0,
                strokeWeight: 3,
                icons: [{
                    icon: {
                        path: 'M 0,-1 0,1',
                        strokeOpacity: 1,
                        scale: 3,
                        strokeColor: '#F59E0B'
                    },
                    offset: '0',
                    repeat: '12px'
                }],
                map: map
            });
            registrarPolyline(caminandoPolyline.value);
        }
    };

    return {
        encontrarParadaCercana,
        limpiarCaminata,
        paradaCercana,
        distanciaMetros,
        duracionCaminata
    };
}
