Show:
/**
 * Copyright 2016, Yahoo! Inc.
 * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
 */

import Ember from 'ember';
import ValidationResultCollection from './result-collection';
import InternalResultObject from './internal-result-object';

const {
  get,
  set,
  isNone,
  isArray,
  computed,
  setProperties,
  getProperties,
} = Ember;

const {
  readOnly
} = computed;

/**
 * This class is `private` and is only used by {{#crossLink 'ResultCollection'}}{{/crossLink}}
 * @module Validations
 * @class Result
 * @private
 */

export default Ember.Object.extend({

  /**
   * @property model
   * @type {Object}
   */
  model: null,

  /**
   * @property attribute
   * @type {String}
   */
  attribute: '',

  /**
   * @property _promise
   * @async
   * @private
   * @type {Promise}
   */
  _promise: null,

  /**
   * The validator that returned this result
   * @property _validator
   * @private
   * @type {Validator}
   */
  _validator: null,

  /**
   * @property isValid
   * @readOnly
   * @type {Ember.ComputedProperty}
   */
  isValid: readOnly('_validations.isValid'),

  /**
   * @property isInvalid
   * @readOnly
   * @type {Ember.ComputedProperty}
   */
  isInvalid: readOnly('_validations.isInvalid'),

  /**
   * @property isValidating
   * @readOnly
   * @type {Ember.ComputedProperty}
   */
  isValidating: readOnly('_validations.isValidating'),

  /**
   * @property isTruelyValid
   * @readOnly
   * @type {Ember.ComputedProperty}
   */
  isTruelyValid: readOnly('_validations.isTruelyValid'),

  /**
   * @property isAsync
   * @readOnly
   * @type {Ember.ComputedProperty}
   */
  isAsync: readOnly('_validations.isAsync'),

  /**
   * @property isDirty
   * @readOnly
   * @type {Ember.ComputedProperty}
   */
  isDirty: readOnly('_validations.isDirty'),

  /**
   * @property message
   * @readOnly
   * @type {Ember.ComputedProperty}
   */
  message: readOnly('_validations.message'),

  /**
   * @property messages
   * @readOnly
   * @type {Ember.ComputedProperty}
   */
  messages: readOnly('_validations.messages'),

  /**
   * @property error
   * @readOnly
   * @type {Ember.ComputedProperty}
   */
  error: readOnly('_validations.error'),

  /**
   * @property errors
   * @readOnly
   * @type {Ember.ComputedProperty}
   */
  errors: readOnly('_validations.errors'),

  /**
   * This hold all the logic for the above CPs. We do this so we can easily switch this object out with a different validations object
   * @property _validations
   * @private
   * @type {Result}
   */
  _validations: computed('model', 'attribute', '_promise', function () {
    return InternalResultObject.create(getProperties(this, ['model', 'attribute', '_promise']));
  }),

  init() {
    this._super(...arguments);

    if (get(this, 'isAsync')) {
      this._handlePromise();
    }
  },

  /**
   * Update the current validation result object with the given result
   * - If result is undefined or null, set isValid to false
   * - If result is a validations object from a different model/object, set the _validations object to the one given (belongs-to)
   * - If result is a collection of result objects, create a Validation Result Collection and set that to the _validations object (has-many)
   * - If result is a string, set the message to the given string and set isValid to false
   * - If result is a boolean, set isValid to result
   * - If result is a pojo, update _validations object with the information given
   *
   * @method update
   * @private
   * @param result
   */
  update(result) {
    const validations = get(this, '_validations');

    if (isNone(result)) {
      this.update(false);
      return;
    }

    if (get(result, 'isValidations')) {
      set(this, '_validations', result);
    } else if (isArray(result)) {
      const validationResultsCollection = ValidationResultCollection.create({
        attribute: get(this, 'attribute'),
        content: result
      });
      set(this, '_validations', validationResultsCollection);
    } else if (typeof result === 'string') {
      setProperties(validations, {
        message: result,
        isValid: false
      });
    } else if (typeof result === 'boolean') {
      set(validations, 'isValid', result);
    } else if (typeof result === 'object') {
      setProperties(validations, result);
    }
  },

  /**
   * Promise handler
   * @method  _handlePromise
   * @private
   */
  _handlePromise() {
    const validations = get(this, '_validations');

    set(validations, 'isValidating', true);
    get(this, '_promise').then(
      result => this.update(result),
      result => this.update(result)
    ).catch(reason => {
      // TODO: send into error state
      throw reason;
    }).finally(() => {
      set(validations, 'isValidating', false);
    });
  }
});