/**
 * Analyzes, if an object is obsolete by checking if a specified retention time is exceeded.
 *
 * This analyzer does that, by checking the objects (and possible children) changed date. If all changes are older then the minimum age an action will be executed.
 *
 *     var analyzer = sol.create("sol.common_monitoring.as.analyzers.RetentionAnalyzer", {
 *       retention: { value: 2, unit: "M" },
 *       action: { type: "WORKFLOW", templateId: "TestMonitoring", user: "Administrator" }
 *     });
 *
 * For possible retention configuration see {@link sol.common_monitoring.as.MonitorUtils#evalDateUnitConfig}.
 *
 * The action needs to be specified, because analysers are responsible for determining the actions. This special analyzer just returns exactely one ore no action.
 *
 * @eloas
 *
 * @requires sol.common.DateUtils
 * @requires sol.common.RepoUtils
 * @requires sol.common_monitoring.as.MonitorUtils
 */
sol.define("sol.common_monitoring.as.analyzers.RetentionAnalyzer", {

  requiredConfig: ["retention", "action"],

  /**
   * @cfg {Object} retention Configuration to determine, if an object is obsolete or not (see {@link sol.common_monitoring.as.MonitorUtils#evalDateUnitConfig})
   * @cfg {String} retention.type (optional)
   * @cfg {String} retention.key (optional)
   * @cfg {Number} retention.value (optional)
   * @cfg {String|Object} retention.unit (optional)
   * @cfg {String} retention.unit.type (optional)
   * @cfg {String} retention.unit.key (optional)
   */

  /**
   * @cfg {Object} action
   */

  initialize: function (config) {
    var me = this;
    me.$super("sol.Base", "initialize", [config]);
    me._todayIso = sol.common.DateUtils.dateToIso(new Date());
  },

  /**
   * Analyzes if the object and all children are older than a specified retention time.
   * @param {de.elo.ix.client.Sord} sord
   * @return {Object[]}
   */
  analyze: function (sord) {
    var me = this,
        results = [],
        latestChange, latestChildChangedDate, nextRun;

    me.logger.enter("analyze", { objId: sord.id, name: String(sord.name) });

    latestChange = me.retrieveChangedDate(sord);
    latestChildChangedDate = me.retrieveOldestChildrenChangedDate(sord);
    if (latestChildChangedDate && (latestChange.getTime() < latestChildChangedDate.getTime())) {
      latestChange = latestChildChangedDate;
    }
    me.logger.debug(["last change: {0}", latestChange]);

    if (me.isObsolete(sord, latestChange)) {
      results.push({ action: me.action });
      me.logger.info(["retention exceeded: objId={0}, latestChange={1}", sord.id, latestChange]);
    } else {
      nextRun = me.calculateNextCheck(sord, latestChange, true);
      results.push({ nextRun: nextRun });
      me.logger.info(["retention not exceeded: objId={0}, nextPossibleRetention={1};", sord.id, nextRun]);
    }

    me.logger.exit("analyze", results);

    return results;
  },

  /**
   * @private
   * Retrieves an objects last changed date.
   * @param {de.elo.ix.client.Sord} sord
   * @return {Date}
   */
  retrieveChangedDate: function (sord) {
    return sol.common.DateUtils.isoToDate(sord.TStamp + "");
  },

  /**
   * @private
   * Retrieves the most current last changed date of all children.
   * @param {de.elo.ix.client.Sord} sord
   * @return {Date}
   */
  retrieveOldestChildrenChangedDate: function (sord) {
    var me = this,
        findOptions = new FindOptions(),
        changedDate = null,
        children;

    findOptions.orderBy = "objtstamp DESC";
    findOptions.totalCount = 1;

    children = sol.common.RepoUtils.findChildren(sord.id, {
      includeFolders: true,
      includeDocuments: true,
      sordZ: SordC.mbLean,
      recursive: true,
      level: -1,
      findOptions: findOptions
    });

    if (children && (children.length === 1)) {
      changedDate = me.retrieveChangedDate(children[0]);
      me.logger.debug(["latest changed child: objId={0}; name={1}, changed={2}", children[0].id, children[0].name, changedDate]);
    }
    return changedDate;
  },

  /**
   * @private
   * Calculates the date on which the next check should occur using {@link #retention} and `latestChange`.
   * @param {de.elo.ix.client.Sord} sord Used to determine the retention (if it depends on indexfields)
   * @param {Date} latestChange The most current change of the object (or any child)
   * @param {Boolean} [resultWithoutTime=false] (optional) Defines, if the resutling ISO string should contain tzhe time or not
   * @return {String}
   */
  calculateNextCheck: function (sord, latestChange, resultWithoutTime) {
    var me = this,
        distance = sol.common_monitoring.as.MonitorUtils.evalDateUnitConfig(sord, me.retention),
        checkDate;

    checkDate = sol.common.DateUtils.shift(latestChange, distance.value, { unit: distance.unit });

    return sol.common.DateUtils.dateToIso(checkDate, { withoutTime: resultWithoutTime });
  },

  /**
   * @private
   * Checks, if an date (`latestChange`) has exceeded the retention time.
   * @param {de.elo.ix.client.Sord} sord Used to determine the retention (if it depends on indexfields)
   * @param {Date} latestChange The most current change of the object (or any child)
   * @return {Boolean}
   */
  isObsolete: function (sord, latestChange) {
    var me = this,
        nextCheck = me.calculateNextCheck(sord, latestChange);

    me.logger.debug(["check retention: object would expire at {0}", nextCheck]);

    return nextCheck < me._todayIso;
  }

});