import type { LatLngTuple } from "leaflet";
import type {
  NominatimResponse,
  ReverseGeocodingResponse,
} from "~/types/OpenStreetMap";
import { point } from "@turf/helpers";
import distance from "@turf/distance";
import { useCefStore } from "~/store/cef";

export const useOpenStreetMap = () => {
  const cefStore = useCefStore();
  const toast = useToast();
  const franquiciadoMasCercano = ref();
  const top5CefMasCercanos = ref()
  const zoom = ref(17);
  const query = ref("");
  const direccionCompleta = ref("");
  const cargando = ref(false);
  const error = ref();

  //Calcular ruta:
  const ruta = ref<LatLngTuple[]>([]);
  const distancia = ref(0);
  const duracion = ref(0);

  const coordenadasCentro = ref<LatLngTuple>([
    14.518336483223846, -90.60106639456704,
  ]);

  //:::::::::::::::::::::::: Computed functions ::::::::::::::::::::::::

  //Get franchisees from the cef store
  const franquiciados = computed(() => {
    return (cefStore.franquiciados || []).map((cef) => {
      return {
        id: cef.id,
        cef: cef.cef,
        coordenadas: [
          parseFloat(cef.latitud),
          parseFloat(cef.longitud),
        ] as LatLngTuple,
      };
    });
  });

  //:::::::::::::::::::::::: Functions ::::::::::::::::::::::::

  // Convert Coordinates to an Address String (Reverse Geocoding)
  async function getAddressFromCoords(lat: string, lon: string) {
    try {
      const url = "https://nominatim.openstreetmap.org/reverse";
      const response = await $fetch<ReverseGeocodingResponse>(url, {
        params: {
          lat: lat,
          lon: lon,
          format: "jsonv2",
          "accept-language": "es",
        },
      });
      cefStore.direccion = response.display_name;
      coordenadasCentro.value = [
        Number.parseFloat(String(lat)),
        Number.parseFloat(String(lon)),
      ];
      return response.display_name;
    } catch (err) {
      console.error(err);
      error.value = "Ocurrió un error al realizar la búsqueda.";
    } finally {
      cargando.value = false;
    }
  }

  // Drag marker to get coordinates
  const onMarkerMoveEnd = async (event: any) => {
    const marker = event.target;
    const { lat, lng } = marker.getLatLng();
    /*coordenadasCentro.value = [
      Number.parseFloat(String(lat)),
      Number.parseFloat(String(lng)),
    ];*/
    const direccion = await getAddressFromCoords(lat, lng);
    //console.log('Direccion Completa', direccion)
    //cefStore.direccion = direccion
  };

  // Get latitude and longitude by its address
  const buscarDireccion = async () => {
    if (!cefStore.direccion.trim()) {
      return;
    }
    cargando.value = true;
    error.value = "";
    try {
      const response = await $fetch<NominatimResponse[]>(
        "https://nominatim.openstreetmap.org/search",
        {
          params: {
            q: cefStore.direccion,
            format: "jsonv2",
            limit: 1,
            "accept-language": "es",
            countrycodes: "gt",
            addressdetails: 1,
          },
        },
      );
      // Avenida Petapa y 42-36, zona 12
      //Planes de Bárcena, Zona 3 de Villa Nueva, Villa Nueva, Departamento de Guatemala, 01064, Guatemala
      //[Dirección exacta/Lote/Avenida], [Colonia/Barrio], Zona [Número], [Municipio], [Departamento]
      if (response.length > 0) {
        const res = response[0];
        coordenadasCentro.value = [
          Number.parseFloat(String(res?.lat)),
          Number.parseFloat(String(res?.lon)),
        ];
        zoom.value = 13;
        return { success: true }
      } else {
        cefStore.error = true
        //cefStore.direccionError = 'No se encontraron resultados para esta dirección.'
        error.value = "No se encontraron resultados para esta dirección.";
        console.error("No se encontraron resultados para esta dirección.")
        return { success: false }
      }
    } catch (err) {
      cefStore.error = true
      console.error(err);
      error.value = "Ocurrió un error al realizar la búsqueda.";
    } finally {
      cargando.value = false;
    }
  };
  // Create a route from point A to point B
  async function calcularRuta(
    origenLat: number,
    origenLng: number,
    destinoLat: number,
    destinoLng: number,
  ) {
    console.log(origenLat, origenLng, destinoLat, destinoLng);

    const url = `https://router.project-osrm.org/route/v1/driving/${origenLng},${origenLat};${destinoLng},${destinoLat}`;
    const response = await $fetch<any>(url, {
      params: {
        overview: "full",
        geometries: "geojson",
      },
    });

    const route = response.routes[0];

    distancia.value = route.distance; // metros
    duracion.value = route.duration; // segundos

    ruta.value = route.geometry.coordinates.map(
      ([lng, lat]: [number, number]) => [lat, lng],
    );
  }

  const getNCandidatosCercanos = () => {

    const cliente = point([
      coordenadasCentro.value[1], // lng
      coordenadasCentro.value[0], // lat
    ]);
    const conDistancia = cefStore.franquiciados.map((cef) => ({
      ...cef,
      distanciaLineal: distance(cliente,
        point([parseFloat(cef.longitud), parseFloat(cef.latitud)]), { units: "kilometers" }),
    }));

    return conDistancia
      .sort((a, b) => a.distanciaLineal - b.distanciaLineal)
      .slice(0, 5);
  }

  const getNearestRoute = async () => {
    const cercanos = getNCandidatosCercanos()
    top5CefMasCercanos.value = cercanos
    const coordinates = cercanos.map((cef) => `${cef.longitud},${cef.latitud}`).join(";");
    const url = `https://router.project-osrm.org/table/v1/driving/${coordenadasCentro.value[1]},${coordenadasCentro.value[0]};${coordinates}`;
    const response = await $fetch<any>(url, {
      params: {
        sources: "0", // el punto A
        annotations: "distance,duration", // pedimos ambas
      },
    });

    if (response.code !== "Ok") {
      throw new Error(`OSRM error: ${response.code}`);
    }

    const distancias: number[] = response.distances[0];
    const duraciones: number[] = response.durations[0];
    // ignoramos el índice 0 (A contra sí mismo) y buscamos el mínimo
    let mejorIndex = 1;
    for (let i = 2; i < distancias.length; i++) {
      // Si distancias[i] es undefined, se evalúa como Infinity
      if ((distancias[i] ?? Infinity) < (distancias[mejorIndex] ?? Infinity)) {
        mejorIndex = i;
      }
    }
    const puntoMasCercano = cercanos[mejorIndex - 1]; // -1 porque candidatos no incluye a A
    franquiciadoMasCercano.value = puntoMasCercano?.id;
    cefStore.franquiciadoMasCercano = Number.parseFloat(String(puntoMasCercano?.id))
    return puntoMasCercano

  }
  // Get the nearest point B from point A
  const cefCercano = async () => {

    /*
    if (!cefStore.departamento) return toast.add({
      title: "Departamento",
      description: "Debe seleccionar un Departamento",
      icon: "i-lucide-map-pin",
      color: "error"
    });

    if (!cefStore.municipio) return toast.add({
      title: "Municipio",
      description: "Debe seleccionar un Municipio",
      icon: "i-lucide-map-pin",
      color: "error"
    });*/

    /*const buscarDir = await buscarDireccion()

    if (buscarDir?.success) {
    */

    const puntoMasCercano = await getNearestRoute()
    console.log('cefCercano', puntoMasCercano)
    zoom.value = 15;
    //Trazar la ruta
    calcularRuta(
      coordenadasCentro.value[0],
      coordenadasCentro.value[1],
      Number.parseFloat(String(puntoMasCercano?.latitud)),
      Number.parseFloat(String(puntoMasCercano?.longitud)),
    );

    toast.add({
      title: `CEF: ${puntoMasCercano?.cef}`,
      description: "Se ha encontrado el cef más cercano.",
      icon: "i-lucide-map-pin",
      color: 'success'
    });
  }


  /**
   * Obtener:
   * Departamento
   * Municipio
   * Colonia
   * Zona 
   * Direccion
   */
  const buscarDireccionMapa = async () => {
    try {
      cefStore.franquiciadoMasCercano = null
      await buscarDireccion()
    } catch (error) {

    }
  }
  return {
    onMarkerMoveEnd,
    cefCercano,
    buscarDireccionMapa,
    franquiciadoMasCercano,
    coordenadasCentro,
    franquiciados,
    distancia,
    duracion,
    zoom,
    ruta,
    top5CefMasCercanos,
    direccionCompleta,
    error
  };
};