import { drawGeometries } from '../leaflet-draw.helper.js'

var leafletInputMap

$(document).ready(() => {
  if (
    typeof mapFieldData == 'object' &&
    'bazWheelZoom' in mapFieldData &&
    'bazShowNav' in mapFieldData &&
    'mapProvider' in mapFieldData &&
    'mapProviderCredentials' in mapFieldData &&
    'bazMapCenterLat' in mapFieldData &&
    'bazMapCenterLon' in mapFieldData &&
    'bazMapZoom' in mapFieldData
  ) {
    // Init leaflet map
    leafletInputMap = new L.Map('osmmapform', {
      scrollWheelZoom: mapFieldData.bazWheelZoom,
      zoomControl: mapFieldData.bazShowNav,
    })
    const provider = L.tileLayer.provider(
      mapFieldData.mapProvider,
      mapFieldData.mapProviderCredentials,
    )
    leafletInputMap.addLayer(provider)

    leafletInputMap.setView(
      new L.LatLng(mapFieldData.bazMapCenterLat, mapFieldData.bazMapCenterLon),
      mapFieldData.bazMapZoom,
    )

    if (mapFieldData.hasGeometries) {
      var drawnItems = L.featureGroup().addTo(leafletInputMap)
      L.drawLocal.edit.toolbar.actions.save.title = _t('SAVE_CHANGES_TITLE')
      L.drawLocal.edit.toolbar.actions.save.text = _t('SAVE_BUTTON_TEXT')
      L.drawLocal.edit.toolbar.actions.cancel.title = _t('CANCEL_EDITING_TITLE')
      L.drawLocal.edit.toolbar.actions.cancel.text = _t('CANCEL_BUTTON_TEXT')
      L.drawLocal.edit.toolbar.actions.clearAll.title = _t(
        'CLEAR_ALL_LAYERS_TITLE',
      )
      L.drawLocal.edit.toolbar.actions.clearAll.text = _t(
        'CLEAR_ALL_BUTTON_TEXT',
      )
      L.drawLocal.edit.toolbar.buttons.edit = _t('EDIT_LAYERS_BUTTON')
      L.drawLocal.edit.toolbar.buttons.editDisabled = _t('EDIT_DISABLED_BUTTON')
      L.drawLocal.edit.toolbar.buttons.remove = _t('DELETE_LAYERS_BUTTON')
      L.drawLocal.edit.toolbar.buttons.removeDisabled = _t(
        'DELETE_DISABLED_BUTTON',
      )
      L.drawLocal.edit.handlers.edit.tooltip.text = _t('EDIT_TOOLTIP_TEXT')
      L.drawLocal.edit.handlers.edit.tooltip.subtext = _t(
        'EDIT_TOOLTIP_SUBTEXT',
      )
      L.drawLocal.edit.handlers.remove.tooltip.text = _t('REMOVE_TOOLTIP_TEXT')
      L.drawLocal.draw.toolbar.actions.title = _t('CANCEL_DRAWING_TITLE')
      L.drawLocal.draw.toolbar.actions.text = _t('CANCEL_BUTTON_TEXT')
      L.drawLocal.draw.toolbar.finish.title = _t('FINISH_DRAWING_TITLE')
      L.drawLocal.draw.toolbar.finish.text = _t('FINISH_BUTTON_TEXT')
      L.drawLocal.draw.toolbar.undo.title = _t('DELETE_LAST_POINT_TITLE')
      L.drawLocal.draw.toolbar.undo.text = _t('DELETE_LAST_POINT_TEXT')
      L.drawLocal.draw.toolbar.buttons.polyline = _t('DRAW_POLYLINE_BUTTON')
      L.drawLocal.draw.toolbar.buttons.polygon = _t('DRAW_POLYGON_BUTTON')
      L.drawLocal.draw.toolbar.buttons.rectangle = _t('DRAW_RECTANGLE_BUTTON')
      L.drawLocal.draw.toolbar.buttons.circle = _t('DRAW_CIRCLE_BUTTON')
      L.drawLocal.draw.toolbar.buttons.marker = _t('DRAW_MARKER_BUTTON')
      L.drawLocal.draw.toolbar.buttons.circlemarker = _t(
        'DRAW_CIRCLE_MARKER_BUTTON',
      )
      L.drawLocal.draw.handlers.circle.tooltip.start = _t(
        'CIRCLE_TOOLTIP_START',
      )
      L.drawLocal.draw.handlers.circle.radius = _t('CIRCLE_RADIUS_LABEL')
      L.drawLocal.draw.handlers.circlemarker.tooltip.start = _t(
        'CIRCLE_MARKER_TOOLTIP_START',
      )
      L.drawLocal.draw.handlers.marker.tooltip.start = _t(
        'MARKER_TOOLTIP_START',
      )
      L.drawLocal.draw.handlers.polygon.tooltip.start = _t(
        'POLYGON_TOOLTIP_START',
      )
      L.drawLocal.draw.handlers.polygon.tooltip.cont = _t(
        'POLYGON_TOOLTIP_CONT',
      )
      L.drawLocal.draw.handlers.polygon.tooltip.end = _t('POLYGON_TOOLTIP_END')
      L.drawLocal.draw.handlers.polyline.error = _t('POLYLINE_ERROR')
      L.drawLocal.draw.handlers.polyline.tooltip.start = _t(
        'POLYLINE_TOOLTIP_START',
      )
      L.drawLocal.draw.handlers.polyline.tooltip.cont = _t(
        'POLYLINE_TOOLTIP_CONT',
      )
      L.drawLocal.draw.handlers.polyline.tooltip.end = _t(
        'POLYLINE_TOOLTIP_END',
      )
      L.drawLocal.draw.handlers.rectangle.tooltip.start = _t(
        'RECTANGLE_TOOLTIP_START',
      )
      L.drawLocal.draw.handlers.simpleshape.tooltip.end = _t(
        'SIMPLE_SHAPE_TOOLTIP_END',
      )
      var geocodedmarker

      if (mapFieldData.geometries) {
        drawnItems = drawGeometries(
          drawnItems,
          mapFieldData.geometries.features,
        )
      }
      leafletInputMap.addControl(
        new L.Control.Draw({
          edit: {
            featureGroup: drawnItems,
            remove: true,
            poly: {
              allowIntersection: false,
            },
          },
          draw: {
            position: 'topleft',
            polyline: mapFieldData.chosenGeometries.includes('line'),
            polygon: mapFieldData.chosenGeometries.includes('polygon'),
            rectangle: mapFieldData.chosenGeometries.includes('rectangle'),
            circle: mapFieldData.chosenGeometries.includes('circle'),
            circlemarker: false,
            marker: false,
          },
        }),
      )

      leafletInputMap.on(L.Draw.Event.CREATED, function (e) {
        var layer = e.layer
        drawnItems.addLayer(layer)
        $('#geometries').val(JSON.stringify(drawnItemsToGeoJSON(drawnItems)))
      })
      leafletInputMap.on('draw:edited', function (e) {
        $('#geometries').val(JSON.stringify(drawnItemsToGeoJSON(drawnItems)))
      })
      leafletInputMap.on(L.Draw.Event.DELETED, function (e) {
        $('#geometries').val(JSON.stringify(drawnItemsToGeoJSON(drawnItems)))
      })

      leafletInputMap.whenReady(function () {
        var bounds = drawnItems.getBounds()
        if (bounds.isValid()) {
          leafletInputMap.fitBounds(bounds, {
            padding: [50, 50],
          })
        }
      })
    }
    function drawnItemsToGeoJSON(drawnItems) {
      var data = {
        type: 'FeatureCollection',
        features: [],
      }

      drawnItems.eachLayer(function (layer) {
        if (layer instanceof L.Circle) {
          // For circles, store center and radius as custom properties
          data.features.push({
            type: 'Feature',
            properties: {
              type: 'circle',
              radius: layer.getRadius(),
              ...layer.options,
            },
            geometry: {
              type: 'Point', // GeoJSON still sees it as a point
              coordinates: [layer.getLatLng().lng, layer.getLatLng().lat],
            },
          })
        } else {
          data.features.push(layer.toGeoJSON())
        }
      })
      return data
    }

    function getParentGeocode({ element }) {
      if (!element) {
        return null
      }
      const parent = $(element).closest('div.geocode-input')
      return parent && parent.length > 0 ? parent : null
    }

    function getFieldNames({ element, parent = null }) {
      if (!element) {
        return null
      }
      let foundParent = parent
      if (foundParent === null) {
        foundParent = getParentGeocode({ element })
      }
      if (foundParent === null) {
        return null
      }
      const fieldNames = $(foundParent).data('fieldNames')
      if (typeof fieldNames === 'object' && fieldNames !== null) {
        const fields = [
          'street',
          'street1',
          'street2',
          'town',
          'postalCode',
          'county',
          'state',
        ]
          .map((name) => (name in fieldNames ? fieldNames[name] : null))
          .filter((e) => typeof e === 'string' && e.length > 0)
          .map((name) => $(`#${name}`))
          .filter((field) => field.length > 0)
        return { ...fieldNames, fields }
      }
      return null
    }

    function getLatLon({ element }) {
      const fieldNames = getFieldNames({ element })
      const latitude =
        fieldNames !== null && 'latitude' in fieldNames
          ? fieldNames.latitude
          : 'bf_latitude'
      const longitude =
        fieldNames !== null && 'longitude' in fieldNames
          ? fieldNames.longitude
          : 'bf_longitude'
      return { latitude, longitude }
    }

    const fieldsToFollow = {
      latitude: [],
      longitude: [],
    }
    $('.geocode-input').each(function () {
      const fieldNames = getFieldNames({ element: $(this) })
      if (fieldNames !== null) {
        let latitude = null
        let longitude = null
        if (
          'latitude' in fieldNames &&
          !fieldsToFollow.latitude.includes(fieldNames.latitude)
        ) {
          fieldsToFollow.latitude.push(fieldNames.latitude)
          const latitudeField = $(`#${fieldNames.latitude}`)
          if (latitudeField && latitudeField.length > 0) {
            latitude = latitudeField.val()
          }
        }
        if (
          'longitude' in fieldNames &&
          !fieldsToFollow.longitude.includes(fieldNames.longitude)
        ) {
          fieldsToFollow.longitude.push(fieldNames.longitude)
          const longitudeField = $(`#${fieldNames.longitude}`)
          if (longitudeField && longitudeField.length > 0) {
            longitude = longitudeField.val()
          }
        }
        if (
          longitude !== null &&
          longitude != 0 &&
          latitude !== null &&
          latitude != 0
        ) {
          showAddressOk(longitude, latitude)
        }
      }
    })
    const cssToFollow = [
      ...fieldsToFollow.latitude,
      ...fieldsToFollow.longitude,
    ]
      .map((e) => `#${e}`)
      .join(', ')
    if (cssToFollow.length > 0) {
      $('body').on('keyup keypress', cssToFollow, function () {
        const pattern = /^-?[\d]{1,3}[.][\d]+$/
        const thisVal = $(this).val()
        if (!thisVal.match(pattern))
          $(this).val(
            $(this)
              .val()
              .replace(/[^\d.]/g, ''),
          )
      })
      $('body').on('blur', cssToFollow, function () {
        const names = getLatLon({ element: $(this) })
        showAddressOk(
          $(`#${names.longitude}`).val(),
          $(`#${names.latitude}`).val(),
        )
      })
    }

    function showAddress(map, element, move = false) {
      let address = ''
      const fieldsNames = getFieldNames({ element })
      address = fieldsNames.fields.map((field) => field.val()).join(' ')
      address = address.replace(/\\("|'|\\)/g, ' ').trim()
      if (!address) {
        geocodedmarkerRefresh(leafletInputMap.getCenter())
        return
      }
      const formattedFields = {}
      fieldsNames.fields.forEach((field) => {
        const id = field.prop('id')
        ;[
          'street',
          'street1',
          'street2',
          'town',
          'postalCode',
          'county',
          'state',
        ].forEach((key) => {
          if (
            key in fieldsNames &&
            typeof fieldsNames[key] === 'string' &&
            fieldsNames[key] === id
          ) {
            const val = field.val()
            if (val.length > 0) {
              formattedFields[key] = val
            }
          }
        })
      })
      let setToTry = []
      if (
        'street' in formattedFields &&
        'street1' in formattedFields &&
        'street2' in formattedFields
      ) {
        setToTry.push({
          method: 'geolocate',
          fields: {
            ...formattedFields,
            ...{
              street: `${formattedFields.street} ${formattedFields.street1} ${formattedFields.street2}`,
            },
          },
        })
        setToTry.push({
          method: 'geolocate',
          fields: {
            ...formattedFields,
            ...{
              street: `${formattedFields.street} ${formattedFields.street1}`,
            },
          },
        })
        setToTry.push({
          method: 'geolocate',
          fields: {
            ...formattedFields,
            ...{
              street: `${formattedFields.street} ${formattedFields.street2}`,
            },
          },
        })
        setToTry.push({
          method: 'geolocate',
          fields: {
            ...formattedFields,
            ...{ street: `${formattedFields.street}` },
          },
        })
        setToTry.push({
          method: 'geolocate',
          fields: {
            ...formattedFields,
            ...{
              street: `${formattedFields.street1} ${formattedFields.street2}`,
            },
          },
        })
        setToTry.push({
          method: 'geolocate',
          fields: {
            ...formattedFields,
            ...{ street: `${formattedFields.street1}` },
          },
        })
        setToTry.push({
          method: 'geolocate',
          fields: {
            ...formattedFields,
            ...{ street: `${formattedFields.street2}` },
          },
        })
        const withoutStreet = { ...formattedFields }
        delete withoutStreet.street
        setToTry.push({ method: 'geolocate', fields: withoutStreet })
      } else if ('street' in formattedFields && 'street1' in formattedFields) {
        setToTry.push({
          method: 'geolocate',
          fields: {
            ...formattedFields,
            ...{
              street: `${formattedFields.street} ${formattedFields.street1}`,
            },
          },
        })
        setToTry.push({
          method: 'geolocate',
          fields: {
            ...formattedFields,
            ...{ street: `${formattedFields.street}` },
          },
        })
        setToTry.push({
          method: 'geolocate',
          fields: {
            ...formattedFields,
            ...{ street: `${formattedFields.street1}` },
          },
        })
        const withoutStreet = { ...formattedFields }
        delete withoutStreet.street
        setToTry.push({ method: 'geolocate', fields: withoutStreet })
      } else if ('street' in formattedFields && 'street2' in formattedFields) {
        setToTry.push({
          method: 'geolocate',
          fields: {
            ...formattedFields,
            ...{
              street: `${formattedFields.street} ${formattedFields.street2}`,
            },
          },
        })
        setToTry.push({
          method: 'geolocate',
          fields: {
            ...formattedFields,
            ...{ street: `${formattedFields.street}` },
          },
        })
        setToTry.push({
          method: 'geolocate',
          fields: {
            ...formattedFields,
            ...{ street: `${formattedFields.street2}` },
          },
        })
        const withoutStreet = { ...formattedFields }
        delete withoutStreet.street
        setToTry.push({ method: 'geolocate', fields: withoutStreet })
      } else if ('street' in formattedFields) {
        setToTry.push({
          method: 'geolocate',
          fields: {
            ...formattedFields,
            ...{ street: `${formattedFields.street}` },
          },
        })
        const withoutStreet = { ...formattedFields }
        delete withoutStreet.street
        setToTry.push({ method: 'geolocate', fields: withoutStreet })
      } else {
        setToTry.push({ method: 'geolocate', fields: { ...formattedFields } })
      }
      setToTry.push({
        method: 'geolocateRetryWithoutNumberAtBeginningIfNeeded',
        fields: address,
      })

      let manageData = null
      const processNextSet = async () => {
        if (setToTry.length == 0) {
          throw new Error(_t('GEOLOCATER_NOT_FOUND', { addr: address }))
        } else {
          const newSet = setToTry[0]
          setToTry = setToTry.slice(1)
          return await geolocationHelper[newSet.method](newSet.fields).then(
            manageData,
          )
        }
      }
      manageData = async (data) => {
        if (
          data.length > 0 &&
          data[0].latitude.length > 0 &&
          data[0].longitude.length > 0
        ) {
          return data
        }
        return await processNextSet().then((data) => data)
      }
      processNextSet()
        .then((data) => {
          showAddressOk(data[0].longitude, data[0].latitude, move)
        })
        .catch((error) => {
          showAddressError(
            error instanceof Error ? error.message : String(error),
          )
        })

      return false
    }
    function showAddressOk(lon, lat, move) {
      if (move) {
         leafletInputMap.flyTo([lat, lon], leafletInputMap.getMaxZoom())
      } else {
         geocodedmarkerRefresh(L.latLng(lat, lon))
      }
    }

    function showAddressError(msg) {
      // console.log("showAddressError: "+msg);
      if (msg == 'not found') {
        alert(_t('BAZ_GEOLOC_NOT_FOUND'))
        geocodedmarkerRefresh(leafletInputMap.getCenter())
      } else {
        alert(_t('BAZ_MAP_ERROR', { msg }))
      }
    }
    function popupHtml(point) {
      return `
            <div class="input-group" style="margin-bottom: 10px">
                <span class="input-group-addon">Lat</span>
                <input type="text" class="form-control bf_latitude" pattern="-?\\\d{1,3}\\\.\\\d+" value="${point.lat}" />
                <span class="input-group-addon">Lon</span>
                <input type="text" class="form-control bf_longitude" pattern="-?\\\d{1,3}\\\.\\\d+" value="${point.lng}" />
            </div>
            <div class="text-center">${_t('BAZ_ADJUST_MARKER_POSITION')}</div>
        `
    }

    function geocodedmarkerRefresh(point) {
      if (geocodedmarker) leafletInputMap.removeLayer(geocodedmarker)
      if (mapFieldData.chosenGeometries.includes('marker')) {
        geocodedmarker = L.marker(point, { draggable: true }).addTo(
          leafletInputMap,
        )
        leafletInputMap.setView(point, 18)
        geocodedmarker
          .bindPopup(popupHtml(geocodedmarker.getLatLng()), {
            closeButton: false,
            closeOnClick: false,
            minWidth: 300,
          })
          .openPopup()
        $('#bf_latitude').val(point.lat)
        $('#bf_longitude').val(point.lng)

        geocodedmarker.on('dragend', function (ev) {
          this.openPopup()
          const changedPos = ev.target.getLatLng()
          $('#bf_latitude').val(changedPos.lat)
          $('#bf_longitude').val(changedPos.lng)
          $('.bf_latitude').val(changedPos.lat)
          $('.bf_longitude').val(changedPos.lng)
        })
      } else {
        // remove formerly encoded marker position
        $('#bf_latitude,#bf_longitude,.bf_latitude,.bf_longitude').val('')
      }
    }
    $('.btn-geolocate').on('click', function () {
      const names = getLatLon({ element: $(this) })
      function onLocationFound(e) {
        $(`#${names.latitude}`).val(e.latitude)
        $(`#${names.longitude}`).val(e.longitude)
        geocodedmarkerRefresh(e.latlng)
        leafletInputMap.panTo(e.latlng, { animate: true })
      }

      function onLocationError(e) {
        $(`#${names.latitude}`).val('')
        $(`#${names.longitude}`).val('')
        console.log(e.message)
      }

      leafletInputMap.on('locationfound', onLocationFound)
      leafletInputMap.on('locationerror', onLocationError)

      leafletInputMap.locate({ setView: true, maxZoom: 16 })
    })
    $('.btn-move-to-address').on('click', function () {
      showAddress(leafletInputMap, $(this), true)
    })
    $('.btn-geolocate-address').on('click', function () {
      showAddress(leafletInputMap, $(this))
    })
    $('body').on('change', '.bf_latitude, .bf_longitude', function (e) {
      const names = getLatLon({ element: $(this) })
      if ($(this).is(':invalid')) {
        $(`#${names.latitude}`).val('')
        $(`#${names.longitude}`).val('')
        alert(_t('BAZ_NOT_VALID_GEOLOC_FORMAT'))
      } else {
        $(`#${names.latitude}`).val(
          $(this).parent().find('.bf_latitude').first().val(),
        )
        $(`#${names.longitude}`).val(
          $(this).parent().find('.bf_longitude').first().val(),
        )
        geocodedmarker.setLatLng([
          $(`#${names.latitude}`).val(),
          $(`#${names.longitude}`).val(),
        ])
        leafletInputMap.panTo(geocodedmarker.getLatLng(), { animate: true })
      }
    })
  }
})

export { leafletInputMap }
