'use strict';

const axios = require('axios');
const fs = require('fs');
const path = require('path');
const csv = require('csv-parser');
const polylineLib = require('@mapbox/polyline');
const auth = require('../utils/auth');
const logger = require('firebase-functions/logger');

// ─────────────────────────────────────────────
// Chargement du CSV des gares de péage (cache mémoire)
// ─────────────────────────────────────────────
let _tollStations = null;

function loadTollStations() {
  return new Promise((resolve, reject) => {
    if (_tollStations) return resolve(_tollStations);
    const csvPath = path.join(__dirname, '../travel/gares_peage_export.csv');
    if (!fs.existsSync(csvPath)) {
      logger.warn('[Travel] CSV not found at ' + csvPath);
      _tollStations = [];
      return resolve(_tollStations);
    }
    const results = [];
    fs.createReadStream(csvPath)
      .pipe(csv())
      .on('data', (row) => {
        if (row.id_gare && row.lat && row.lon) {
          results.push({
            id: row.id_gare,
            operatorId: row.id_gare.substring(0, 2),
            tollId: row.id_gare.substring(2, 5),
            name: row.nom || '',
            lat: parseFloat(row.lat),
            lon: parseFloat(row.lon),
          });
        }
      })
      .on('end', () => { _tollStations = results; resolve(results); })
      .on('error', reject);
  });
}

// ─────────────────────────────────────────────
// Ulys — Détection des péages sur un tracé
// POST https://api-ulys.azure-api.net/placemark/v2/legs?precision=6&includeLayersIds=GaresPeage
// Body = la polyline encodée (string brute, pas de JSON wrapper)
// ─────────────────────────────────────────────
async function getUlysTollLegs(encodedPolyline) {
  try {
    const polylineCoords = polylineLib.decode(encodedPolyline);
    const ulysUrl = 'https://api-ulys.azure-api.net/placemark/v2/legs?precision=5&includeLayersIds=GaresPeage';
    let finalPolyline = encodedPolyline;
    
    // OPTION 1 : Mapbox Route Recreation
    if (process.env.MAPBOX_API_KEY && polylineCoords.length > 2) {
      logger.info('[Travel] MAPBOX_API_KEY is present. Recreating route with Mapbox for Ulys precision...');
      try {
        // Envoyer uniquement le point de départ et le point d'arrivée
        // Mapbox s'occupe de recréer l'itinéraire complet de la meilleure façon
        const waypoints = [polylineCoords[0], polylineCoords[polylineCoords.length - 1]];
        
        // Mapbox expects longitude,latitude
        const coordinatesString = waypoints.map(p => `${p[1]},${p[0]}`).join(';');
        const mapboxUrl = `https://api.mapbox.com/directions/v5/mapbox/driving/${coordinatesString}?geometries=polyline&overview=full&access_token=${process.env.MAPBOX_API_KEY}`;
        
        const mapboxRes = await axios.get(mapboxUrl);
        if (mapboxRes.data && mapboxRes.data.routes && mapboxRes.data.routes.length > 0) {
            finalPolyline = mapboxRes.data.routes[0].geometry;
            logger.info('[Travel] Mapbox route recreation successful.');
        }
      } catch (mapboxErr) {
        logger.error('[Travel] Mapbox API error:', mapboxErr.response ? mapboxErr.response.data : mapboxErr.message);
        // Fallback to Google Maps polyline if Mapbox fails
      }
    }

    // Appeler Ulys /legs
    const res = await axios.post(
      ulysUrl,
      JSON.stringify(finalPolyline),
      {
        headers: {
          'Content-Type': 'application/json',
          'Host': 'api-ulys.azure-api.net'
        },
        timeout: 10000,
      }
    );
    return res.data;
  } catch (e) {
    logger.warn('[Travel] Ulys /legs failed:', e.message);
    return null;
  }
}

