'use strict';
define(function() {
  var elasticQueryService = function($http, $filter, $q) {
    var elasticQueryService = {};

    function getTimezoneOffset(d) {
      var timezone_offset_min = d.getTimezoneOffset(),
        offset_hrs = parseInt(Math.abs(timezone_offset_min / 60)),
        offset_min = Math.abs(timezone_offset_min % 60),
        timezone_standard;

      if (offset_hrs < 10) offset_hrs = '0' + offset_hrs;

      if (offset_min < 10) offset_min = '0' + offset_min;

      // Add an opposite sign to the offset
      // If offset is 0, it means timezone is UTC
      if (timezone_offset_min < 0)
        timezone_standard = '+' + offset_hrs + ':' + offset_min;
      else if (timezone_offset_min > 0)
        timezone_standard = '-' + offset_hrs + ':' + offset_min;
      else if (timezone_offset_min == 0) timezone_standard = 'Z';

      return timezone_standard;
    }

    function toFilters(query, fieldMap) {
      var filters = query.map(parseQueryGroup.bind(query, fieldMap));
      return filters;
    }

    function toQuery(filters, fieldMap) {
      var query = filters.map(parseFilter.bind(filters));
      // ajout filtre sur les undefined eventuels
      query = query.filter(function(x) {
        return x != null;
      });
      return query;
    }

    function parseQueryGroup(fieldMap, group, truthy) {
      if (truthy !== false) truthy = true;

      var key = Object.keys(group)[0],
        typeMap = {
          or: 'group',
          and: 'group',
          range: 'number',
        },
        type = typeMap[key] || 'item',
        obj = getFilterTemplate(type);

      switch (key) {
        case 'or':
        case 'and':
          obj.rules = group[key].map(parseQueryGroup.bind(group, fieldMap));
          obj.subType = key;
          break;
        case 'missing':
        case 'exists':
          obj.field = group[key].field;
          obj.subType = {
            exists: 'exists',
            missing: 'notExists',
          }[key];
          delete obj.value;
          break;
        case 'match':
        case 'terms':
          obj.field = Object.keys(group[key])[0];
          var fieldData = fieldMap[Object.keys(group[key])[0]];

          if (fieldData.type === 'multi') {
            var vals = group[key][obj.field];
            if (typeof vals === 'string') vals = [vals];
            obj.values = fieldData.choices.reduce(function(prev, choice) {
              prev[choice] = truthy === ~group[key][obj.field].indexOf(choice);
              return prev;
            }, {});
          } else {
            obj.subType = truthy ? 'equals' : 'notEquals';
            obj.value = group[key][obj.field];

            if (typeof obj.value === 'number') {
              obj.subType = 'boolean';
            }
          }
          break;
        case 'range':
          var date, parts;
          obj.field = Object.keys(group[key])[0];
          obj.subType = Object.keys(group[key][obj.field])[0];

          if (angular.isNumber(group[key][obj.field][obj.subType])) {
            obj.value = group[key][obj.field][obj.subType];
            break;
          }

          if (angular.isDefined(Object.keys(group[key][obj.field])[1])) {
            date = group[key][obj.field].gte;

            if (~date.indexOf('now-')) {
              obj.subType = 'last';
              obj.value = parseInt(date.split('now-')[1].split('d')[0]);
              break;
            }

            if (~date.indexOf('now')) {
              obj.subType = 'next';
              date = group[key][obj.field].lte;
              obj.value = parseInt(date.split('now+')[1].split('d')[0]);
              break;
            }

            obj.subType = 'equals';
            parts = date.split('T')[0].split('-');
            obj.date = parts[2] + '/' + parts[1] + '/' + parts[0];
            break;
          }

          date = group[key][obj.field][obj.subType];
          parts = date.split('T')[0].split('-');
          obj.date = parts[2] + '/' + parts[1] + '/' + parts[0];
          break;

        case 'not':
          obj = parseQueryGroup(fieldMap, group[key].filter, false);
          break;
        default:
          obj.field = Object.keys(group[key])[0];
          break;
      }

      return obj;
    }

    /**
     * ajoute la condition 'not equals' au filtre elastic qui sera envoyé au backend 
     * @param {*} filter le filtre elastic au format kis(front)
     * @param {*} elasticObject le filtre elastic au format elasticSearch
     */
    const addFilterValueWhenNotEquals = (filter, elasticObject) => {
      elasticObject.not = { filter: { match: {} } };
      if (filter.attr && Array.isArray(filter.attr.restrictions) && filter.attr.restrictions[0] && filter.restrictedValue != undefined) {
        elasticObject.not.filter.match[filter.name] = filter.restrictedValue[filter.name];
      } else if (filter.value !== undefined) {
        elasticObject.not.filter.match[filter.name] = filter.value;
      }
    };
    const addFilterValueWhenEquals = (filter, elasticCondition) => {
      if (filter.attr && Array.isArray(filter.attr.restrictions) && filter.attr.restrictions[0] && filter.restrictedValue != undefined) {
        elasticCondition[filter.name] = filter.restrictedValue[filter.name];
      } else if (filter.value !== undefined) {
        elasticCondition[filter.name] = filter.value;
      }
    };
    function parseFilter(filter) {
      // console.log(filter);
      var dateFormat = 'yyyy-MM-dd';
      var obj = {};
      var subtype;
      switch (filter.type) {
        /// CAS DU STRING
        case 'java.lang.String':
          switch (filter.operand) {
            case 'startWith':
              if (filter.value === undefined) return;
              obj.prefix = {};
              obj.prefix[filter.name] = filter.value;
              break;
            case 'endWith':
              if (filter.value === undefined) return;
              obj.regexp = {};
              obj.regexp[filter.name] = '.*' + filter.value;
              break;
            case 'regexp':
              if (filter.value === undefined) return;
              obj.regexp = {};
              obj.regexp[filter.name] = '.*' + filter.value + '.*';
              break;
            case 'equals':
              obj.match = {};
              addFilterValueWhenEquals(filter, obj.match);
              break;
            case 'boolean':
              if (filter.value === undefined) return;
              obj.match = {};
              obj.match[filter.name] = filter.value;
              break;
            case 'notEquals':
              addFilterValueWhenNotEquals(filter, obj);
              break;
            case 'include':
            case 'anc_ville_regie':
            case 'anc_ville_dsp':
              if (filter.value === undefined) return;
              obj.terms = {};
              obj.terms[filter.name] = filter.value.split(',');
              break;
            case 'exists':
              obj.exists = { field: filter.name };
              break;
            case 'notExists':
              obj.missing = { field: filter.name };
              break;
            /**
             * Si le statut de rdv egale 'present' , il faut inclure dans la recherche les
             * les controles avec le statut rdv null
             */
            case 'statut_rdv_present':
              const objMatch = {
                bool: {
                  must: [
                    {
                      match: {
                        statut_rdv: 'present',
                      },
                    },
                  ],
                },
              };
              const objMissing = {
                bool: {
                  must: [
                    {
                      missing: {
                        field: filter.name,
                      },
                    },
                  ],
                },
              };
              obj.bool = {};
              obj.bool.should = [];
              obj.bool.should[0] = objMissing;
              obj.bool.should[1] = objMatch;
              break;
            default:
              console.error('unexpected subtype ' + filter.operand);
          }
          break;
        //CAS DU FLOAT
        case 'java.lang.Float':
        case 'java.lang.Double':
        case 'java.lang.Integer':
          switch (filter.operand) {
            case 'equals':
              obj.term = {};
              addFilterValueWhenEquals(filter, obj.term);
              break;
            case 'notEquals':
              if (filter.value === undefined) return;
              addFilterValueWhenNotEquals(filter, obj);
              break;
            case 'exists':
              obj.exists = { field: filter.name };
              break;
            case 'notExists':
              obj.missing = { field: filter.name };
              break;
            default:
              obj.range = {};
              obj.range[filter.name] = {};
              obj.range[filter.name][filter.operand] = filter.value;
              break;
          }
          break;

        //CAS DU Boolean
        case 'java.lang.Boolean':
          obj.term = {};
          obj.term[filter.name] = filter.value;
          break;

        case 'java.sql.Timestamp':
        case 'java.util.Date':
        case 'java.sql.Date':
          if (!filter.operand) return;
          // @RB ajoute sans quoi un champ vide dans la date avait une influence sur les resultats

          if (filter.value === undefined || filter.value == null) return;
          if (
            !filter.showAllValues &&
            filter.newValueUtil &&
            filter.newValueUtil[filter.name]
          )
            filter.value = filter.newValueUtil[filter.name];

          // avant toute modif on stock la date initiale
          var baseFilterValue = new Date(filter.value);
          if (
            !filter.showAllValues &&
            filter.newValueUtil &&
            filter.value &&
            (filter.value instanceof Date ||
              (filter.value.toLowerCase &&
                filter.value.toLowerCase().indexOf('t') !== -1))
          )
            filter.value = $filter('date')(filter.value, dateFormat);

          switch (filter.operand) {
            case 'equals':
              obj.range = {};
              obj.range[filter.name] = {};
              //obj.range[filter.name]['gte'] = formatDate($filter, filter.value, dateFormat);
              obj.range[filter.name]['gte'] = filter.value;
              /*obj.range[filter.name]['gte'] = obj.range[filter.name]['gte'].replace("T"," ").replace("Z","")
                                                                         .substring(0,obj.range[filter.name]['gte'].replace("T"," ").replace("Z","").length-9);*/
              obj.range[filter.name]['lte'] = obj.range[filter.name]['gte'];
              obj.range[filter.name].format = dateFormat;
              obj.range[filter.name].time_zone = getTimezoneOffset(
                baseFilterValue
              );
              break;
            case 'lt':
            case 'lte':
              //if (!angular.isDate(filter.value)) return;
              obj.range = {};
              obj.range[filter.name] = {};
              obj.range[filter.name][filter.operand] = filter.value; //.replace("Z","+0000");
              /*obj.range[filter.name][filter.operand] = obj.range[filter.name][filter.operand].replace("T"," ").replace("Z","")
                                                                         .substring(0,obj.range[filter.name][filter.operand].replace("T"," ").replace("Z","").length-9);*/
              obj.range[filter.name].format = dateFormat;
              obj.range[filter.name].time_zone = getTimezoneOffset(
                baseFilterValue
              );
              break;
            case 'gt':
            case 'gte':
              //if (!angular.isDate(filter.value)) return;
              obj.range = {};
              obj.range[filter.name] = {};
              obj.range[filter.name][filter.operand] = filter.value; //.replace("Z","+0000");
              /*obj.range[filter.name][filter.operand] = obj.range[filter.name][filter.operand].replace("T"," ").replace("Z","")
                                                                         .substring(0,obj.range[filter.name][filter.operand].replace("T"," ").replace("Z","").length-9);*/
              obj.range[filter.name].format = dateFormat;
              obj.range[filter.name].time_zone = getTimezoneOffset(
                baseFilterValue
              );

              break;
            case 'last':
              //if (!angular.isNumber(group.value)) return;
              obj.range = {};
              obj.range[filter.name] = {};
              obj.range[filter.name].gte = 'now-' + filter.newvalue + 'd';
              obj.range[filter.name].lte = 'now';
              break;
            case 'next':
              if (!angular.isNumber(filter.value)) return;
              obj.range = {};
              obj.range[filter.name] = {};
              obj.range[filter.name].gte = 'now';
              obj.range[filter.name].lte = 'now+' + filter.newvalue + 'd';
              break;
            case 'exists':
              obj.exists = { field: filter.name };
              break;
            case 'notExists':
              obj.missing = { field: filter.name };
              break;
            default:
              console.error('unexpected subtype ' + filter.operand);
          }
          break;
      }

      return obj;
    }

    function parseDeepControle(filter) {
      var dateFormat = 'yyyy-MM-dd';
      var obj = {};
      var subtype;
      switch (filter.questionchoosen.type) {
        case 'text':
        case 'textarea':
          switch (filter.operand) {
            case 'startWith':
              if (filter.value === undefined) return;
              obj.prefix = {};
              obj.prefix[filter.questionchoosen.key.replace('cdata.', '')] =
                filter.value;
              break;
            case 'endWith':
              if (filter.value === undefined) return;
              obj.regexp = {};
              obj.regexp[filter.questionchoosen.key.replace('cdata.', '')] =
                '.*' + filter.value;
              break;
            case 'regexp':
              if (filter.value === undefined) return;
              obj.regexp = {};
              obj.regexp[filter.questionchoosen.key.replace('cdata.', '')] =
                '.*' + filter.value + '.*';
              break;
            case 'equals':
            case 'boolean':
              if (filter.value === undefined) return;
              obj.match = {};
              obj.match[filter.questionchoosen.key.replace('cdata.', '')] =
                filter.value;
              break;
            case 'notEquals':
              if (filter.value === undefined) return;
              obj.not = { filter: { match: {} } };
              obj.not.filter.match[
                filter.questionchoosen.key.replace('cdata.', '')
              ] = filter.value;
              break;
            case 'include':
              if (filter.value === undefined) return;
              obj.terms = {};
              obj.terms[
                filter.questionchoosen.key.replace('cdata.', '')
              ] = filter.value.split(',');
              break;
            case 'exists':
              obj.exists = {
                field: filter.questionchoosen.key.replace('cdata.', ''),
              };
              break;
            case 'notExists':
              obj.missing = {
                field: filter.questionchoosen.key.replace('cdata.', ''),
              };
              break;
            default:
              console.error('unexpected subtype ' + filter.operand);
          }
          break;
        //CAS DU FLOAT
        case 'number':
          switch (filter.operand) {
            case 'equals':
              obj.term = {};
              obj.term[filter.questionchoosen.key.replace('cdata.', '')] = {};
              obj.term[filter.questionchoosen.key.replace('cdata.', '')] =
                filter.value;
              break;
            case 'notEquals':
              if (filter.value === undefined) return;
              obj.not = { filter: { match: {} } };
              obj.not.filter.match[
                filter.questionchoosen.key.replace('cdata.', '')
              ] = filter.value;
              break;
            case 'exists':
              obj.exists = {
                field: filter.questionchoosen.key.replace('cdata.', ''),
              };
              break;
            case 'notExists':
              obj.missing = {
                field: filter.questionchoosen.key.replace('cdata.', ''),
              };
              break;
            default:
              obj.range = {};
              obj.range[filter.questionchoosen.key.replace('cdata.', '')] = {};
              obj.range[filter.questionchoosen.key.replace('cdata.', '')][
                filter.operand
              ] = filter.value;
              break;
          }
          break;

        //CAS DU Boolean
        case 'radio':
          obj.term = {};
          obj.term[filter.questionchoosen.key.replace('cdata.', '')] = {};
          obj.term[filter.questionchoosen.key.replace('cdata.', '')] =
            filter.value.value;
          break;

        case 'date':
          if (!filter.operand) return;
          //if ( !filter.showAllValues && filter.newValueUtil ) filter.value = filter.newValueUtil.value;
          switch (filter.operand) {
            case 'equals':
              obj.range = {};
              obj.range[filter.questionchoosen.key.replace('cdata.', '')] = {};
              //obj.range[filter.questionchoosen.key.replace("cdata.","")]['gte'] = formatDate($filter, filter.value, dateFormat);
              obj.range[filter.questionchoosen.key.replace('cdata.', '')][
                'gte'
              ] = filter.value; //.replace("Z","+0000");
              /*obj.range[filter.questionchoosen.key.replace("cdata.","")]['gte'] = obj.range[filter.questionchoosen.key.replace("cdata.","")]['gte'].replace("T"," ").replace("Z","")
                                                                         .substring(0,obj.range[filter.questionchoosen.key.replace("cdata.","")]['gte'].replace("T"," ").replace("Z","").length-9);*/
              obj.range[filter.questionchoosen.key.replace('cdata.', '')][
                'lte'
              ] =
                obj.range[filter.questionchoosen.key.replace('cdata.', '')][
                  'gte'
                ];
              obj.range[
                filter.questionchoosen.key.replace('cdata.', '')
              ].format = dateFormat;
              break;
            case 'lt':
            case 'lte':
              //if (!angular.isDate(filter.value)) return;
              obj.range = {};
              obj.range[filter.questionchoosen.key.replace('cdata.', '')] = {};
              obj.range[filter.questionchoosen.key.replace('cdata.', '')][
                filter.operand
              ] = filter.value; //.replace("Z","+0000");
              /*obj.range[filter.questionchoosen.key.replace("cdata.","")][filter.operand] = obj.range[filter.questionchoosen.key.replace("cdata.","")][filter.operand].replace("T"," ").replace("Z","")
                                                                         .substring(0,obj.range[filter.questionchoosen.key.replace("cdata.","")][filter.operand].replace("T"," ").replace("Z","").length-9);*/
              obj.range[
                filter.questionchoosen.key.replace('cdata.', '')
              ].format = dateFormat;
              break;
            case 'gt':
            case 'gte':
              //if (!angular.isDate(filter.value)) return;
              obj.range = {};
              obj.range[filter.questionchoosen.key.replace('cdata.', '')] = {};
              obj.range[filter.questionchoosen.key.replace('cdata.', '')][
                filter.operand
              ] = filter.value; //.replace("Z","+0000");
              /*obj.range[filter.questionchoosen.key.replace("cdata.","")][filter.operand] = obj.range[filter.questionchoosen.key.replace("cdata.","")][filter.operand].replace("T"," ").replace("Z","")
                                                                         .substring(0,obj.range[filter.questionchoosen.key.replace("cdata.","")][filter.operand].replace("T"," ").replace("Z","").length-9);*/
              obj.range[
                filter.questionchoosen.key.replace('cdata.', '')
              ].format = dateFormat;
              break;
            case 'last':
              //if (!angular.isNumber(group.value)) return;
              obj.range = {};
              obj.range[filter.questionchoosen.key.replace('cdata.', '')] = {};
              obj.range[filter.questionchoosen.key.replace('cdata.', '')].gte =
                'now-' + filter.newvalue + 'd';
              obj.range[filter.questionchoosen.key.replace('cdata.', '')].lte =
                'now';
              break;
            case 'next':
              if (!angular.isNumber(filter.value)) return;
              obj.range = {};
              obj.range[filter.questionchoosen.key.replace('cdata.', '')] = {};
              obj.range[filter.questionchoosen.key.replace('cdata.', '')].gte =
                'now';
              obj.range[filter.questionchoosen.key.replace('cdata.', '')].lte =
                'now+' + filter.newvalue + 'd';
              break;
            case 'exists':
              obj.exists = {
                field: filter.questionchoosen.key.replace('cdata.', ''),
              };
              break;
            case 'notExists':
              obj.missing = {
                field: filter.questionchoosen.key.replace('cdata.', ''),
              };
              break;
            default:
              console.error('unexpected subtype ' + filter.operand);
          }
          break;
      }
      return obj;
    }

    function parseFilterGroup(fieldMap, $filter, group) {
      var obj = {};
      if (group.type === 'group') {
        obj[group.subType] = group.rules
          .map(parseFilterGroup.bind(group, fieldMap, $filter))
          .filter(function(item) {
            return !!item;
          });
        return obj;
      }

      var fieldName = group.field;
      var fieldData = fieldMap[fieldName];

      if (!fieldName) return;

      switch (fieldData.type) {
        case 'match':
          if (fieldData.subType === 'boolean') group.subType = 'boolean';

          if (!group.subType) return;
          switch (group.subType) {
            case 'equals':
            case 'boolean':
              if (group.value === undefined) return;
              obj.match = {};
              obj.match[fieldName] = group.value;
              break;
            case 'notEquals':
              if (group.value === undefined) return;
              obj.not = { filter: { match: {} } };
              obj.not.filter.match[fieldName] = group.value;
              break;
            case 'exists':
              obj.exists = { field: fieldName };
              break;
            case 'notExists':
              obj.missing = { field: fieldName };
              break;
            default:
              console.error('unexpected subtype ' + group.subType);
          }
          break;

        case 'number':
          obj.range = {};
          obj.range[fieldName] = {};
          obj.range[fieldName][group.subType] = group.value;
          break;

        case 'date':
          if (!group.subType) return;

          switch (group.subType) {
            case 'equals':
              if (!angular.isDate(group.date)) return;
              obj.match = {};
              obj.match[fieldName] = formatDate(
                $filter,
                group.date,
                group.dateFormat
              );
              break;
            case 'lt':
            case 'lte':
              if (!angular.isDate(group.date)) return;
              obj.range = {};
              obj.range[fieldName] = {};
              obj.range[fieldName][group.subType] = formatDate(
                $filter,
                group.date,
                group.dateFormat
              );
              break;
            case 'gt':
            case 'gte':
              if (!angular.isDate(group.date)) return;
              obj.range = {};
              obj.range[fieldName] = {};
              obj.range[fieldName][group.subType] = formatDate(
                $filter,
                group.date,
                group.dateFormat
              );
              break;
            case 'last':
              if (!angular.isNumber(group.value)) return;
              obj.range = {};
              obj.range[fieldName] = {};
              obj.range[fieldName].gte = 'now-' + group.value + 'd';
              obj.range[fieldName].lte = 'now';
              break;
            case 'next':
              if (!angular.isNumber(group.value)) return;
              obj.range = {};
              obj.range[fieldName] = {};
              obj.range[fieldName].gte = 'now';
              obj.range[fieldName].lte = 'now+' + group.value + 'd';
              break;
            case 'exists':
              obj.exists = { field: fieldName };
              break;
            case 'notExists':
              obj.missing = { field: fieldName };
              break;
            default:
              console.error('unexpected subtype ' + group.subType);
          }
          break;

        case 'multi':
          obj.terms = {};
          obj.terms[fieldName] = Object.keys(group.values || {}).reduce(
            function(prev, key) {
              if (group.values[key]) prev.push(key);

              return prev;
            },
            []
          );
          break;

        default:
          console.error('unexpected type');
      }

      return obj;
    }

    function getFilterTemplate(type) {
      var templates = {
        group: {
          type: 'group',
          subType: '',
          rules: [],
        },
        item: {
          field: '',
          subType: '',
          value: '',
        },
        number: {
          field: '',
          subType: '',
          value: null,
        },
      };

      return angular.copy(templates[type]);
    }

    function formatDate($filter, date) {
      //if (!angular.isDate(date)) return false;
      var dateFormat = 'yyyy-MM-ddTHH:mm:ss.sssZ';
      var fDate = $filter('date')(date, dateFormat);
      return fDate;
    }

    function ParseQuery(queries, size) {
      var query;
      if (!queries || queries == '') {
        query = {
          query: {
            match_all: {},
          },
          from: 0,
          size: size,
          sort: {
            _score: {
              order: 'asc',
            },
          },
        };
      } else {
        query = {
          query: {
            query: {
              bool: {
                must: queries,
              },
            },
          },
          from: 0,
          size: size,
          sort: {
            _score: {
              order: 'asc',
            },
          },
        };
      }
      return query;
    }

    function parseAncRelation(relation, opts, ancOuBac) {
      var result = {};

      switch (relation.choicesdone) {
        case $filter('translate')('elastic.search.anc.contient'):
          var parentChild =
            relation.relation_DOSSIER_CONTROLE_choosen.name == 'kis_anc_dossier'
              ? 'has_parent'
              : 'has_child';
          if (ancOuBac == 'bac')
            parentChild =
              relation.relation_DOSSIER_CONTROLE_choosen.name ==
              'kis_bac_dossier'
                ? 'has_parent'
                : 'has_child';

          if (relation.filters.length > 0) {
            var filters = relation.filters
              .map(function(filt) {
                var _filt = angular.copy(filt);

                // ajout du prefixe bac pour les filtres sur les json
                if (
                  ancOuBac == 'bac' && filt.selectedfti && 
                  filt.selectedfti.name == 'kis_bac_dossier_controle_reponse'
                ) {
                  _filt.name = 'bac.' + _filt.name;
                }

                if (filt.choice === 'rule') return parseFilter(_filt);
                else return parseAncRelation(_filt, opts, ancOuBac);
                // rb : on ne prends que les regles definies, dans certains cas le retour de parsefilter est undefined
                // par ex si on ne specifie pas de valeur dans une regle
                // j'ai besoin dans ce cas que la rule soit ignoree plutot que de planter la recherche (utile dans le cadre des requetes editbles de l'anc par ex)
              })
              .filter(function(x) {
                //  console.log(x);
                return angular.isDefined(x);
              });

            //console.log(filters);

            result[parentChild] = {
              type: relation.relation_DOSSIER_CONTROLE_choosen.name,
              query: {
                bool: {
                  must: filters,
                },
              },
            };

            result[parentChild].inner_hits = {};
          } else {
            result[parentChild] = {
              type: relation.relation_DOSSIER_CONTROLE_choosen.name,
              query: {
                match_all: {},
              },
            };

            result[parentChild].inner_hits = {};
          }
          break;
        case $filter('translate')('elastic.search.anc.contient_vidange'):
          var parentChild =
            relation.relation_DOSSIER_VIDANGE_choosen.name == 'kis_anc_dossier'
              ? 'has_parent'
              : 'has_child';

          if (relation.filters.length > 0) {
            var filters = relation.filters
              .map(function(filt) {
                if (filt.choice === 'rule') return parseFilter(filt);
                else return parseAncRelation(filt, opts, ancOuBac);
              })
              .filter(function(x) {
                console.log(x);
                return angular.isDefined(x);
              });

            console.log(filters);

            result[parentChild] = {
              type: relation.relation_DOSSIER_VIDANGE_choosen.name,
              query: {
                bool: {
                  must: filters,
                },
              },
            };

            result[parentChild].inner_hits = {};
          } else {
            result[parentChild] = {
              type: relation.relation_DOSSIER_VIDANGE_choosen.name,
              query: {
                match_all: {},
              },
            };

            result[parentChild].inner_hits = {};
          }
          break;

        default:
          var f = parseDeepControle(relation);
          console.log(f);

          result = {
            has_child: {
              type:
                ancOuBac == 'bac'
                  ? 'kis_bac_dossier_controle_reponse'
                  : 'kis_anc_dossier_controle_reponse',
              query: {
                bool: {
                  must: f,
                },
              },
            },
          };

          result.has_child.inner_hits = {};
          break;
      }
      //console.log(result);

      return result;
    }

    function parseRelationReverse(relation, opts, type) {
      var b;
      if (type == 'controle') {
        b = {
          parent: relation.selectedfti.name,
          child: relation.relation_DOSSIER_CONTROLE_choosen.name,
          jointure:
            opts[0][relation.relation_DOSSIER_CONTROLE_choosen.name] &&
            opts[0][relation.relation_DOSSIER_CONTROLE_choosen.name].jointure
              ? true
              : false,
        };
      }
      if (type == 'vidange') {
        b = {
          parent: relation.selectedfti.name,
          child: relation.relation_DOSSIER_VIDANGE_choosen.name,
          jointure:
            opts[0][relation.relation_DOSSIER_VIDANGE_choosen.name] &&
            opts[0][relation.relation_DOSSIER_VIDANGE_choosen.name].jointure
              ? true
              : false,
        };
      }
      return b;
    }

    function parseAncRelationReverse(relation, result, opts, ancOuBac) {
      if (
        relation.choicesdone &&
        relation.choicesdone ===
          $filter('translate')('elastic.search.anc.contient')
      ) {
        var b = parseRelationReverse(relation, opts, 'controle');
        if (b) result.push(b);
        if (relation.filters.length > 0) {
          relation.filters.map(function(filt) {
            if (filt.choice === 'relation')
              result = parseAncRelationReverse(filt, result, opts, ancOuBac);
          });
        }
      }
      if (
        relation.choicesdone &&
        relation.choicesdone ===
          $filter('translate')('elastic.search.anc.contient_vidange')
      ) {
        var b = parseRelationReverse(relation, opts, 'vidange');
        if (b) result.push(b);
        if (relation.filters.length > 0) {
          relation.filters.map(function(filt) {
            if (filt.choice === 'relation')
              result = parseAncRelationReverse(filt, result, opts, ancOuBac);
          });
        }
      } else {
        if (relation.filters && relation.filters.length > 0) {
          relation.filters.map(function(filt) {
            if (filt.choice === 'relation')
              result = parseAncRelationReverse(filt, result, opts, ancOuBac);
          });
        }
      }
      return result;
    }

    function parseAncRelationR(relation) {
      var result = {};
      switch (relation.choicesdone) {
        case $filter('translate')('elastic.search.anc.contient'):
          if (relation.filters.length > 0) {
            var filters = relation.filters.map(function(filt) {
              if (filt.choice === 'rule') return parseFilter(filt);
              else return parseAncRelationR(filt);
            });
            result = filters;
          } else {
            result = { match_all: {} };
          }
          break;
        /*case "Contrôle de conception":
                case "Contrôle de bonne éxécution":
                case "Contrôle de bon fonctionnement":*/
        default:
          result = parseDeepControle(relation);
          break;
      }
      return result;
    }

    function ParseRelations(relations) {
      var result = [];

      angular.forEach(relations, function(relation) {
        switch (relation.relation) {
          case 'REL_NM':
            var res = parseRelNM(relation);
            result.push(res);
            break;
          case 'REL_SIMPLE':
            var res = parseRelSIMPLE(relation);
            result.push(res);
            break;
          case 'REL_TOPOLOGY':
            var res = parseRelTOPOLOGY(relation);
            result.push(res);
            break;
        }
      });

      return result;
    }

    function parseRelTOPOLOGY(relation) {
      var result = {
        type: relation.relation,
        fti: relation.relation_TOPOLOGY_choosen,
        operand: relation.operand,
        getBackResult: relation.getBackResult,
      };

      if (relation.filters) {
        var queries = relation.filters
          .map(function(x) {
            if (x.choice === 'rule') return x;
          })
          .filter(function(x) {
            if (x) return x;
          });
        if (queries && queries.length > 0) {
          var condition = '';
          if (queries) condition = toQuery(queries, undefined);
          result.query = ParseQuery(condition, 10000);
        }

        [];
        var relationsRecursive = relation.filters
          .map(function(x) {
            if (x.choice === 'relation') return x;
          })
          .filter(function(x) {
            if (x) return x;
          });
        result.relations = ParseRelations(relationsRecursive);
      }

      return result;
    }

    function parseRelNM(relation) {
      var result = {
        type: relation.relation,
        fti:
          relation.relation === 'REL_SIMPLE'
            ? relation.relation_SIMPLE_choosen
            : relation.relation_NM_choosen,
        relationsNames: relation.relationName,
        getBackResult: relation.getBackResult,
      };

      if (relation.filters) {
        var queries = relation.filters
          .map(function(x) {
            if (x.choice === 'rule') return x;
          })
          .filter(function(x) {
            if (x) return x;
          });
        if (queries && queries.length > 0) {
          var condition = '';
          if (queries) condition = toQuery(queries, undefined);
          result.query = ParseQuery(condition, 10000);
        }

        [];
        var relationsRecursive = relation.filters
          .map(function(x) {
            if (x.choice === 'relation') return x;
          })
          .filter(function(x) {
            if (x) return x;
          });
        result.relations = ParseRelations(relationsRecursive);
      }

      return result;
    }

    function parseRelSIMPLE(relation) {
      var result = {
        type: relation.relation,
        fti: relation.relation_SIMPLE_choosen,
        relationsNames: relation.relationName,
        getBackResult: relation.getBackResult,
      };

      if (relation.filters) {
        var queries = relation.filters
          .map(function(x) {
            if (x.choice === 'rule') return x;
          })
          .filter(function(x) {
            if (x) return x;
          });
        if (queries && queries.length > 0) {
          var condition = '';
          if (queries) condition = toQuery(queries, undefined);
          result.query = ParseQuery(condition, 10000);
        }

        [];
        var relationsRecursive = relation.filters
          .map(function(x) {
            if (x.choice === 'relation') return x;
          })
          .filter(function(x) {
            if (x) return x;
          });
        result.relations = ParseRelations(relationsRecursive);
      }

      var simple_relations = [];
      relation.selectedfti.relations.map(function(rel) {
        if (
          rel.type === 'REL_SIMPLE' &&
          (rel.componentEnd === relation.relation_SIMPLE_choosen.name ||
            rel.componentStart === relation.relation_SIMPLE_choosen.name)
        ) {
          var val = {};
          val[relation.relation_SIMPLE_choosen.name] = rel.fieldEnd;
          val[relation.selectedfti.name] = rel.fieldStart;
          simple_relations.push(val);
        }
      });
      result.relation = simple_relations;

      return result;
    }

    function parseFilterJ(filter, ftis) {
      var f = {};
      switch (filter.relation) {
        case 'REL_NM':
          f.fti = filter.relation_NM_choosen;
          f.relationnameNM = filter.relationName[0];
          f.relationNM = filter.relation;
          f.relationNMAlias = filter.choicesdone;
          break;
        case 'REL_SIMPLE':
          f.fti = filter.relation_SIMPLE_choosen;
          f.relationnameSIMPLE = filter.relationName[0];
          f.relationSIMPLE = filter.relation;
          f.relationSIMPLEAlias = filter.choicesdone;
          break;
        case 'REL_TOPOLOGY':
          f.fti = filter.relation_TOPOLOGY_choosen;
          f.operand = filter.operand;
          f.relationnameNM = filter.relationName;
          f.relationNM = filter.relation;
          f.relationNMAlias = filter.choicesdone;
          break;
      }
      if (filter.filters && filter.filters.length > 0)
        f = parseFiltersJointure(f, filter, ftis);
      return f;
    }

    function parseFiltersJointure(res, relation, ftis) {
      var filters = [];
      for (var i = 0; i < relation.filters.length; i++) {
        var filter = relation.filters[i];
        if (filter.choice === 'relation') {
          var filterJointure = parseFilterJ(filter, ftis);
          var existingftis = filters.map(function(x) {
            return x.fti.name;
          });
          var index = existingftis.indexOf(filterJointure.fti.name);
          if (index === -1) filters.push(filterJointure);
          else angular.extend(filters[index], filterJointure);
        }
      }
      res.filters = filters;
      return res;
    }

    function parseJointureRelation(ftis, relations, result, savedinfo) {
      var defer = $q.defer();
      if (!savedinfo) {
        for (var i = 0; i < relations.length; i++) {
          var relation = relations[i];
          relation.selectedfti =
            ftis[
              ftis
                .map(function(x) {
                  return x.alias;
                })
                .indexOf(relation.selectGlobaladvancedFti)
            ];
          var res = { type: 'master', fti: relation.selectedfti };
          if (relation.filters && relation.filters.length > 0)
            res = parseFiltersJointure(res, relation, ftis);
          result.push(res);
        }

        defer.resolve(result);
      } else {
        defer.resolve(savedinfo);
      }
      return defer.promise;
    }

    function parseJointureRelations(ftis, relations, savedinfo) {
      var result = [];
      return parseJointureRelation(ftis, relations, result, savedinfo);
    }

    function parseF(filter, uid, name, attributes) {
      var f = {};
      if (filter.simplejointure) {
        f.nameRelationSimple = filter.relationnameSIMPLE;
        f.relationSimpleEndUid = filter.fti.uid;
        f.relationSimpleEndName = filter.fti.name;
        f.relationSimpleEndftiattr = filter.fti.attributes
          .map(function(x) {
            if (x.selected) return x;
          })
          .filter(function(x) {
            if (x) return x;
          });
        f.relationSimpleEndAttributes = f.relationSimpleEndftiattr
          .map(function(x) {
            if (x) return x.name;
          })
          .filter(function(x) {
            if (x) return x;
          });
        f.relationSimpleStartUid = uid;
        f.relationSimpleStartName = name;
        f.relationSimpleStartAttributes = attributes;
      }
      f.jointuresimple = filter.simplejointure;
      if (filter.nmjointure) {
        f.jointure = filter.nmjointure;
        f.nameRelationNM = filter.relationnameNM;
        f.nameRelationTOPO = filter.operand;
        f.relationEndUid = filter.fti.uid;
        f.relationEndName = filter.fti.name;
        f.relationEndftiattr = filter.fti.attributes
          .map(function(x) {
            if (x.selected) return x;
          })
          .filter(function(x) {
            if (x) return x;
          });
        f.relationEndAttributes = f.relationEndftiattr
          .map(function(x) {
            if (x) return x.name;
          })
          .filter(function(x) {
            if (x) return x;
          });
        f.typeofjointure = filter.jointurenmtype;
        f.relationStartUid = uid;
        f.relationStartName = name;
        f.relationStartAttributes = attributes;
      }
      f.jointure = filter.nmjointure;
      return f;
    }

    function parseFilterO(filterb, filters, uid, name, attributes) {
      var f = parseF(filterb, uid, name, attributes);
      if (filterb.filters && filterb.filters.length > 0) {
        for (var i = 0; i < filterb.filters.length; i++) {
          var filter = filterb.filters[i];
          if (filter.fti) {
            var attr = filterb.fti.attributes
              .map(function(x) {
                if (x.selected) return x.name;
              })
              .filter(function(x) {
                if (x) return x;
              });
            filters = parseFilterO(
              filter,
              filters,
              filterb.fti.uid,
              filterb.fti.name,
              attr
            );
          }
        }
      }
      filters.push(f);
      return filters;
    }

    function parseFiltersJointureOptions(res, relation, uid, name, attributes) {
      var filters = [];
      for (var i = 0; i < relation.filters.length; i++) {
        var filter = relation.filters[i];
        if (filter.fti)
          filters = parseFilterO(filter, filters, uid, name, attributes);
      }
      res.relations = filters;
      return res;
    }

    function parseJointureOptions(relations, result) {
      var defer = $q.defer();

      for (var i = 0; i < relations.length; i++) {
        var relation = relations[i];
        var attributes = relation.fti.attributes
          .map(function(x) {
            if (x.selected) return x.name;
          })
          .filter(function(x) {
            if (x) return x;
          });
        var res = {
          type: 'master',
          ftiuid: relation.fti.uid,
          ftiname: relation.fti.name,
          attributes: attributes,
          fti: relation.fti,
        };
        if (relation.filters && relation.filters.length > 0)
          res = parseFiltersJointureOptions(
            res,
            relation,
            relation.fti.uid,
            relation.fti.name,
            attributes
          );

        result.push(res);
      }

      defer.resolve(result);
      return defer.promise;
    }

    function parseOptions(elements) {
      var result = [];
      return parseJointureOptions(elements, result);
    }

    function parseFilterJAnc(res, filter, ftis, type) {
      var f = {};
      f.type = 'ANC';
      if (type == 'controle') {
        f.fti = filter.relation_DOSSIER_CONTROLE_choosen;
      }
      if (type == 'vidange') {
        f.fti = filter.relation_DOSSIER_VIDANGE_choosen;
      }

      res.filters.push(f);

      if (filter.filters && filter.filters.length > 0)
        parseFiltersJointureAnc(res, filter, ftis);
    }

    function parseFiltersJointureAnc(res, relation, ftis) {
      for (var i = 0; i < relation.filters.length; i++) {
        var filter = relation.filters[i];
        if (
          filter.choice === 'relation' &&
          filter.relation_DOSSIER_CONTROLE_choosen
        ) {
          parseFilterJAnc(res, filter, ftis, 'controle');
        }
        if (
          filter.choice === 'relation' &&
          filter.relation_DOSSIER_VIDANGE_choosen
        ) {
          parseFilterJAnc(res, filter, ftis, 'vidange');
        }
      }
    }

    function parseJointureRelationAnc(ftis, relations, result, resultsaved) {
      var defer = $q.defer();
      if (!resultsaved) {
        for (var i = 0; i < relations.length; i++) {
          var relation = relations[i];
          var ind = ftis
            .map(function(x) {
              return x.alias;
            })
            .indexOf(relation.selectGlobaladvancedFti);
          if (ind !== -1) {
            relation.selectedfti = ftis[ind];
            var res = { type: 'master', fti: relation.selectedfti };
            if (relation.filters && relation.filters.length > 0) {
              res.filters = [];
              parseFiltersJointureAnc(res, relation, ftis);
            }
            result.push(res);
          }
        }

        defer.resolve(result);
      } else {
        defer.resolve(resultsaved);
      }
      return defer.promise;
    }

    function parseJointureRelationsAnc(ftis, relations, resultsaved) {
      var result = [];
      return parseJointureRelationAnc(ftis, relations, result, resultsaved);
    }

    function parseJointureOptionsANC(relations, result) {
      var defer = $q.defer();

      for (var i = 0; i < relations.length; i++) {
        var relation = relations[i];
        var attributes = relation.fti.attributes
          .map(function(x) {
            if (x.selected) return x.name;
          })
          .filter(function(x) {
            if (x) return x;
          });
        var res = {};

        res[relation.fti.name] = {
          type: 'master',
          ftiuid: relation.fti.uid,
          ftiname: relation.fti.name,
          attributes: attributes,
          fti: relation.fti,
        };

        if (relation.filters && relation.filters.length > 0) {
          for (var j = 0; j < relation.filters.length; j++) {
            var filter = relation.filters[j];
            var attributesf = filter.fti.attributes
              .map(function(x) {
                if (x.selected) return x.name;
              })
              .filter(function(x) {
                if (x) return x;
              });
            res[filter.fti.name] = {
              type: 'child',
              ftiuid: filter.fti.uid,
              ftiname: filter.fti.name,
              attributes: attributesf,
              fti: filter.fti,
              jointure: filter.ancjointure,
              jointureType: filter.jointurenmtype,
            };
          }
        }

        result.push(res);
      }

      defer.resolve(result);
      return defer.promise;
    }

    function parseOptionsANC(elements) {
      var result = [];
      return parseJointureOptionsANC(elements, result);
    }

    return {
      elasticQueryService: elasticQueryService,
      toFilters: toFilters,
      toQuery: toQuery,
      parseQueryGroup: parseQueryGroup,
      parseFilterGroup: parseFilterGroup,
      getFilterTemplate: getFilterTemplate,
      formatDate: formatDate,
      ParseQuery: ParseQuery,
      ParseRelations: ParseRelations,
      parseFilter: parseFilter,
      parseAncRelation: parseAncRelation,
      parseJointureRelations: parseJointureRelations,
      parseOptions: parseOptions,
      parseJointureRelationsAnc: parseJointureRelationsAnc,
      parseOptionsANC: parseOptionsANC,
      parseAncRelationReverse: parseAncRelationReverse,
    };
  };
  elasticQueryService.$inject = ['$http', '$filter', '$q'];
  return elasticQueryService;
});
