'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, $timeout) => {

    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 = [];
    let nbAttempt = 0;


    /**
     * KIS-3710
     * @function getKisGeocodage
     * @description Récupère les paramètres de configuration des géocodeurs KIS.
     *   On attend que la decsription du portail soit récpérée,
     * sinon on a un crash et KIS ne démarre pas.
     * @return {undefined}
     */
    const getKisGeocodage = () => {
      if ($rootScope.xgos.portal) {
        if (
          $rootScope.xgos.portal.parameters.kisgeocodage &&
          $rootScope.xgos.portal.parameters.kisgeocodage.length > 0
        ) {
          kisgeocodage = $rootScope.xgos.portal.parameters.kisgeocodage;
        }
      }
      else if (nbAttempt++<50) {
        $timeout(getKisGeocodage,500);
      }
    };

    getKisGeocodage();


    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 (Object.prototype.hasOwnProperty.call(cityCountry.value, 'city'))
        landorcity.value = cityCountry.value.city;
      if (Object.prototype.hasOwnProperty.call(cityCountry.value, 'land'))
        landorcity.value += ', ' + cityCountry.value.land;
    };

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

    const getServiceNameFromConfig = () => {
      const serviceCfg = config.find(cfg =>
        Object.prototype.hasOwnProperty.call(cfg, 'name') && cfg.name === 'service');
      if (serviceCfg) {
        const serviceObject = serviceCfg.value;
        if (
          serviceObject !== undefined &&
            Object.prototype.hasOwnProperty.call(serviceObject, 'google') &&
            String(serviceObject['google']).length > 0
        ) {
          googleService = serviceObject.google;
        }
        if (
          serviceObject !== undefined &&
            Object.prototype.hasOwnProperty.call(serviceObject, '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', '$timeout'];
  return GeolocalisationService;
});
