// Generated by CoffeeScript 1.12.7
ez5.IUCNUtil = (function() {
  function IUCNUtil() {}

  IUCNUtil.LINK_FIELD_SEPARATOR = ":__link:";

  IUCNUtil.getFieldType = function() {
    return "custom:base.custom-data-type-iucn.iucn";
  };

  IUCNUtil.getAssessmentData = function(plugin_endpoint, assessment_id) {
    return ez5.IUCNUtil.getFromPlugin("/assessment/" + assessment_id, plugin_endpoint);
  };

  IUCNUtil.searchByTaxonname = function(plugin_endpoint, genus, species) {
    return ez5.IUCNUtil.getFromPlugin("/taxa/scientific_name?genus_name=" + encodeURIComponent(genus) + "&species_name=" + encodeURIComponent(species), plugin_endpoint);
  };

  IUCNUtil.searchBySisTaxonId = function(plugin_endpoint, sis_taxon_id) {
    return ez5.IUCNUtil.getFromPlugin("/taxa/sis/" + sis_taxon_id, plugin_endpoint);
  };

  IUCNUtil.getFromPlugin = function(iucn_query, url) {
    var url_data, xhr;
    if (url == null) {
      url = null;
    }
    url = url || ez5.IUCNUtil.getPluginEndpoint();
    url_data = {
      "iucn_query": iucn_query
    };
    xhr = new CUI.XHR({
      method: "GET",
      url: CUI.appendToUrl(url, url_data),
      headers: {
        "authorization": 'Bearer ' + ez5.session.token,
        "x-easydb-token": ez5.session.token
      }
    });
    return xhr.start();
  };

  IUCNUtil.setObjectData = function(object, data) {
    var i, len, n, ref, ref1;
    if (CUI.util.isEmpty(data)) {
      delete object.idTaxon;
      delete object.scientificName;
      delete object.mainCommonName;
      delete object.category;
      delete object.redList;
      return;
    }
    if (CUI.util.isArray(data)) {
      data = data[0];
    }
    object.redList = false;
    object.category = "";
    object.mainCommonName = "";
    object.scientificName = data.scientific_name;
    if (!data.sis_taxon_id) {
      return object;
    }
    object.idTaxon = "" + data.sis_taxon_id;
    if (!data.taxon) {
      return object;
    }
    object.scientificName = data.taxon.scientific_name || "";
    if (data.red_list_category.code) {
      object.category = data.red_list_category.code;
      object.redList = (ref = data.red_list_category.code) === "EX" || ref === "EW" || ref === "CR" || ref === "EN" || ref === "VU";
    }
    if (!data.taxon.common_names) {
      return object;
    }
    if (!CUI.util.isArray(data.taxon.common_names)) {
      return object;
    }
    ref1 = data.taxon.common_names;
    for (i = 0, len = ref1.length; i < len; i++) {
      n = ref1[i];
      if (!n.main) {
        continue;
      }
      if (!n.name) {
        continue;
      }
      object.mainCommonName = n.name;
      break;
    }
    return object;
  };

  IUCNUtil.isEqual = function(objectOne, objectTwo) {
    var i, key, len, ref;
    ref = ["idTaxon", "scientificName", "mainCommonName", "category", "redList"];
    for (i = 0, len = ref.length; i < len; i++) {
      key = ref[i];
      if (!CUI.util.isEqual(objectOne[key], objectTwo[key])) {
        return false;
      }
    }
    return true;
  };

  IUCNUtil.getSaveData = function(data) {
    var saveData;
    saveData = {
      idTaxon: data.idTaxon,
      scientificName: data.scientificName,
      mainCommonName: data.mainCommonName,
      category: data.category,
      redList: data.redList,
      _fulltext: {
        text: data.scientificName + " " + data.mainCommonName,
        string: data.idTaxon != null ? "" + data.idTaxon : ""
      },
      _standard: {
        text: data.scientificName
      }
    };
    return saveData;
  };

  IUCNUtil.getSettings = function() {
    return ez5.session.getBaseConfig("plugin", "custom-data-type-iucn").iucn_settings;
  };

  IUCNUtil.getPluginEndpoint = function() {
    var ref;
    return ((ref = ez5.pluginManager.getPlugin('custom-data-type-iucn')) != null ? ref.__plugin_url : void 0) + "/proxy_api_v4";
  };

  IUCNUtil.getLatestAssessmentIdFromSearchResult = function(data) {
    var a, deferred, i, len, ref;
    deferred = new CUI.Deferred();
    if (!data) {
      return {};
    }
    if (!data.assessments) {
      return {};
    }
    if (!CUI.util.isArray(data.assessments)) {
      return {};
    }
    ref = data.assessments;
    for (i = 0, len = ref.length; i < len; i++) {
      a = ref[i];
      if (!a.latest) {
        continue;
      }
      if (!a.assessment_id) {
        continue;
      }
      return a.assessment_id;
    }
    return 0;
  };

  return IUCNUtil;

})();
// Generated by CoffeeScript 1.12.7
var IUCNUpdate;

