/**
 * Controller per Geocoding usando servizi Open Source
 * - Nominatim (OpenStreetMap) per geocoding/reverse
 * - OSRM per routing
 * - Photon per autocomplete
 */

const https = require('https');
const http = require('http');

// Configurazione servizi
const NOMINATIM_BASE = 'https://nominatim.openstreetmap.org';
const PHOTON_BASE = 'https://photon.komoot.io';
const OSRM_BASE = 'https://router.project-osrm.org';

// User-Agent richiesto da Nominatim
const USER_AGENT = 'FreePlanetApp/1.0';

/**
 * Helper per fare richieste HTTP/HTTPS
 */
const makeRequest = (url) => {
  return new Promise((resolve, reject) => {
    const client = url.startsWith('https') ? https : http;
    
    const req = client.get(url, {
      headers: {
        'User-Agent': USER_AGENT,
        'Accept': 'application/json'
      }
    }, (res) => {
      let data = '';
      res.on('data', chunk => data += chunk);
      res.on('end', () => {
        try {
          resolve(JSON.parse(data));
        } catch (e) {
          reject(new Error('Errore parsing risposta'));
        }
      });
    });

    req.on('error', reject);
    req.setTimeout(10000, () => {
      req.destroy();
      reject(new Error('Timeout richiesta'));
    });
  });
};

/**
 * @desc    Autocomplete città (Photon API)
 * @route   GET /api/trasporti/geo/autocomplete
 */
const autocomplete = async (req, res) => {
  try {
    const { q, limit = 5, lang = 'it' } = req.query;

    if (!q || q.length < 2) {
      return res.status(400).json({
        success: false,
        message: 'Query deve essere almeno 2 caratteri'
      });
    }

    // Photon API - gratuito e veloce
    const url = `${PHOTON_BASE}/api/?q=${encodeURIComponent(q)}&limit=${limit}&lang=${lang}&osm_tag=place:city&osm_tag=place:town&osm_tag=place:village`;

    const data = await makeRequest(url);

    // Formatta risultati
    const results = data.features.map(feature => ({
      city: feature.properties.name,
      province: feature.properties.county || feature.properties.state,
      region: feature.properties.state,
      country: feature.properties.country,
      postalCode: feature.properties.postcode,
      coordinates: {
        lat: feature.geometry.coordinates[1],
        lng: feature.geometry.coordinates[0]
      },
      displayName: [
        feature.properties.name,
        feature.properties.county,
        feature.properties.state,
        feature.properties.country
      ].filter(Boolean).join(', '),
      type: feature.properties.osm_value || 'place'
    }));

    res.status(200).json({
      success: true,
      data: results
    });

  } catch (error) {
    console.error('Errore autocomplete:', error);
    res.status(500).json({
      success: false,
      message: 'Errore durante la ricerca',
      error: error.message
    });
  }
};

/**
 * @desc    Geocoding - indirizzo a coordinate (Nominatim)
 * @route   GET /api/trasporti/geo/geocode
 */
const geocode = async (req, res) => {
  try {
    const { address, city, country = 'Italy' } = req.query;

    const searchQuery = [address, city, country].filter(Boolean).join(', ');

    if (!searchQuery) {
      return res.status(400).json({
        success: false,
        message: 'Fornisci un indirizzo o città da cercare'
      });
    }

    const url = `${NOMINATIM_BASE}/search?format=json&q=${encodeURIComponent(searchQuery)}&limit=5&addressdetails=1`;

    const data = await makeRequest(url);

    if (!data || data.length === 0) {
      return res.status(404).json({
        success: false,
        message: 'Nessun risultato trovato'
      });
    }

    const results = data.map(item => ({
      displayName: item.display_name,
      city: item.address.city || item.address.town || item.address.village || item.address.municipality,
      address: item.address.road ? `${item.address.road}${item.address.house_number ? ' ' + item.address.house_number : ''}` : null,
      province: item.address.county || item.address.province,
      region: item.address.state,
      country: item.address.country,
      postalCode: item.address.postcode,
      coordinates: {
        lat: parseFloat(item.lat),
        lng: parseFloat(item.lon)
      },
      type: item.type,
      importance: item.importance
    }));

    res.status(200).json({
      success: true,
      data: results
    });

  } catch (error) {
    console.error('Errore geocoding:', error);
    res.status(500).json({
      success: false,
      message: 'Errore durante il geocoding',
      error: error.message
    });
  }
};

/**
 * @desc    Reverse geocoding - coordinate a indirizzo (Nominatim)
 * @route   GET /api/trasporti/geo/reverse
 */
