import Service from '@ember/service';
import {inject as injectService} from '@ember/service';
import {computed} from '@ember/object';
import {notEmpty} from '@ember/object/computed';
import Evented from '@ember/object/evented';
import {A as emberA} from '@ember/array';
import {isEmpty} from '@ember/utils';
import match from '@bennerinformatics/ember-fw-gc/utils/match';
import RSVP from 'rsvp';

/**
 * This service handles various functionality that deals with logged in
 * user management. This is one of the most used services in Ember FW GC,
 * and once injected has various properties and methods which can be called upon it
 * which are invaluable to various aspects of the department.
 *
 * **Note**: This is different from the user model in that this deals with the currently logged
 * in user, rather than an array of user models from Group Control. Because this is a service
 * and not a user model, it should not be used as such. For example, a property expecting a user
 * model, should not be set in the following way:
 * ```javascript
 * this.set('user', this.get('currentUser'));
 * ```
 *
 * But rather using the id to find the actual user model from a user array:
 *
 * ```javascript
 * this.set('user', this.get('users').findBy('id', this.get('currentUser.userId')));
 * ```
 *
 * That being said the CurrentUser service is indispensable to a proper integration of Group Control with
 * the apps. For a list of all available properties and methods, see below.
 *
 * @public
 * @class CurrentUserService
 * @extends Ember.Service
 * @uses Ember.Evented
 * @module Services
 */
export default Service.extend(Evented, {
    /**
     * Username of the logged in user.
     *
     * @public
     * @property userId
     * @type {String}
     */
    userId: '',

    /**
     * Array of apps the user has access to
     *
     * @public
     * @property apps
     * @type {Array}
     */
    apps: emberA(),

    /**
     * Array of roles the user has for the current app and department
     *
     * @public
     * @property roles
     * @type {Array}
     */
    roles: emberA(),

    /**
     * If true, the user has roles in this app
     *
     * @public
     * @property hasRoles
     * @type {boolean}
     */
    hasRoles: notEmpty('roles').readOnly(),

    /**
     * Array of departments the user has access to for the current app
     *
     * @public
     * @property departments
     * @type {Array}
     */
    departments: emberA(),

    /**
     * If true, the user has at least one department
     *
     * @public
     * @property hasRoles
     * @type {boolean}
     */
    hasDepartments: notEmpty('departments').readOnly(),

    /**
     * Current department the user is viewing the application as
     *
     * @public
     * @property currentDept
     * @type {String}
     */
    currentDept: '',

    /**
     * First name of the user
     *
     * @public
     * @property nameFirst
     * @type {String}
     */
    nameFirst: '',

    /**
     * Last name of the user
     *
     * @public
     * @property nameLast
     * @type {String}
     */
    nameLast: '',

    /**
     * Preferred first name (nickname) of the user
     *
     * @public
     * @property namePref
     * @type {String}
     */
    namePref: '',

    /**
     * Email of the user
     *
     * @public
     * @property email
     * @type {String}
     */
    email: '',

    config: injectService(),
    ajax: injectService(),

    /**
     * Full name of the user, using the user's first, last, and
     * preferred name (if available)
     *
     * @public
     * @property nameFull
     * @type {String}
     */
    nameFull: computed('nameFirst', 'nameLast', 'namePref', function () {
        if (isEmpty(this.get('nameFirst')) || isEmpty(this.get('nameLast'))) {
            return '';
        }
        return `${(this.get('namePref') ? this.get('namePref') : this.get('nameFirst'))} ${this.get('nameLast')}`;
    }),

    /**
     * Checks if the user is currently viewing the application as
     * a particular department
     *
     * @public
     * @method checkDepartment
     * @param  {String} department department to check
     * @return {Boolean}     whether or not the current department matches the provided one
     */
    checkDepartment(department) {
        return this.get('currentDept') === department;
    },

    /**
     * Changes the department the user is currently viewing the department as.
     *
     * @public
     * @method changeDepartment
     * @param  {String} newDept new department to view
     * @return {Promise}        Promise that resolves when the request has been made successfully
     */
    changeDepartment(department) {
        if (isEmpty(department)) {
            return RSVP.resolve();
        }
        let config = this.get('config');
        let app = config.get('appId');

        return this.get('ajax').request(config.formGCUrl('session', 'change', {app, department})).then((data) => {
            this.setProperties(data);
        });
    },

    /**
     * Checks if the user is still logged in.
     *
     * @public
     * @method check
     * @return {Promise} Promise that resolves with the current user's status
     */
    check() {
        let url = this.get('config').formGCUrl('session', 'check');
        return this.get('ajax').request(url).catch((error) => {
            // if we 500, retry once in case the server was randomly down
            if (error.status == 500) {
                return this.get('ajax').request(url);
            }
            // return false for active otherwise
            return {isActive: false};
        }).then(({isActive}) => {
            return isActive;
        }).catch(() => {
            return false;
        });
    },

    /**
     * Reloads the user's roles
     *
     * @public
     * @method reload
     * @return {Promise} Promise that resolves when the request has completed.
     */
    reload() {
        let config = this.get('config');
        let app = config.get('appId');

        return this.get('ajax').request(config.formGCUrl('session', 'reload', {app})).then(() => {
            window.location.replace(this.get('config.url'));
        });
    },

    /**
     * Checks if the user's roles match the provided list of roles.
     * Uses the [match](../classes/MatchUtil.html) utility to match users
     * current roles. Note the use of arrays to match with AND/OR
     * (a single array is OR, a double array is AND).
     *
     * @public
     * @param  {Array|String} roles Roles to check in either and/or array or string.
     * @return {Boolean}      Whether or not the given roles match the user's roles
     */
    match(roles) {
        return match(this.get('roles'), roles);
    },

    /**
     * Shortcut to trigger the change-password trigger
     *
     * @private
     * @method changePassword
     * @param {Boolean} needSecure Whether or the user needs to change this password for security reasons.
     */
    changePassword(needSecure = false) {
        this.trigger('change-password', needSecure);
    }
});