'use strict';
define(function() {
  /**
   * Service créé pour partager la configuration du widget "GeolocalisationWidget" avec
   * l'outil de la barre verticale "geolocalisationbutton"
   * Copie du code à gerber de GeolocalisationWidget
   * Il n'a pas été demandé de prendre le temps de reformater le widget.
   *
   * @param ConfigFactory
   * @param $rootScope
   * @param $filter
   * @param $q
   * @param NominatimFactory
   * @param kisGeocodageFactory
   * @param GoogleGeocodeFactory
   * @return {{landorcity: {}, getAddresses: ((function(*): (*))|*), kisgeocodage: *[], cityCountry, config: [{name: string, value: {city: string, land: string}}, {name: string, value: {nominatim: string, kisgeocodage_value: *[], google: string}}, {name: string, value: {}}], getConfig: getConfig}}
   * @constructor
   */
  const GeolocalisationService = (ConfigFactory, $rootScope, $filter, $q, NominatimFactory,
      kisGeocodageFactory, GoogleGeocodeFactory, gaJsUtils) => {

    let config = [
      { name: 'filter', value: { land: '', city: '' } },
      {
        name: 'service',
        value: {
          nominatim: '',
          google: '',
          kisgeocodage_value: [],
        },
      },
      { name: 'srid', value: {} },
    ];
    let cityCountry;
    let landorcity = {};
    let queryFilter;
    let nominatimService = false;
    let googleService = false;
    let kisgeocodage = [];
    if (
        $rootScope.xgos.portal.parameters.kisgeocodage &&
        $rootScope.xgos.portal.parameters.kisgeocodage.length > 0
    ) {
      kisgeocodage = $rootScope.xgos.portal.parameters.kisgeocodage;
    }

    const getConfig = () => {
      ConfigFactory.get('widgets', 'geolocalisation').then(function(res) {
        if (res.data !== '' && res.status === 200) {
          readConfig(res.data);
        }
      });
    };

    const readConfig = (configReceived) => {
      config = configReceived;
      setLandOrCity(config)
      getFilterFromConfig();
      getServiceNameFromConfig();
      readKisGeocodageConfig();
    };

    const setLandOrCity = (config) => {
      for (let cfg of config) {
        if (cfg.name === 'filter') {
          cityCountry = cfg;
          break;
        }
      }
      if (cityCountry.value.hasOwnProperty('city'))
        landorcity.value = cityCountry.value.city;
      if (cityCountry.value.hasOwnProperty('land'))
        landorcity.value += ', ' + cityCountry.value.land;
    };

    const getFilterFromConfig = () => {
      queryFilter = '';
      for (let cfg of config) {
        if (cfg.hasOwnProperty('name') && cfg.name === 'filter') {
          const filterObject = cfg.value;
          if (filterObject !== undefined && filterObject.hasOwnProperty('city')
              && String(filterObject['city']).length > 0) {
            queryFilter += ', ' + filterObject.city;
          }
          if (filterObject !== undefined && filterObject.hasOwnProperty('land')
              && String(filterObject['land']).length > 0 ) {
            queryFilter += ', ' + filterObject.land;
          }
          break;
        }
      }
    };

    const getServiceNameFromConfig = () => {
      const serviceCfg = config.find(cfg => cfg.hasOwnProperty('name') && cfg.name === 'service');
      if (serviceCfg) {
        const serviceObject = serviceCfg.value;
        if (
            serviceObject !== undefined &&
            serviceObject.hasOwnProperty('google') &&
            String(serviceObject['google']).length > 0
        ) {
          googleService = serviceObject.google;
        }
        if (
            serviceObject !== undefined &&
            serviceObject.hasOwnProperty('nominatim') &&
            String(serviceObject['nominatim']).length > 0
        ) {
          nominatimService = serviceObject.nominatim;
        }
      }
    };

    const readKisGeocodageConfig = () => {
      for (const cfg of config) {
        if (cfg.name === 'service' && cfg.value.kisgeocodage_value) {
          for (let i = 0; i < cfg.value.kisgeocodage_value.length; i++) {
            if (cfg.value.kisgeocodage_value[i].value === true) {
              for (let j = 0; j < kisgeocodage.length; j++) {
                if (kisgeocodage[j].label === cfg.value.kisgeocodage_value[i].label) {
                  kisgeocodage[j].value = true;
                }
              }
            }
          }
        }
      }
    };

    const getAddresses = (inputAddress) => {
      const addresses = [];
      const defer = $q.defer();
      const hasKisGeocodage = config.some(cfg => cfg.name === 'service' && cfg.value.kisgeocodage_value.length > 0);
      const isOsmActive = nominatimService !== false;
      const isGoogleActive = googleService !== false;
      if (hasKisGeocodage || isOsmActive ||isGoogleActive) {

        // Géocodeurs à interroger
        const toProcess = {
          kis: hasKisGeocodage,
          google: isGoogleActive,
          osm: isOsmActive
        };

        // En l'absence de géocodeur paramétré on retourne aucun résultat
        if (Object.values(toProcess).every(cod => !cod)) {
          defer.resolve(addresses);
          return defer.promise;
        }

        if (hasKisGeocodage) {
          // Interroge tous les géocodeurs KIS avant de renvoyer la promesse
          const promises = kisgeocodage.filter(cod => cod.active || cod.value === true).map(
              cod => kisGeocodageFactory.search(cod.url, inputAddress.value));
          $q.all(promises).then(
              res => {
                for (let i = 0; i < res.length; i++) {
                  for (const result of res[i].data.features) {
                    const resultingAddress = {
                      address: {
                        state: result.properties.city,
                      },
                      display_name: result.properties.label,
                      lat: result.geometry.coordinates[1],
                      lon: result.geometry.coordinates[0],
                    };
                    // nom du géocodeur KIS
                    resultingAddress.origin = kisgeocodage[i].label;
                    addresses.push(resultingAddress);
                  }
                }
                toProcess.kis = false;
                // Si tous les géocodeurs sont interrogés, on renvoie la promesse
                if (Object.values(toProcess).every(val => !val)) {
                  defer.resolve(addresses);
                }
              },
              err => {
                console.error(err);
                toProcess.kis = false;
                if (Object.values(toProcess).every(val => !val)) {
                  defer.resolve(addresses);
                }
              }
          );
        }
        if (isOsmActive) {
          NominatimFactory.search(inputAddress.value + queryFilter).then(
              res => {
                for (const result of res.data) {
                  const address = result;
                  address.origin = 'osm';
                  addresses.push(address);
                }
                toProcess.osm = false;
                if (Object.values(toProcess).every(val => !val)) {
                  defer.resolve(addresses);
                }
              },
              err => {
                console.error(err.data);
                toProcess.osm = false;
                if (Object.values(toProcess).every(val => !val)) {
                  defer.resolve(addresses);
                }
              });
        }
        if (isGoogleActive) {
          let cityCountry;
          for (const cfg of config) {
            if (cfg.name === 'filter') {
              cityCountry = cfg;
              break;
            }
          }
          const filters = {
            country: cityCountry ? cityCountry.value.land : null,
            locality: cityCountry ? cityCountry.value.city : null,
          };
          GoogleGeocodeFactory.getInformationsFromAddress(inputAddress.value, filters).then(
              (res) => {
                for (const result of res.data.results) {
                  const resultingAddress = {
                    address: {
                      state: result.address_components[3].long_name,
                    },
                    display_name: result.formatted_address,
                    lat: result.geometry.location.lat,
                    lon: result.geometry.location.lng,
                  };
                  resultingAddress.origin = 'google';
                  addresses.push(resultingAddress);
                }
                toProcess.google = false;
                if (Object.values(toProcess).every(val => !val)) {
                  defer.resolve(addresses);
                }
              },
              err => {
                console.error(err.data);
                toProcess.google = false;
                if (Object.values(toProcess).every(val => !val)) {
                  defer.resolve(addresses);
                }
              });
        }
      } else {
        defer.resolve(addresses)
      }
      return defer.promise;
    };
    /**
     * Vérifie la présence du widget "Localisation à l'adresse" dans le tableau "categories"
     */
    const hasGeolocalisationWidget = (categories) => {
      let allowTool;
      let localisationWidget;
      for (const cat of categories) {
        if (Array.isArray(cat.modules)) {
          localisationWidget = cat.modules.find(mod => mod.directive === 'geolocalisationwidget');
          if (localisationWidget !== undefined) {
            break;
          }
        }
      }
      if (localisationWidget) {
        if (Array.isArray(localisationWidget.roles) && localisationWidget.roles.length > 0) {
          allowTool = localisationWidget.roles.some(role => gaJsUtils.userHasRole($rootScope.xgos.user, role));
        } else {
          allowTool = true;
        }
      } else {
        allowTool = false;
      }
      return allowTool;
    };



    return {
      config: config,
      cityCountry: cityCountry,
      landorcity: landorcity,
      kisgeocodage: kisgeocodage,
      getConfig: getConfig,
      getAddresses: getAddresses,
      hasGeolocalisationWidget: hasGeolocalisationWidget,
      readConfig: readConfig
    };
  };
  GeolocalisationService.$inject = ['ConfigFactory', '$rootScope', '$filter', '$q', 'NominatimFactory',
    'kisGeocodageFactory', 'GoogleGeocodeFactory', 'gaJsUtils'];
    return GeolocalisationService;
  });