const reverseGeocode = async (req, res) => {
  try {
    const { lat, lng } = req.query;

    if (!lat || !lng) {
      return res.status(400).json({
        success: false,
        message: 'Coordinate lat e lng richieste'
      });
    }

    const url = `${NOMINATIM_BASE}/reverse?format=json&lat=${lat}&lon=${lng}&addressdetails=1`;

    const data = await makeRequest(url);

    if (!data || data.error) {
      return res.status(404).json({
        success: false,
        message: 'Nessun risultato trovato'
      });
    }

    const result = {
      displayName: data.display_name,
      city: data.address.city || data.address.town || data.address.village || data.address.municipality,
      address: data.address.road ? `${data.address.road}${data.address.house_number ? ' ' + data.address.house_number : ''}` : null,
      province: data.address.county || data.address.province,
      region: data.address.state,
      country: data.address.country,
      postalCode: data.address.postcode,
      coordinates: {
        lat: parseFloat(lat),
        lng: parseFloat(lng)
      }
    };

    res.status(200).json({
      success: true,
      data: result
    });

  } catch (error) {
    console.error('Errore reverse geocoding:', error);
    res.status(500).json({
      success: false,
      message: 'Errore durante il reverse geocoding',
      error: error.message
    });
  }
};

/**
 * @desc    Calcola percorso tra due punti (OSRM)
 * @route   GET /api/trasporti/geo/route
 */
const getRoute = async (req, res) => {
  try {
    const { 
      startLat, startLng, 
      endLat, endLng,
      waypoints // formato: "lat1,lng1;lat2,lng2;..."
    } = req.query;

    if (!startLat || !startLng || !endLat || !endLng) {
      return res.status(400).json({
        success: false,
        message: 'Coordinate di partenza e arrivo richieste'
      });
    }

    // Costruisci stringa coordinate
    let coordinates = `${startLng},${startLat}`;
    
    if (waypoints) {
      const waypointsList = waypoints.split(';');
      waypointsList.forEach(wp => {
        const [lat, lng] = wp.split(',');
        coordinates += `;${lng},${lat}`;
      });
    }
    
    coordinates += `;${endLng},${endLat}`;

    const url = `${OSRM_BASE}/route/v1/driving/${coordinates}?overview=full&geometries=polyline&steps=true`;

    const data = await makeRequest(url);

    if (!data || data.code !== 'Ok' || !data.routes || data.routes.length === 0) {
      return res.status(404).json({
        success: false,
        message: 'Impossibile calcolare il percorso'
      });
    }

    const route = data.routes[0];

    // Estrai città attraversate (dalle istruzioni)
    const citiesAlongRoute = [];
    if (route.legs) {
      route.legs.forEach(leg => {
        if (leg.steps) {
          leg.steps.forEach(step => {
            if (step.name && step.name.length > 0) {
              // Qui potresti fare reverse geocoding per ottenere città
              // Per ora usiamo i nomi delle strade principali
            }
          });
        }
      });
    }

    const result = {
      distance: Math.round(route.distance / 1000 * 10) / 10, // km
      duration: Math.round(route.duration / 60), // minuti
      polyline: route.geometry, // Polyline encoded
      legs: route.legs.map(leg => ({
        distance: Math.round(leg.distance / 1000 * 10) / 10,
        duration: Math.round(leg.duration / 60),
        summary: leg.summary,
        steps: leg.steps ? leg.steps.slice(0, 10).map(s => ({ // Limita step
          instruction: s.maneuver ? s.maneuver.instruction : '',
          name: s.name,
          distance: Math.round(s.distance),
          duration: Math.round(s.duration / 60)
        })) : []
      }))
    };

    res.status(200).json({
      success: true,
      data: result
    });

  } catch (error) {
    console.error('Errore calcolo percorso:', error);
    res.status(500).json({
      success: false,
      message: 'Errore durante il calcolo del percorso',
      error: error.message
    });
  }
};

/**
 * @desc    Suggerisci città intermedie su un percorso
 * @route   GET /api/trasporti/geo/suggest-waypoints
 */