// ─────────────────────────────────────────────
// Ulys — Tarif pour un segment (entrée → sortie)
// POST https://api-ulys.azure-api.net/tollstation/v1/rate
// ─────────────────────────────────────────────
async function getUlysRate(vehicleCategory, passages) {
  try {
    const now = new Date().toISOString();
    const payload = {
      vehicleCategory: String(vehicleCategory),
      paymentOption: 2,
      tollPassages: passages.map((p) => ({
        toll: { operatorId: p.operatorId, tollId: p.tollId },
        passageDate: now,
      })),
    };
    const body = JSON.stringify(payload);
    const res = await axios.post(
      'https://api-ulys.azure-api.net/tollstation/v1/rate',
      payload,
      {
        headers: {
          'Content-Type': 'application/json',
          'Content-Length': Buffer.byteLength(body).toString(),
          'Host': 'api-ulys.azure-api.net',
        },
        timeout: 8000,
      },
    );
    const data = res.data;
    if (Array.isArray(data) && data.length > 0) {
      if (passages.length === 2) {
        // We expect a single closed system response
        if (data.length === 1 && data[0].entranceToll && data[0].exitToll && data[0].price > 0) {
          return data[0].price;
        }
        return null;
      } else if (passages.length === 1) {
        if (data.length === 1 && data[0].price > 0) {
          return data[0].price;
        }
        return null;
      }
    }
    return null;
  } catch (e) {
    return null;
  }
}

// ─────────────────────────────────────────────
// Calcul du total de péage via Ulys /legs puis /rate
// ─────────────────────────────────────────────
async function calculateTollCost(encodedPolyline, vehicleCategory) {
  try {
    // 1. Demander à Ulys les gares sur le tracé
    const legsData = await getUlysTollLegs(encodedPolyline);
    const features = Array.isArray(legsData) ? legsData : (legsData && legsData.features ? legsData.features : []);

    if (features && features.length > 0) {
      // Extraire les gares dans l'ordre du tracé
      const tollGates = [];
      for (const feature of features) {
        const props = feature.properties || feature.Placemark || feature.placemark || {};

        // La réponse Ulys peut utiliser différents noms de champs
        // On cherche l'identifiant de la gare dans tous les champs connus
        let id =
          props.id_gare ||
          props.idGare ||
          props.id ||
          props.gareId ||
          props.gare_id ||
          props.tollStationId;

        if (!id && props.Code) {
          id = props.Code.split('_')[0];
        }

        if (!id) continue;
        const idStr = String(id);
        if (idStr.length < 5) continue;

        tollGates.push({
          id: idStr,
          operatorId: idStr.substring(0, 2),
          tollId: idStr.substring(2, 5),
          name: props.nom || props.name || props.label || idStr,
        });
      }

      logger.info(`[Travel] Ulys /legs found ${tollGates.length} toll gates`);
      if (tollGates.length === 0) return 0;

      // Greedy: trouver les segments fermés + barrières ouvertes
      return await _computeTollFromGates(tollGates, vehicleCategory);
    }

    // Fallback : pas de résultat Ulys /legs, retourner 0
    logger.info('[Travel] Ulys /legs returned no toll gates for this route');
    return 0;
  } catch (e) {
    logger.error('[Travel] calculateTollCost error:', e);
    return 0;
  }
}

async function _computeTollFromGates(gates, vehicleCategory) {
  let total = 0;
  let i = 0;
  while (i < gates.length) {
    let found = false;
    // Essayer le segment fermé le plus long possible (greedy backward)
    for (let j = gates.length - 1; j > i; j--) {
      const price = await getUlysRate(vehicleCategory, [gates[i], gates[j]]);
      if (price !== null && price > 0) {
        total += price;
        i = j;
        found = true;
        break;
      }
    }
    if (!found) {
      // Barrière ouverte : tarif unitaire
      const price = await getUlysRate(vehicleCategory, [gates[i]]);
      if (price !== null && price > 0 && price < 20) {
        total += price;
      }
      i++;
    }
  }
  return total;
}

// ─────────────────────────────────────────────
// EXPORT: Google Maps Autocomplete (proxy CORS)
// ─────────────────────────────────────────────
exports.googleMapsAutocomplete = async (req, res) => {
  if (req.method === 'OPTIONS') {
    return res.status(204).send('');
  }
  try {
    await auth.authenticateUser(req);

    const body = req.body.data || req.body;
    const query = body.query || req.query.query;
    if (!query) return res.status(400).json({ error: 'query is required' });

    const apiKey = process.env.API_MAPS;
    if (!apiKey) return res.status(500).json({ error: 'API_MAPS not configured in .env' });

    const url = new URL('https://maps.googleapis.com/maps/api/place/autocomplete/json');
    url.searchParams.set('input', query);
    url.searchParams.set('key', apiKey);
    url.searchParams.set('language', 'fr');
    url.searchParams.set('components', 'country:fr');
    url.searchParams.set('types', 'geocode');

    const gRes = await axios.get(url.toString(), { timeout: 5000 });
    return res.status(200).json(gRes.data);
  } catch (e) {
    logger.error('[Travel] googleMapsAutocomplete error:', e.message);
    return res.status(500).json({ error: e.message });
  }
};

