const db = require('../config/db'); const agheeraPushClient = require('../services/agheeraPushClient'); const AGHEERA_CLIENT_ID = 532; const getDniFromLocation = (locationData) => { if (locationData?.extras?.alias) { return String(locationData.extras.alias); } if (locationData?.alias) { return String(locationData.alias); } return null; }; const getCoordinatesFromLocation = (locationData) => { if (locationData?.location?.coords) { return { lat: locationData.location.coords.latitude, lng: locationData.location.coords.longitude }; } if (locationData?.coords) { return { lat: locationData.coords.latitude, lng: locationData.coords.longitude }; } if ( Object.prototype.hasOwnProperty.call(locationData || {}, 'latitude') && Object.prototype.hasOwnProperty.call(locationData || {}, 'longitude') ) { return { lat: locationData.latitude, lng: locationData.longitude }; } return null; }; const normalizeTripId = (rawValue) => { if (rawValue === undefined || rawValue === null || String(rawValue).trim() === '') { return null; } const parsed = Number.parseInt(String(rawValue).trim(), 10); return Number.isInteger(parsed) && parsed > 0 ? parsed : null; }; const getTripIdFromLocation = (locationData) => { const candidates = [ locationData?.id_viaje, locationData?.tripId, locationData?.idViaje, locationData?.params?.id_viaje, locationData?.params?.tripId, locationData?.extras?.id_viaje, locationData?.extras?.tripId ]; for (const value of candidates) { const normalized = normalizeTripId(value); if (normalized !== null) { return normalized; } } return null; }; const getRawTimestampFromLocation = (locationData) => { const candidates = [ locationData?.timestamp, locationData?.location?.timestamp ]; for (const value of candidates) { if (value !== undefined && value !== null && String(value).trim() !== '') { return String(value).trim(); } } return null; }; const getPersistedTimestampFromLocation = (locationData) => { const rawTimestamp = getRawTimestampFromLocation(locationData); if (rawTimestamp === null) { return { value: new Date(), usedFallback: true, reason: 'missing' }; } const parsedTimestamp = new Date(rawTimestamp); if (Number.isNaN(parsedTimestamp.getTime())) { return { value: new Date(), usedFallback: true, reason: 'invalid', rawTimestamp }; } return { value: parsedTimestamp, usedFallback: false, reason: null, rawTimestamp }; }; const pushLocationToAgheera = async ({ latitude, longitude, dni, tripId, measurementTime }) => { if (!tripId || !dni) { return null; } const [tripRows] = await db.query( `SELECT id_cliente FROM c_viajes WHERE id_viaje = ? LIMIT 1`, [tripId] ); if (Number.parseInt(tripRows[0]?.id_cliente, 10) !== AGHEERA_CLIENT_ID) { return null; } const baseResult = { trip_id: tripId, attempted: true, success: false, http_status: null, error: null }; const [authorizationRows] = await db.query( `SELECT id_tipovehiculo AS matricula FROM c_viajes_proveedor WHERE id_viaje = ? AND dni = ? ORDER BY n_proveedor ASC LIMIT 1`, [tripId, dni] ); const licensePlate = String(authorizationRows[0]?.matricula || '').trim(); if (!licensePlate) { return { ...baseResult, error: 'LICENSE_PLATE_NOT_FOUND' }; } try { const result = await agheeraPushClient.pushPosition({ latitude, longitude, vehicleId: licensePlate, licensePlate, measurementTime }); return { ...baseResult, success: true, http_status: result?.status ?? null }; } catch (error) { console.error('Agheera push failed after location update:', { tripId, dni, message: error.message, status: error.status || null }); return { ...baseResult, http_status: error.status || null, error: error.message }; } }; const pushLocationsToAgheera = async (locationsToPush) => { const results = []; for (const location of locationsToPush) { const result = await pushLocationToAgheera(location); if (result) { results.push(result); } } return results; }; const saveLocation = async (req, res) => { try { const data = req.body; console.log('Location payload received:', JSON.stringify(data, null, 2)); let locations = []; let globalDni = null; let globalTripId = null; if (req.user?.dni) { globalDni = String(req.user.dni); } if (Array.isArray(data)) { locations = data; } else if (data?.location && Array.isArray(data.location)) { locations = data.location; if (!globalDni) { if (data.alias) { globalDni = String(data.alias); } else if (data.params?.alias) { globalDni = String(data.params.alias); } } globalTripId = getTripIdFromLocation(data); } else { locations = [data]; globalTripId = getTripIdFromLocation(data); } const rowsToInsert = []; const locationsToPush = []; for (const loc of locations) { const coords = getCoordinatesFromLocation(loc); const dni = globalDni || getDniFromLocation(loc); const tripId = globalTripId !== null ? globalTripId : getTripIdFromLocation(loc); if (coords && coords.lat !== undefined && coords.lat !== null && coords.lng !== undefined && coords.lng !== null) { const persistedTimestamp = getPersistedTimestampFromLocation(loc); if (persistedTimestamp.usedFallback) { console.warn('Location timestamp fallback applied:', { reason: persistedTimestamp.reason, rawTimestamp: persistedTimestamp.rawTimestamp || null, uuid: loc?.uuid || loc?.location?.uuid || null, tripId, dni: dni || null }); } rowsToInsert.push([ String(coords.lat), String(coords.lng), dni || null, persistedTimestamp.value, tripId ]); locationsToPush.push({ latitude: coords.lat, longitude: coords.lng, dni, tripId, measurementTime: persistedTimestamp.value }); } } if (rowsToInsert.length === 0) { return res.json({ success: false, error: 'no_coords', message: 'No valid coordinates found in batch/object' }); } await db.query( `INSERT INTO c_trazabilidad_transportista (latitud, longitud, id_usuario, fecha, id_viaje) VALUES ?`, [rowsToInsert] ); const agheeraResults = await pushLocationsToAgheera(locationsToPush); const responseBody = { success: true, count: rowsToInsert.length, message: 'Locations saved' }; if (agheeraResults.length > 0) { responseBody.agheera_push = agheeraResults.length === 1 ? agheeraResults[0] : agheeraResults; } return res.json(responseBody); } catch (error) { console.error('Error saving location:', error); return res.status(500).json({ success: false, error: error.message }); } }; module.exports = { saveLocation };