import Component from '@ember/component';
import EmberError from '@ember/error';
import {computed} from '@ember/object';
import {inject} from '@ember/service';
import {isEmpty, isNone} from '@ember/utils';
import layout from '../templates/components/fw-app-link';

/**
 * This component is used to create a link to another FW app. It is used similarly to the Ember `LinkTo` helper.
 *
 * To use inline, parameters must be provided for the link name, the app ID, and the route name.
 * An optional query parameters object can be added at the end. See the [Ember docs](https://guides.emberjs.com/v3.12.0/templates/links/#toc_setting-query-params) for more information on query params
 *
 * ```handlebars
 * <FwAppLink @title='Link Text' @appId='appId' @route='route.name' />
 * <FwAppLink @title='Link Text' @appId='appId' @route='route.name' @query={{hash key=value}} />
 * ```
 *
 * When used in block format, the block contents are used in place of link text, so the first parameter is the app ID.
 * An optional block parameter is passed containing the app metadata:
 *
 * ```handlebars
 * <FwAppLink @appId='appId' @route='route.name' as |app|>
 *     <i>{{app.name}}</i>
 * </FwAppLink>
 * <FwAppLink @appId='appId' @route='route.name' @query={{hash key=value}}>
 *     <i>Block text</i>
 * </FwAppLink>
 * ```
 *
 * If the app is missing or the user does not have access to the app, no HTML will be rendered.
 * Use the `has-app` helper to show content when the app is missing.
 *
 * @public
 * @class FwAppLink
 * @extends Ember.Component
 * @module Components
 */
const FWAppLink = Component.extend({
    layout,
    tagName: '',
    appMeta: inject(),
    currentUser: inject(),

    /**
     * Dynamic parameters array. ***This is deprecated and should not be used. Specify all your parameters***
     * @property params
     * @type {Array}
     * @deprecated
     */

    /**
     * Link title, will be null in block form
     * @property title
     * @type {String}
     */
    title: null,

    /**
     * Shortcode of app
     * @property appId
     * @type {String}
     */
    appId: null,

    /**
     * Route within the app you are linking to
     * @property route
     * @type {String}
     */
    route: '',

    /**
     * App meta object for the related app. Will be null if the app is not found. Set by `didReceiveAttrs`.
     * @property app
     * @type {EmberObject}
     * @private
     */
    app: null,

    /**
     * This is deprecated. Query parameters for the link. It was set if using the params array and
     * passed query params using the `query-params` helper, but the preferred method is using the `@query`
     * property, as is described [here](https://guides.emberjs.com/release/routing/query-params/#toc_linkto--component)
     *
     * @property queryParams
     * @type {Object}
     * @deprecated
     * @private
     */
    queryParams: null,

    /**
     * Computed property of URL encoded query params. This is again deprecated as it relies on the `params` order of the old way
     * rather than specified values, as is with the new syntax.
     * @property queryString
     * @type {Computed String}
     * @deprecated
     * @private
     */
    queryString: computed('queryParams', function() {
        // make sure we have a query object and its not empty
        let query = this.get('queryParams');
        if (isNone(query)) {
            return '';
        }
        let keys = Object.keys(query);
        if (isEmpty(keys)) {
            return '';
        }
        // convert all query params into a query string
        return `?${keys.map((key) => `${key}=${encodeURIComponent(query[key])}`).join('&')}`;
    }),

    /**
     * Computed property of link URL within the other app.
     * @property url
     * @type {Computed String}
     * @private
     */
    url: computed('route', 'app.urlClean', 'queryString', function() {
        if (!this.get('app')) {
            return null;
        }
        let route = this.get('route').replace(/\./g, '/');
        return `${this.get('app.urlClean')}/#/${route}${this.get('queryString')}`;
    }),

    /**
     * All this stuff about params is deprecated and just there for backwards compatibility. Don't remove,
     * but you really shouldn't be using the params attribute in any other app.
     */
    didReceiveAttrs() {
        this._super(...arguments);

        let appId = this.get('appId');
        if (appId && this.get('currentUser.apps').includes(appId)) {
            this.get('appMeta').findMeta(appId).then((meta) => {
                this.set('app', meta);
            });
        }

        // parse params
        let params = this.get('params');
        if (params) {
            let len = params.length;

            // query params is the last parameter, fetch that first
            let lastParam = params[len - 1];
            if (lastParam && lastParam.isQueryParams) {
                len--;
                this.set('queryParams', lastParam.values);
            }

            // no parameters remaining means no app
            if (len === 0) {
                throw new EmberError('App must be defined for FW App Link');
            }

            // if we have 3 parameters, the first is the title
            // ideally would check for HAS_BLOCK here, but Ember does not give access to that
            let next = 0;
            if (len >= 3) {
                this.set('title', params[next++]);
            }

            // next, fetch the app off the params, always defined
            let appId = params[next++];
            // skip fetching if we lack permissions, link will be no good
            if (this.get('currentUser.apps').includes(appId)) {
                this.get('appMeta').findMeta(appId).then((meta) => {
                    this.set('app', meta);
                });
            }

            // finally, fetch the route if provided
            // this is optional in block format, but required in inline due to no HAS_BLOCK
            if (len >= 2) {
                this.set('route', params[next++]);
            }
        }
    }
});

FWAppLink.reopenClass({
    positionalParams: 'params'
});

export default FWAppLink;