import { ref } from 'vue' import { useMapState } from './useMapState' import { useDirectionsRoute } from './useDirectionsRoute' import { useParadaCercana } from './useParadaCercana' import type { BusStop } from '@/types' import { useRouteStore } from '@/stores/route' export const useFlujoPrincipal = () => { const { limpiarMapa, registrarMarker } = useMapState() const { encontrarParadaCercana } = useParadaCercana() const { trazarRuta } = useDirectionsRoute() const cargando = ref(false) const errorMsg = ref('') // Simulated obtenerUbicacion (since it was just navigator.geolocation) const obtenerUbicacion = (): Promise<{ lat: number, lng: number }> => { return new Promise((resolve, reject) => { if (!navigator.geolocation) return reject(new Error('No geolocation')) navigator.geolocation.getCurrentPosition( (pos) => resolve({ lat: pos.coords.latitude, lng: pos.coords.longitude }), (err) => reject(err), { enableHighAccuracy: true, timeout: 8000, maximumAge: 30000 } ) }) } const procesarSeleccionDeRuta = async ( _ruta: { id: string }, paradas: BusStop[], map: google.maps.Map | undefined ) => { if (!map) return try { // ── PASO 1: Limpiar todo lo que había antes ────────── limpiarMapa() cargando.value = true // ── PASO 2: Obtener ubicación ── // Paradas ya vienen precargadas desde store para evitar doble fetch let ubicacion: { lat: number, lng: number } | null = null; try { ubicacion = await obtenerUbicacion(); } catch (err) { console.warn('SIBU | No se pudo obtener ubicación', err); } // Format Paradas for trazarRuta const paradasFormateadas = paradas.map((p, i) => ({ id: typeof p.id === 'string' ? parseInt(p.id) || i : p.id || i, nombre: p.name, latitud: p.latitude, longitud: p.longitude, orden: i })); // Si no hay paradas o muy pocas, abortar if (paradasFormateadas.length < 2) return; if (!ubicacion) { // Fallback: solo dibujar la ruta sin parada cercana (o podríamos no hacer zoom guiado) await trazarRuta(paradasFormateadas, map, false); return; } // ── PASO 3: Dibujar ruta completa (fondo, gris tenue) ─ await trazarRuta(paradasFormateadas, map, true); // ── PASO 4: Calcular parada más cercana ─────────────── await encontrarParadaCercana(ubicacion, paradas, map) // Buscamos manualmente porque encontrarParadaCercana guarda en su propio ref interno que no retornamos fácil // o podemos usar useParadaCercana().paradaCercana.value // Actually, refactor from useParadaCercana: finding the closest const { paradaCercana } = useParadaCercana() await encontrarParadaCercana(ubicacion, paradas, undefined) // sin mapa para no dibujar polilínea vieja por encima const paradaCercanaFound = paradaCercana.value if (!paradaCercanaFound) { return; } const routeStore = useRouteStore() if (routeStore.selectedRouteId !== _ruta.id) { console.log('Abortando flujo: el mapa fue limpiado o cambió de ruta.') return; } // ── PASO 5: Dibujar tramo relevante (azul vivo) ─────── const idx = paradasFormateadas.findIndex(p => p.longitud === paradaCercanaFound.longitude && p.latitud === paradaCercanaFound.latitude) if (idx !== -1) { const tramoRelevante = paradasFormateadas.slice(idx) if (tramoRelevante.length > 1) { await trazarRuta(tramoRelevante, map, false) } } // ── PASO 6: Marcador de parada cercana ──────────────── registrarMarker( new google.maps.Marker({ position: { lat: paradaCercanaFound.latitude, lng: paradaCercanaFound.longitude }, map, icon: { path: google.maps.SymbolPath.CIRCLE, fillColor: '#F59E0B', fillOpacity: 1, strokeColor: '#FFFFFF', strokeWeight: 3, scale: 12 }, title: paradaCercanaFound.name, zIndex: 10 }) ) // ── PASO 7: Pulso animado en ubicación del usuario ──── dibujarPulsoUsuario(ubicacion, map) // ── PASO 8: Zoom centrado en usuario + parada cercana ─ hacerZoomAlTramoRelevante(ubicacion, paradaCercanaFound, map) } catch (error) { console.error('SIBU | Error procesando ruta:', error) errorMsg.value = 'No se pudo cargar la ruta' } finally { cargando.value = false } } // PULSO ANIMADO EN LA UBICACIÓN DEL USUARIO: const dibujarPulsoUsuario = ( ubicacion: { lat: number, lng: number }, map: google.maps.Map ) => { const { registrarCircle, registrarMarker } = useMapState() // Círculo exterior pulsante (efecto ripple) registrarCircle( new google.maps.Circle({ map, center: ubicacion, radius: 80, // metros fillColor: '#3B82F6', fillOpacity: 0.15, strokeColor: '#3B82F6', strokeOpacity: 0.4, strokeWeight: 1, zIndex: 1 }) ) // Punto central sólido del usuario registrarMarker( new google.maps.Marker({ position: ubicacion, map, icon: { path: google.maps.SymbolPath.CIRCLE, fillColor: '#3B82F6', // azul fillOpacity: 1, strokeColor: '#FFFFFF', strokeWeight: 3, scale: 9 }, title: 'Tu ubicación', zIndex: 20 // encima de todo }) ) } // ZOOM QUE MUESTRA USUARIO Y PARADA CERCANA: const hacerZoomAlTramoRelevante = ( ubicacion: { lat: number, lng: number }, paradaCercana: BusStop, map: google.maps.Map ) => { const bounds = new google.maps.LatLngBounds() // Incluir solo estos dos puntos clave bounds.extend(new google.maps.LatLng(ubicacion.lat, ubicacion.lng)) bounds.extend( new google.maps.LatLng(paradaCercana.latitude, paradaCercana.longitude) ) // Ajustar zoom con padding generoso para que se vean bien map.fitBounds(bounds, { top: 100, // espacio para la navbar bottom: 200, // espacio para el bottom sheet de ETA left: 60, right: 60 }) // Asegurar zoom mínimo para que se vea el contexto de la calle google.maps.event.addListenerOnce( map, 'bounds_changed', () => { if ((map.getZoom() ?? 0) > 17) { map.setZoom(17) // no acercar demasiado } if ((map.getZoom() ?? 0) < 14) { map.setZoom(14) // no alejar demasiado } } ) } return { procesarSeleccionDeRuta, cargando, errorMsg } }