import {useState, useEffect} from "react"
import {Loader} from "@googlemaps/js-api-loader"
import {
  AutocompletePrediction,
  CoreLibrary,
  GeocoderAddressComponent,
  GeocodingLibrary,
  IDecodedAddress,
  MapsLibrary,
  PlacesLibrary,
} from "../model"

export const useMaps = () => {
  const loader = new Loader({
    apiKey: process.env.NEXT_PUBLIC_GOOGLE_API_KEY || "",
    libraries: ["maps", "places", "geocoding", "core"],
  })

  const [maps, setMaps] = useState<MapsLibrary | null>(null)
  const [places, setPlaces] = useState<PlacesLibrary | null>(null)
  const [geocoding, setGeocoding] = useState<GeocodingLibrary | null>(null)
  const [mapCore, setMapCore] = useState<CoreLibrary | null>(null)

  useEffect(() => {
    if (!maps) loadMaps()

    if (!places) loadPlaces()

    if (!geocoding) loadGeoCoding()

    if (!mapCore) loadMapCore()
  }, [])

  const loadMaps = async () => {
    const response = await loader.importLibrary("maps")
    if (response) {
      setMaps(response)
    }
  }

  const loadPlaces = async () => {
    const response = await loader.importLibrary("places")
    if (response) {
      setPlaces(response)
    }
  }

  const loadGeoCoding = async () => {
    const response = await loader.importLibrary("geocoding")
    if (response) {
      setGeocoding(response)
    }
  }

  const loadMapCore = async () => {
    const response = await loader.importLibrary("core")
    if (response) {
      setMapCore(response)
    }
  }

  const decodeAddressComponents = (addressComponents: GeocoderAddressComponent[]) => {
    const getAddressComponent = (type: string, value: "short_name" | "long_name" = "long_name") => {
      const {short_name = "", long_name = ""} = addressComponents.find(({types}) => types.includes(type)) || {}

      return value === "long_name" ? long_name : short_name
    }

    const doorAndFloorNo = getAddressComponent("premise")
    const subLocality = addressComponents
      .filter(it => it.types.includes("sublocality_level_1"))
      .map(lMark => lMark.long_name)
      .join(", ")
    const city = getAddressComponent("locality") || getAddressComponent("political")
    const state = getAddressComponent("administrative_area_level_1")
    const country = getAddressComponent("country")
    const countryCode = getAddressComponent("country")
    const pinCode = getAddressComponent("postal_code")

    return {
      doorAndFloorNo,
      subLocality,
      city: city || subLocality,
      state,
      country,
      countryCode,
      pinCode,
    }
  }

  const reverseGeoCode = async (lat: number, lng: number): Promise<IDecodedAddress | undefined> => {
    if (geocoding) {
      const {Geocoder} = geocoding
      const geoCoder = new Geocoder()
      const {results} = await geoCoder.geocode({location: {lat, lng}})

      if (results[0]) {
        const sorted = results?.find(result =>
          result?.address_components?.find(addrComp => addrComp.types.includes("postal_code"))
        )

        const addressComponents = sorted?.address_components || []
        /* const address = sorted?.formatted_address */
        const data = decodeAddressComponents(addressComponents)
        return {
          ...data,
          latitude: Number(lat.toFixed(14)),
          longitude: Number(lng.toFixed(14)),
        }
      }
    }
  }

  const getCurrentLocationData = (callback: (addressData: IDecodedAddress | null) => void) => {
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(
        async position => {
          const response = await reverseGeoCode(position.coords.latitude, position.coords.longitude)
          callback(response || null)
        },
        err => {
          console.error("User denied access to location >>>>>>>>>>", err)
          callback(null)
        },
        {enableHighAccuracy: true}
      )
    } else {
      console.error("Geolocation is not supported by this browser. >>>>>>>>>>")
      callback(null)
    }
  }

  const getPlacesPredictions = (searchText: string, onCallback: (predictions: AutocompletePrediction[]) => void) => {
    if (places) {
      const service = new places.AutocompleteService()
      service.getPlacePredictions(
        {input: searchText, componentRestrictions: {country: "in"}},
        (predictions, status) => {
          if (status === places.PlacesServiceStatus.OK) {
            onCallback(predictions || [])
          }
        }
      )
    }
  }

  return {
    loader,
    maps,
    places,
    geocoding,
    mapCore,
    reverseGeoCode,
    getCurrentLocationData,
    getPlacesPredictions,
  }
}