const suggestWaypoints = async (req, res) => {
  try {
    const { startLat, startLng, endLat, endLng } = req.query;

    if (!startLat || !startLng || !endLat || !endLng) {
      return res.status(400).json({
        success: false,
        message: 'Coordinate di partenza e arrivo richieste'
      });
    }

    // Prima ottieni il percorso
    const routeUrl = `${OSRM_BASE}/route/v1/driving/${startLng},${startLat};${endLng},${endLat}?overview=full&geometries=geojson`;

    const routeData = await makeRequest(routeUrl);

    if (!routeData || routeData.code !== 'Ok') {
      return res.status(404).json({
        success: false,
        message: 'Impossibile calcolare il percorso'
      });
    }

    // Prendi punti lungo il percorso (ogni ~50km circa)
    const coordinates = routeData.routes[0].geometry.coordinates;
    const totalPoints = coordinates.length;
    const step = Math.max(1, Math.floor(totalPoints / 6)); // ~5 punti intermedi

    const sampledPoints = [];
    for (let i = step; i < totalPoints - step; i += step) {
      sampledPoints.push(coordinates[i]);
    }

    // Fai reverse geocoding per ogni punto
    const cities = [];
    const seenCities = new Set();

    for (const point of sampledPoints.slice(0, 5)) { // Limita a 5 richieste
      try {
        const reverseUrl = `${NOMINATIM_BASE}/reverse?format=json&lat=${point[1]}&lon=${point[0]}&addressdetails=1&zoom=10`;
        const data = await makeRequest(reverseUrl);
        
        if (data && data.address) {
          const cityName = data.address.city || data.address.town || data.address.village;
          if (cityName && !seenCities.has(cityName.toLowerCase())) {
            seenCities.add(cityName.toLowerCase());
            cities.push({
              city: cityName,
              province: data.address.county || data.address.province,
              region: data.address.state,
              coordinates: {
                lat: point[1],
                lng: point[0]
              }
            });
          }
        }
        
        // Rate limiting - aspetta 1 secondo tra le richieste (requisito Nominatim)
        await new Promise(resolve => setTimeout(resolve, 1100));
      } catch (e) {
        console.log('Errore reverse per punto:', e.message);
      }
    }

    res.status(200).json({
      success: true,
      data: cities
    });

  } catch (error) {
    console.error('Errore suggerimento waypoints:', error);
    res.status(500).json({
      success: false,
      message: 'Errore durante il suggerimento delle tappe',
      error: error.message
    });
  }
};

/**
 * @desc    Cerca città italiane (ottimizzato per Italia)
 * @route   GET /api/trasporti/geo/cities/it
 */
const searchItalianCities = async (req, res) => {
  try {
    const { q, limit = 10 } = req.query;

    if (!q || q.length < 2) {
      return res.status(400).json({
        success: false,
        message: 'Query deve essere almeno 2 caratteri'
      });
    }

    // Usa Nominatim con filtro Italia
    const url = `${NOMINATIM_BASE}/search?format=json&q=${encodeURIComponent(q)}&countrycodes=it&limit=${limit}&addressdetails=1&featuretype=city`;

    const data = await makeRequest(url);

    const results = data
      .filter(item => 
        item.address && 
        (item.address.city || item.address.town || item.address.village)
      )
      .map(item => ({
        city: item.address.city || item.address.town || item.address.village,
        province: item.address.county || item.address.province,
        region: item.address.state,
        postalCode: item.address.postcode,
        coordinates: {
          lat: parseFloat(item.lat),
          lng: parseFloat(item.lon)
        },
        displayName: `${item.address.city || item.address.town || item.address.village}, ${item.address.county || item.address.state}`
      }));

    // Rimuovi duplicati
    const unique = results.filter((v, i, a) => 
      a.findIndex(t => t.city.toLowerCase() === v.city.toLowerCase()) === i
    );

    res.status(200).json({
      success: true,
      data: unique
    });

  } catch (error) {
    console.error('Errore ricerca città italiane:', error);
    res.status(500).json({
      success: false,
      message: 'Errore durante la ricerca',
      error: error.message
    });
  }
};

/**
 * @desc    Calcola distanza e durata tra due punti
 * @route   GET /api/trasporti/geo/distance
 */
const getDistance = async (req, res) => {
  try {
    const { startLat, startLng, endLat, endLng } = req.query;

    if (!startLat || !startLng || !endLat || !endLng) {
      return res.status(400).json({
        success: false,
        message: 'Tutte le coordinate sono richieste'
      });
    }

    const url = `${OSRM_BASE}/route/v1/driving/${startLng},${startLat};${endLng},${endLat}?overview=false`;

    const data = await makeRequest(url);

    if (!data || data.code !== 'Ok' || !data.routes || data.routes.length === 0) {
      return res.status(404).json({
        success: false,
        message: 'Impossibile calcolare la distanza'
      });
    }

    const route = data.routes[0];

    res.status(200).json({
      success: true,
      data: {
        distance: Math.round(route.distance / 1000 * 10) / 10, // km
        duration: Math.round(route.duration / 60), // minuti
        durationFormatted: formatDuration(route.duration)
      }
    });

  } catch (error) {
    console.error('Errore calcolo distanza:', error);
    res.status(500).json({
      success: false,
      message: 'Errore durante il calcolo della distanza',
      error: error.message
    });
  }
};

// Helper per formattare durata
const formatDuration = (seconds) => {
  const hours = Math.floor(seconds / 3600);
  const minutes = Math.round((seconds % 3600) / 60);
  
  if (hours === 0) {
    return `${minutes} min`;
  } else if (minutes === 0) {
    return `${hours} h`;
  } else {
    return `${hours} h ${minutes} min`;
  }
};

module.exports = {
  autocomplete,
  geocode,
  reverseGeocode,
  getRoute,
  suggestWaypoints,
  searchItalianCities,
  getDistance
};