// ─────────────────────────────────────────────
// EXPORT: Google Maps Compute Route (2 itinéraires + péages Ulys)
// ─────────────────────────────────────────────
exports.googleMapsComputeRoute = async (req, res) => {
  if (req.method === 'OPTIONS') {
    return res.status(204).send('');
  }
  try {
    await auth.authenticateUser(req);

    const body = req.body.data || req.body;
    const { origin, destination, vehicleTollCategory = 2 } = body;

    if (!origin || !destination) {
      return res.status(400).json({ error: 'origin and destination are required' });
    }

    const apiKey = process.env.API_MAPS;
    if (!apiKey) return res.status(500).json({ error: 'API_MAPS not configured in .env' });

    const routesUrl = 'https://routes.googleapis.com/directions/v2:computeRoutes';
    const fieldMask = [
      'routes.distanceMeters',
      'routes.duration',
      'routes.polyline.encodedPolyline',
      'routes.travelAdvisory.tollInfo',
    ].join(',');

    const commonPayload = {
      travelMode: 'DRIVE',
      routingPreference: 'TRAFFIC_AWARE',
      origin: { address: origin },
      destination: { address: destination },
    };

    const [resToll, resNoToll] = await Promise.all([
      axios.post(routesUrl, { ...commonPayload, routeModifiers: { avoidTolls: false } }, {
        headers: {
          'Content-Type': 'application/json',
          'X-Goog-Api-Key': apiKey,
          'X-Goog-FieldMask': fieldMask,
        },
        timeout: 15000,
      }),
      axios.post(routesUrl, { ...commonPayload, routeModifiers: { avoidTolls: true } }, {
        headers: {
          'Content-Type': 'application/json',
          'X-Goog-Api-Key': apiKey,
          'X-Goog-FieldMask': fieldMask,
        },
        timeout: 15000,
      }),
    ]);

    const routes = [];
    
    console.log("resToll.data.routes length:", resToll.data.routes ? resToll.data.routes.length : 0);
    if (!resToll.data.routes) console.log("resToll.data:", JSON.stringify(resToll.data, null, 2));

    // --- Route avec péage ---
    if (resToll.data.routes && resToll.data.routes.length > 0) {
      const r = resToll.data.routes[0];
      const poly = r.polyline?.encodedPolyline || '';
      let tollCost = 0;
      if (poly) {
        tollCost = await calculateTollCost(poly, vehicleTollCategory);
      }
      routes.push({
        routeType: 'TOLL',
        distanceMeters: r.distanceMeters || 0,
        durationSeconds: _parseDuration(r.duration),
        encodedPolyline: poly,
        tollCost,
      });
    }

    // --- Route sans péage ---
    if (resNoToll.data.routes && resNoToll.data.routes.length > 0) {
      const r = resNoToll.data.routes[0];
      const poly = r.polyline?.encodedPolyline || '';

      // N'ajouter que si différente de la route avec péage
      const isDifferent = routes.length === 0 ||
        r.distanceMeters !== routes[0].distanceMeters ||
        Math.abs(_parseDuration(r.duration) - routes[0].durationSeconds) > 60;

      if (isDifferent) {
        routes.push({
          routeType: 'TOLL_FREE',
          distanceMeters: r.distanceMeters || 0,
          durationSeconds: _parseDuration(r.duration),
          encodedPolyline: poly,
          tollCost: 0,
        });
      }
    }

    return res.status(200).json({ routes });
  } catch (e) {
    logger.error('[Travel] googleMapsComputeRoute error:', e.message, e.response?.data);
    return res.status(500).json({ error: e.message });
  }
};

function _parseDuration(durationStr) {
  if (!durationStr) return 0;
  if (typeof durationStr === 'number') return durationStr;
  // Format: "1234s"
  const match = String(durationStr).match(/^(\d+)s?$/);
  return match ? parseInt(match[1]) : 0;
}

exports.getUlysTollLegs = getUlysTollLegs;
