/* ------------------------------------------------------------------
 * geo.js — Geocodificação e cálculo de rotas
 *
 *  - Geocodificação: Nominatim (OpenStreetMap) — sem chave necessária.
 *  - Routing: OpenRouteService com perfil "driving-hgv" (veículos pesados),
 *    respeitando peso/altura/largura/comprimento quando fornecidos.
 *    Requer chave grátis (definida em Definições).
 *  - Fallback sem chave: estimativa por distância geodésica (Haversine)
 *    × fator de estrada, e tempo com velocidade média de pesado.
 * ------------------------------------------------------------------ */

const Geo = (function () {
  const ROAD_FACTOR = 1.3; // estradas reais vs. linha reta
  const HGV_AVG_KMH = 68; // velocidade média realista de pesado (auto-estrada + paragens)

  /* --- Geocodificação (endereço -> coordenadas) --- */
  async function geocode(query) {
    const url =
      "https://nominatim.openstreetmap.org/search?format=json&limit=1&countrycodes=pt,es,fr&q=" +
      encodeURIComponent(query);
    const res = await fetch(url, {
      headers: { "Accept-Language": "pt" },
    });
    if (!res.ok) throw new Error("Geocodificação falhou (" + res.status + ")");
    const arr = await res.json();
    if (!arr.length) throw new Error("Local não encontrado: " + query);
    return {
      name: arr[0].display_name.split(",").slice(0, 2).join(",").trim(),
      lat: parseFloat(arr[0].lat),
      lon: parseFloat(arr[0].lon),
    };
  }

  /* --- Haversine (km) entre dois pontos --- */
  function haversineKm(a, b) {
    const R = 6371;
    const dLat = toRad(b.lat - a.lat);
    const dLon = toRad(b.lon - a.lon);
    const lat1 = toRad(a.lat);
    const lat2 = toRad(b.lat);
    const h =
      Math.sin(dLat / 2) ** 2 +
      Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLon / 2) ** 2;
    return R * 2 * Math.atan2(Math.sqrt(h), Math.sqrt(1 - h));
  }
  function toRad(d) {
    return (d * Math.PI) / 180;
  }

  /* --- Rota entre pontos ---
   * points: [{lat,lon}, ...] (>=2). truck: opcional para restrições HGV.
   * Devolve { distanceKm, durationMin, geometry:[[lat,lon],...], estimated:bool }
   */
  async function route(points, truck) {
    const settings = Store.getSettings();
    const key = (settings.orsApiKey || "").trim();

    if (key) {
      try {
        return await orsRoute(points, truck, key);
      } catch (e) {
        console.warn("ORS falhou, a usar estimativa:", e.message);
      }
    }
    return estimateRoute(points);
  }

  async function orsRoute(points, truck, key) {
    const coords = points.map((p) => [p.lon, p.lat]); // ORS usa [lon,lat]
    const body = { coordinates: coords, instructions: false };

    // Restrições de veículo pesado (só entram se tivermos os dados)
    if (truck) {
      const r = {};
      if (truck.maxWeightKg) r.weight = truck.maxWeightKg / 1000; // toneladas
      if (truck.heightM) r.height = Number(truck.heightM);
      if (truck.widthM) r.width = Number(truck.widthM);
      if (truck.lengthM) r.length = Number(truck.lengthM);
      if (Object.keys(r).length) {
        body.options = { profile_params: { restrictions: r }, vehicle_type: "hgv" };
      }
    }

    const res = await fetch(
      "https://api.openrouteservice.org/v2/directions/driving-hgv/geojson",
      {
        method: "POST",
        headers: {
          Authorization: key,
          "Content-Type": "application/json",
        },
        body: JSON.stringify(body),
      }
    );
    if (!res.ok) {
      const txt = await res.text();
      throw new Error("ORS " + res.status + ": " + txt.slice(0, 120));
    }
    const gj = await res.json();
    const feat = gj.features[0];
    const summary = feat.properties.summary;
    const geometry = feat.geometry.coordinates.map((c) => [c[1], c[0]]); // -> [lat,lon]
    return {
      distanceKm: +(summary.distance / 1000).toFixed(1),
      durationMin: Math.round(summary.duration / 60),
      geometry,
      estimated: false,
    };
  }

  function estimateRoute(points) {
    let km = 0;
    for (let i = 0; i < points.length - 1; i++) {
      km += haversineKm(points[i], points[i + 1]);
    }
    km = km * ROAD_FACTOR;
    const durationMin = Math.round((km / HGV_AVG_KMH) * 60);
    return {
      distanceKm: +km.toFixed(1),
      durationMin,
      geometry: points.map((p) => [p.lat, p.lon]),
      estimated: true,
    };
  }

  return { geocode, route, haversineKm };
})();