IUCNUpdate = (function() {
  function IUCNUpdate() {}

  IUCNUpdate.prototype.__getEasydbUrl = function(easydb_api_url) {
    if (!easydb_api_url.endsWith("/api/v1")) {
      easydb_api_url += "/api/v1";
    }
    return easydb_api_url;
  };

  IUCNUpdate.prototype.__startUpdate = function(data) {
    this.__login(data).done((function(_this) {
      return function(easydbToken, easydbUrl) {
        var config, i, len, ref, settingsKey, state;
        if (!easydbToken) {
          ez5.respondError("custom.data.type.iucn.start-update.error.easydb-token-empty");
          return;
        }
        config = _this.__getConfig(data);
        if (!config) {
          ez5.respondError("custom.data.type.iucn.start-update.error.server-config-empty");
          return;
        }
        state = {
          easydbToken: easydbToken,
          easydbUrl: easydbUrl,
          config: {}
        };
        ref = ["iucn_settings", "iucn_easydb_settings", "iucn_api_settings"];
        for (i = 0, len = ref.length; i < len; i++) {
          settingsKey = ref[i];
          if (!config[settingsKey]) {
            ez5.respondError("custom.data.type.iucn.start-update.error." + settingsKey + "-not-available-in-server-config");
            return;
          }
          state.config[settingsKey] = config[settingsKey];
        }
        return ez5.respondSuccess({
          state: state
        });
      };
    })(this)).fail((function(_this) {
      return function(messageKey, opts) {
        return ez5.respondError(messageKey, opts);
      };
    })(this));
  };

  IUCNUpdate.prototype.__getConfig = function(data) {
    var ref, ref1;
    return (ref = data.server_config) != null ? (ref1 = ref.base) != null ? ref1.system : void 0 : void 0;
  };

  IUCNUpdate.prototype.__login = function(data) {
    var config, deferred, easydbApiUrl, easydbUrl, getSessionUrl, login, password, ref, ref1, ref2, ref3, xhr;
    config = this.__getConfig(data);
    if (!config) {
      return CUI.rejectedPromise("custom.data.type.iucn.start-update.error.server-config-empty");
    }
    login = (ref = config.iucn_easydb_settings) != null ? ref.easydb_login : void 0;
    password = (ref1 = config.iucn_easydb_settings) != null ? ref1.easydb_password : void 0;
    if (!login || !password) {
      return CUI.rejectedPromise("custom.data.type.iucn.start-update.error.login-password-not-provided", {
        login: login,
        password: password
      });
    }
    easydbApiUrl = (ref2 = data.server_config.system) != null ? (ref3 = ref2.server) != null ? ref3.external_url : void 0 : void 0;
    if (!easydbApiUrl) {
      return CUI.rejectedPromise("custom.data.type.iucn.start-update.error.easydb-api-url-not-configured");
    }
    deferred = new CUI.Deferred();
    easydbUrl = this.__getEasydbUrl(easydbApiUrl);
    getSessionUrl = easydbUrl + "/session";
    xhr = new CUI.XHR({
      method: "GET",
      url: getSessionUrl
    });
    xhr.start().done((function(_this) {
      return function(response) {
        var authenticateUrl;
        authenticateUrl = easydbUrl + "/session/authenticate?login=" + login + "&password=" + password;
        xhr = new CUI.XHR({
          method: "POST",
          url: authenticateUrl,
          headers: {
            'x-easydb-token': response.token
          }
        });
        return xhr.start().done(function(response) {
          return deferred.resolve(response != null ? response.token : void 0, easydbUrl);
        }).fail(function(e) {
          var ref4;
          return deferred.reject("custom.data.type.iucn.start-update.error.authenticate-server-error", {
            e: e != null ? (ref4 = e.response) != null ? ref4.data : void 0 : void 0,
            url: authenticateUrl
          });
        });
      };
    })(this)).fail(function(e) {
      var ref4;
      return deferred.reject("custom.data.type.iucn.start-update.error.get-session-server-error", {
        e: e != null ? (ref4 = e.response) != null ? ref4.data : void 0 : void 0,
        url: getSessionUrl
      });
    });
    return deferred.promise();
  };

  IUCNUpdate.prototype.__update = function(data) {
    var apiSettings, deferred, easydbApiUrl, endpoint, objectsNotFound, objectsToUpdate, objectsToUpdateTags, process_object, ref, searchIdx, speciesById, speciesByName;
    apiSettings = (ref = data.state.config) != null ? ref.iucn_api_settings : void 0;
    if (!apiSettings) {
      ez5.respondError("custom.data.type.iucn.update.error.iucn_api_settings.not-available-in-state", {
        state: data.state
      });
      return;
    }
    easydbApiUrl = data.state.easydbUrl;
    if (!easydbApiUrl) {
      ez5.respondError("custom.data.type.iucn.update.error.easydb-api-url-not-configured");
      return;
    }
    if (!data.state.easydbToken) {
      return CUI.rejectedPromise("custom.data.type.iucn.update.error.not-easydb-token-in-state");
    }
    deferred = new CUI.Deferred();
    objectsToUpdate = [];
    objectsToUpdateTags = [];
    objectsNotFound = [];
    speciesByName = {};
    speciesById = {};
    ez5.session = {
      token: data.state.easydbToken
    };
    endpoint = data.state.easydbUrl + "/plugin/extension/custom-data-type-iucn/proxy_api_v4";
    searchIdx = 0;
    process_object = (function(_this) {
      return function(object) {
        var dfr, genus, parts, searchPromise, species, startTime;
        dfr = new CUI.Deferred();
        startTime = process.hrtime();
        if (!CUI.util.isEmpty(object.data.idTaxon)) {
          searchPromise = ez5.IUCNUtil.searchBySisTaxonId(endpoint, object.data.idTaxon);
        } else if (!CUI.util.isEmpty(object.data.scientificName)) {
          parts = object.data.scientificName.split(/\s+/).filter(function(part) {
            return part.trim() !== "";
          });
          genus = "";
          species = "";
          if (parts.length > 0) {
            genus = parts[0];
          }
          if (parts.length > 1) {
            species = parts[1];
          }
          searchPromise = ez5.IUCNUtil.searchByTaxonname(endpoint, genus, species);
        } else {
          dfr.reject();
          return dfr.promise();
        }
        searchPromise.done(function(response) {
          var _assessment_id;
          if (CUI.util.isEmpty(response)) {
            objectsNotFound.push(object);
            dfr.resolve();
            return;
          }
          _assessment_id = ez5.IUCNUtil.getLatestAssessmentIdFromSearchResult(response);
          return ez5.IUCNUtil.getAssessmentData(endpoint, _assessment_id).done(function(response) {
            var desiredDelay, elapsedMs, endTime, foundData, remainingTime;
            if (CUI.util.isEmpty(response)) {
              objectsNotFound.push(object);
              dfr.resolve();
              return;
            }
            foundData = ez5.IUCNUtil.setObjectData({}, response);
            object.data = ez5.IUCNUtil.getSaveData(foundData);
            object.data.__updateTags = true;
            objectsToUpdateTags.push(object);
            objectsToUpdate.push(object);
            endTime = process.hrtime(startTime);
            elapsedMs = (endTime[0] * 1000) + (endTime[1] / 1000000);
            desiredDelay = 1000;
            if (elapsedMs < desiredDelay) {
              remainingTime = desiredDelay - elapsedMs;
              return setTimeout(function() {
                return dfr.resolve();
              }, remainingTime);
            } else {
              return dfr.resolve();
            }
          }).fail(function(e) {
            return dfr.reject();
          });
        }).fail(function(e) {
          return dfr.reject();
        });
        return dfr.promise();
      };
    })(this);
    CUI.chunkWork.call(this, {
      items: data.objects,
      chunk_size: 1,
      call: (function(_this) {
        return function(batch) {
          return process_object(batch[0]);
        };
      })(this)
    }).fail((function(_this) {
      return function() {
        return ez5.respondError("custom.data.type.iucn.update.error.iucn-api-call");
      };
    })(this)).done((function(_this) {
      return function() {
        var i, len, objectNotFound;
        for (i = 0, len = objectsNotFound.length; i < len; i++) {
          objectNotFound = objectsNotFound[i];
          objectNotFound.redList = false;
          objectsToUpdate.push(objectNotFound);
          objectsToUpdateTags.push(objectNotFound);
        }
        return _this.__updateTags(objectsToUpdateTags, data).done(function() {
          var easydbUrl, response, xhr;
          response = {
            payload: objectsToUpdate
          };
          if (data.batch_info && data.batch_info.offset + data.objects.length >= data.batch_info.total) {
            easydbUrl = _this.__getEasydbUrl(easydbApiUrl);
            xhr = new CUI.XHR({
              method: "POST",
              url: easydbUrl + "/session/deauthenticate"
            });
            return xhr.start().always(function() {
              return ez5.respondSuccess(response);
            });
          } else {
            return ez5.respondSuccess(response);
          }
        }).fail(function(messageKey, opts) {
          if (opts == null) {
            opts = {};
          }
          return ez5.respondError(messageKey, opts);
        });
      };
    })(this));
    return deferred.promise();
  };

  IUCNUpdate.prototype.__updateTags = function(objects, data) {
    var easydbToken, easydbUrl, fields, idTagRed, iucnFields, iucnSettings, linkSeparator, linkedFields, searchLimit;
    if (objects.length === 0) {
      return CUI.resolvedPromise();
    }
    easydbUrl = this.__getEasydbUrl(data.state.easydbUrl);
    iucnSettings = data.state.config.iucn_settings;
    idTagRed = iucnSettings.tag_red;
    iucnFields = iucnSettings.iucn_fields;
    if (!iucnFields || !idTagRed) {
      return CUI.rejectedPromise("custom.data.type.iucn.update.error.not-available-settings");
    }
    easydbToken = data.state.easydbToken;
    linkSeparator = ez5.IUCNUtil.LINK_FIELD_SEPARATOR;
    linkedFields = [];
    fields = [];
    iucnFields.forEach(function(field) {
      var fieldName, index;
      if (field.iucn_field_name.indexOf(linkSeparator) !== -1) {
        fieldName = field.iucn_field_name;
        index = fieldName.indexOf(linkSeparator);
        return linkedFields.push({
          linked_field: fieldName.substring(0, index),
          field: fieldName.substring(index + linkSeparator.length) + ".idTaxon"
        });
      } else {
        return fields.push(field.iucn_field_name + ".idTaxon");
      }
    });
    searchLimit = 1000;
    return CUI.chunkWork.call(this, {
      items: objects,
      chunk_size: 1,
      call: (function(_this) {
        return function(items) {
          var item, updateTags;
          item = items[0];
          updateTags = function(_fields, linked) {
            var _linkedFields, addTagBody, deferred, removeTagBody, search, searchLinked, update;
            if (linked == null) {
              linked = false;
            }
            if (_fields.length === 0) {
              return CUI.resolvedPromise();
            }
            if (linked) {
              _linkedFields = _fields.map(function(_field) {
                return _field.linked_field + "._global_object_id";
              });
              _fields = _fields.map(function(_field) {
                return _field.field;
              });
            }
            deferred = new CUI.Deferred();
            addTagBody = {
              _mask: "_all_fields",
              _tags: [],
              _comment: "IUCN UPDATE - ADD TAG",
              "_tags:group_mode": "tag_add"
            };
            removeTagBody = {
              _mask: "_all_fields",
              _tags: [],
              _comment: "IUCN UPDATE - REMOVE TAG",
              "_tags:group_mode": "tag_remove"
            };
            if (item.data.redList) {
              addTagBody._tags.push({
                _id: idTagRed
              });
              removeTagBody = null;
            } else {
              removeTagBody._tags.push({
                _id: idTagRed
              });
              addTagBody = null;
            }
            update = function(objects) {
              var _addTagBody, _removeTagBody, body, idObjectsByObjecttype, ids, objecttype, updatePromises, updateTagsOpts, updateTagsPromise, xhrUpdateTags;
              idObjectsByObjecttype = {};
              objects.forEach((function(_this) {
                return function(object) {
                  var idObject, objecttype;
                  objecttype = object._objecttype;
                  if (!idObjectsByObjecttype[objecttype]) {
                    idObjectsByObjecttype[objecttype] = [];
                  }
                  idObject = object[objecttype]._id;
                  return idObjectsByObjecttype[objecttype].push(idObject);
                };
              })(this));
              updatePromises = [];
              for (objecttype in idObjectsByObjecttype) {
                ids = idObjectsByObjecttype[objecttype];
                _removeTagBody = CUI.util.copyObject(removeTagBody, true);
                _addTagBody = CUI.util.copyObject(addTagBody, true);
                body = [];
                if (_removeTagBody) {
                  _removeTagBody._objecttype = objecttype;
                  _removeTagBody[objecttype] = {
                    _id: ids
                  };
                  body.push(_removeTagBody);
                }
                if (_addTagBody) {
                  _addTagBody._objecttype = objecttype;
                  _addTagBody[objecttype] = {
                    _id: ids
                  };
                  body.push(_addTagBody);
                }
                updateTagsOpts = {
                  method: "POST",
                  url: easydbUrl + ("/db/" + objecttype + "?base_fields_only=1&format=short"),
                  headers: {
                    'x-easydb-token': easydbToken
                  },
                  body: body
                };
                xhrUpdateTags = new CUI.XHR(updateTagsOpts);
                updateTagsPromise = xhrUpdateTags.start().fail((function(_this) {
                  return function(e) {
                    var ref;
                    return deferred.reject("custom.data.type.iucn.update.error.update-tags", {
                      request: updateTagsOpts,
                      error: e != null ? (ref = e.response) != null ? ref.data : void 0 : void 0
                    });
                  };
                })(this));
                updatePromises.push(updateTagsPromise);
              }
              return CUI.when(updatePromises);
            };
            searchLinked = (function(_this) {
              return function(objects) {
                var _search, searchLinkedDeferred;
                searchLinkedDeferred = new CUI.Deferred();
                _search = function(offset) {
                  var idObjects, objecttypes, searchOpts, xhrLinkSearch;
                  if (offset == null) {
                    offset = 0;
                  }
                  objecttypes = _linkedFields.map(function(fullname) {
                    return fullname.split(".")[0];
                  });
                  idObjects = objects.map(function(object) {
                    return object._global_object_id;
                  });
                  searchOpts = {
                    method: "POST",
                    url: easydbUrl + "/search",
                    headers: {
                      'x-easydb-token': easydbToken
                    },
                    body: {
                      offset: offset,
                      limit: searchLimit,
                      search: [
                        {
                          type: "in",
                          fields: _linkedFields,
                          "in": idObjects,
                          bool: "must"
                        }
                      ],
                      format: "short",
                      objecttypes: objecttypes
                    }
                  };
                  xhrLinkSearch = new CUI.XHR(searchOpts);
                  return xhrLinkSearch.start().done(function(response) {
                    if (!response.objects || response.objects.length === 0) {
                      searchLinkedDeferred.resolve();
                      return;
                    }
                    update(response.objects).done(function() {
                      if (response.count > response.offset + searchLimit) {
                        offset += searchLimit;
                        return _search(offset);
                      } else {
                        return searchLinkedDeferred.resolve();
                      }
                    });
                  }).fail(function(e) {
                    var ref;
                    deferred.reject("custom.data.type.iucn.update.error.search-linked-objects", {
                      request: searchOpts,
                      error: e != null ? (ref = e.response) != null ? ref.data : void 0 : void 0
                    });
                  });
                };
                _search();
                return searchLinkedDeferred.promise();
              };
            })(this);
            search = (function(_this) {
              return function(offset) {
                var objecttypes, searchOpts, xhrSearch;
                if (offset == null) {
                  offset = 0;
                }
                objecttypes = _fields.map(function(fullname) {
                  return fullname.split(".")[0];
                });
                searchOpts = {
                  method: "POST",
                  url: easydbUrl + "/search",
                  headers: {
                    'x-easydb-token': easydbToken
                  },
                  body: {
                    offset: offset,
                    limit: searchLimit,
                    search: [
                      {
                        type: "in",
                        fields: _fields,
                        "in": [item.data.idTaxon],
                        bool: "must"
                      }
                    ],
                    format: "short",
                    objecttypes: objecttypes
                  }
                };
                xhrSearch = new CUI.XHR(searchOpts);
                return xhrSearch.start().done(function(response) {
                  var promise;
                  if (!response.objects || response.objects.length === 0) {
                    deferred.resolve();
                    return;
                  }
                  objects = response.objects;
                  if (linked) {
                    promise = searchLinked(objects);
                  } else {
                    promise = update(objects);
                  }
                  promise.done(function() {
                    if (response.count > response.offset + searchLimit) {
                      offset += searchLimit;
                      return search(offset);
                    } else {
                      return deferred.resolve();
                    }
                  });
                }).fail(function(e) {
                  var ref;
                  deferred.reject("custom.data.type.iucn.update.error.search-objects", {
                    request: searchOpts,
                    error: e != null ? (ref = e.response) != null ? ref.data : void 0 : void 0
                  });
                });
              };
            })(this);
            search();
            return deferred.promise();
          };
          return CUI.when(updateTags(fields), updateTags(linkedFields, true));
        };
      })(this)
    });
  };

  IUCNUpdate.prototype.main = function(data) {
    var i, key, len, ref;
    if (!data) {
      ez5.respondError("custom.data.type.iucn.update.error.payload-missing");
      return;
    }
    ref = ["action", "plugin_config"];
    for (i = 0, len = ref.length; i < len; i++) {
      key = ref[i];
      if (!data[key]) {
        ez5.respondError("custom.data.type.iucn.update.error.payload-key-missing", {
          key: key
        });
        return;
      }
    }
    if (data.action === "start_update") {
      this.__startUpdate(data);
      return;
    } else if (data.action === "update") {
      if (!data.objects) {
        ez5.respondError("custom.data.type.iucn.update.error.objects-missing");
        return;
      }
      if (!(data.objects instanceof Array)) {
        ez5.respondError("custom.data.type.iucn.update.error.objects-not-array");
        return;
      }
      if (!data.state) {
        ez5.respondError("custom.data.type.gazeteer.update.error.state-missing");
        return;
      }
      this.__update(data);
      return;
    } else {
      ez5.respondError("custom.data.type.iucn.update.error.invalid-action", {
        action: data.action
      });
    }
  };

  return IUCNUpdate;

})();

module.exports = new IUCNUpdate();
