'use strict';
/**
 * Module permettant la gestion de formulaire Indigau
 * depuis une application de type "Map" et non "MapV2".
 * Fournit des services pour calculer et préparer les chantiers Indigau.
 * @module IndigauFormFactory
 */
define(function() {
  /**
   * Factory pour la gestion des formulaires Indigau.
   * @param {Object} FeatureTypeFactory - Service de gestion des types de features
   * @param {Object} gaDomUtils - Utilitaires DOM
   * @param {Object} $http - Service HTTP d'AngularJS
   * @param {Object} $timeout - Service timeout d'AngularJS
   * @param {Object} AlertHpoFactory - Service d'alertes
   * @param {Object} $rootScope - Scope racine d'AngularJS
   * @param {Object} ApplicationFactory - Service de gestion des applications
   * @param {Object} ConfigFactory - Service de configuration
   * @param {Object} $q - Service de promesses d'AngularJS
   * @param {Object} EditFactory - Service d'édition
   * @returns {Object} API publique de la factory
   */
  const IndigauFormFactory = function(FeatureTypeFactory, gaDomUtils, $http, $timeout,
    AlertHpoFactory, $rootScope, ApplicationFactory, ConfigFactory, $q, EditFactory) {

    const INDIGAU_FTIS = {};
    const FTI_KEYS = [
      'ponderation',
      'indicateurs',
      'parametres',
      'critereas',
      'priority',
      'correspondance',
      'chantiers'
    ];
    let indigauAppName;
    let preparingCalculerChantier = false;

    /**
     * Récupère le nom du datastore à utiliser pour les features Indigau.
     * Cette fonction interroge l'application Indigau et sa configuration pour déterminer
     * le datastore approprié. Si aucun datastore n'est spécifié dans la configuration,
     * ou si une erreur se produit, le datastore par défaut 'COMMON' est utilisé.
     *
     * @returns {Promise<Object>} Une promesse qui se résout avec un objet contenant
     *                           la propriété storeName indiquant le nom du datastore à utiliser
     */
    const dataStoreName = () => {
      const defer = $q.defer();
      ApplicationFactory.get().then((res) => {
        const indigauApp = res.data.find((app) => app.type === 'Indigau');
        indigauAppName = indigauApp.name;
        ConfigFactory.get('main', 'appconfig', indigauApp.name).then((res) => {
          if (res && res.data && res.data.datastoreName ) {
            defer.resolve({storeName: res.data.datastoreName});
          }
        },
        () => {
          defer.resolve({storeName: 'COMMON'});
        });
      },
      () => {
        defer.resolve({storeName: 'COMMON'});
      });
      return defer.promise;
    };


    // -- Fabrication de la liste des FTIs des composants de travail
    // -- propres à KIS INDIGAU.
    dataStoreName().then((res) => {
      const datastore = res.storeName;
      for (const ftiKey of FTI_KEYS) {
        INDIGAU_FTIS[ftiKey]
          = FeatureTypeFactory.getFeatureByNameAndDatastore(datastore,
            'KIS_INDIGAU_' + ftiKey.toUpperCase() + '_' + datastore);
      }
    });


    /**
     * Vérifie le statut d'un thread de calcul.
     * Cette fonction effectue des appels récursifs pour vérifier
     * si le thread est toujours en cours d'exécution.
     * Si le thread est toujours en cours, elle attend 1 seconde avant de vérifier à nouveau.
     * Si le thread est terminé, elle résout la promesse avec le résultat.
     * Si le thread est terminé avec une erreur, elle résout la promesse avec l'erreur.
     *
     * @param {string} threadid - L'identifiant du thread à vérifier
     * @param {Object} deferred - L'objet de la promesse à résoudre
     * @param {number} count - Le nombre de tentatives effectuées
     */
    const checkLoadDataThread = (threadid, deferred, count) => {
      const promise = $http.get(
        '/services/{portalid}/hpomodelservices/'+ indigauAppName +'/model/checkDataLoading?f=json'
        + '&threadid=' + threadid
      );
      promise.then(
        (res0) => {
          if (res0 && res0.data) {
            const jsRes = res0.data;
            if (jsRes.alive) {
              const delay = count < 3 ? 1000 : 5000;
              $timeout(() => {
                checkLoadDataThread(threadid, deferred, ++count);
              }, delay);
            } else {
              if (jsRes.status == 400) {
                deferred.resolve({ data: jsRes.error });
              } else {
                deferred.resolve({
                  data: jsRes.result,
                  status: jsRes.status,
                });
              }
            }
          } else {
            deferred.reject(res0);
          }
        },
        (res) => {
          deferred.reject(res);
        }
      );
    };


    /**
     * Appel le service de calcul du chantier.
     * Le calcul du chantier consiste à calculer
     * le linéaire couvert par le chantier, le coût de renouvellement du chantier,
     * la priorité du chantier et le nombre de canalisations du chantier.
     * Cette fonction effectue un appel HTTP POST pour calculer le chantier.
     * Si le thread est toujours en cours, elle attend 1 seconde avant de vérifier à nouveau.
     * Si le thread est terminé, elle résout la promesse avec le résultat.
     * Si le thread est terminé avec une erreur, elle résout la promesse avec l'erreur.
     *
     * @param {string} uid - L'identifiant de la couche chantier
     * @param {string} id - L'identifiant du chantier
     * @param {Object} sendata - Les données à envoyer
     * @param {Object} defer - L'objet de la promesse à résoudre
     */
    const calculerChantierService = (uid, id, sendata, defer) => {
      const deferred = defer || $q.defer();
      if (preparingCalculerChantier) {
        $timeout(() => {
          calculerChantierService(uid, id, sendata, defer);
        }, 250);
        return deferred.promise;
      }
      const promise = $http.post(
        '/services/{portalid}/hpoapp/chantiers/'+ indigauAppName
          +'/calculate?uid=' + uid + '&id=' + id,
        sendata
      );
      promise.then(
        (res) => {
          checkLoadDataThread(res.data, deferred, 0);
        },
        (res) => {
          deferred.reject(res);
        }
      );
      return deferred.promise;
    };


    /**
     * Lance le calcul d'un chantier Indigau.
     * Cette fonction prépare les données nécessaires au calcul et appelle le service
     * calculerChantierService. Elle gère l'affichage des loaders et des notifications
     * en fonction du résultat du calcul.
     *
     * Le calcul met à jour les propriétés suivantes du chantier :
     * - Linéaire couvert
     * - Coût de renouvellement
     * - Priorité
     * - Nombre de canalisations
     *
     * @param {Object} chantier - L'objet chantier à calculer
     * @returns {void}
     */
    const calculerChantier = (chantier) => {
      // chantier id
      const chantierId = chantier.id;
      if (angular.isDefined(chantierId)) {
        const sendata = {
          fti_ponderation: INDIGAU_FTIS['ponderation'].uid,
          fti_indicateur: INDIGAU_FTIS['indicateurs'].uid,
          fti_parametrage: INDIGAU_FTIS['parametres'].uid,
          fti_criterea: INDIGAU_FTIS['critereas'].uid,
          fti_priority: INDIGAU_FTIS['priority'].uid,
          fti_correspondance: INDIGAU_FTIS['correspondance'].uid,
        };

        gaDomUtils.showGlobalLoader();

        calculerChantierService(INDIGAU_FTIS['chantiers'].uid, chantierId, sendata)
          .then(
            (res) => {
              if (res.data && res.data.properties) {
                $rootScope.$broadcast('hpo_calculer_chantier#ended');
                chantier.properties = res.data.properties;
                $rootScope.$broadcast('gcOperationalLayerChange', '', 'applyall');
                $rootScope.$broadcast('reloadDatatable');
                gaDomUtils.hideGlobalLoader();
                AlertHpoFactory.successSave();
              }
              else {
                gaDomUtils.hideGlobalLoader();
                AlertHpoFactory.showErrorMessage(res);
              }
            },
            () => {
              gaDomUtils.hideGlobalLoader();
              AlertHpoFactory.failSave();
            }
          );
      }
      else {
        console.log('no id');
      }
    };

    /**
     * Prépare les features avant le calcul d'un chantier.
     * Cette fonction est appelée lors de l'ajout de nouvelles features à un chantier.
     * Elle met à jour la propriété FTIUID de chaque feature avec l'identifiant du FTI ajouté,
     * puis met à jour les features dans la base de données via EditFactory.
     *
     * Pendant la mise à jour, le drapeau preparingCalculerChantier est activé
     * pour éviter que l'attribut FTIUID ne soit vide quand on lance le calcul.
     * L'attribut FTIUID doit absolument être rempli pour que le calcul fonctionne.
     *
     * @param {Object} params - Les paramètres de préparation
     * @param {Object} params.relation - La relation entre les features
     * @param {string} params.relation.idStart - L'identifiant du composant en début de relation
     * @param {string} params.relation.idEnd - L'identifiant du composant en fin de relation
     * @param {Object} params.editedFeatures - Les features à mettre à jour (GeoJson)
     * @param {Array} params.editedFeatures.features - Tableau des features à mettre à jour
     * @param {string} params.objectAddedFtiUid - L'identifiant du FTI ajouté
     * @returns {void}
     */
    const prepareCalculerChantier = (params) => {
      if (params.relation.idStart===INDIGAU_FTIS['chantiers'].uid
        || params.relation.idEnd===INDIGAU_FTIS['chantiers'].uid
      ) {
        for (const feature of params.editedFeatures.features) {
          feature.properties.FTIUID = params.objectAddedFtiUid;
        }
        preparingCalculerChantier = true;
        EditFactory.update(
          INDIGAU_FTIS['chantiers'].uid,
          params.editedFeatures,
          params.srid
        ).finally(() => {
          preparingCalculerChantier = false;
        });
      }
    };


    return {
      calculerChantier: calculerChantier,
      prepareCalculerChantier: prepareCalculerChantier
    };
  };
  IndigauFormFactory.$inject = ['FeatureTypeFactory', 'gaDomUtils', '$http', '$timeout',
    'AlertHpoFactory', '$rootScope', 'ApplicationFactory', 'ConfigFactory', '$q', 'EditFactory'];
  return IndigauFormFactory;
});
