/*!
 * Copyright (c) 2015-2017 Cisco Systems, Inc. See LICENSE file.
 */

/* eslint camelcase: [0] */

import {oneFlight, whileInFlight} from '@ciscospark/common';
import {grantErrors, SparkPlugin} from '@ciscospark/spark-core';

/**
 * NodeJS support for OAuth2
 * @class
 * @variation 2
 */
const Authorization = SparkPlugin.extend({
  derived: {
    /**
     * Alias of {@link Authorization(2)#isAuthorizing}
     * @instance
     * @memberof Authorization(2)
     * @type {boolean}
     */
    isAuthenticating: {
      deps: [`isAuthorizing`],
      fn() {
        return this.isAuthorizing;
      }
    }
  },

  session: {
    /**
     * Indicates if an Authorization Code exchange is inflight
     * @instance
     * @memberof Authorization(2)
     * @type {boolean}
     */
    isAuthorizing: {
      default: false,
      type: `boolean`
    }
  },

  namespace: `Credentials`,

  logout(options = {}) {
    this.spark.request({
      method: `POST`,
      uri: this.config.logoutUrl,
      body: {
        token: options.token,
        cisService: this.config.service
      }
    });
  },

  @whileInFlight(`isAuthorizing`)
  @oneFlight
  /**
   * Exchanges an authorization code for an access token
   * @instance
   * @memberof Authorization(2)
   * @param {Object} options
   * @param {Object} options.code
   * @returns {Promise}
   */
  requestAuthorizationCodeGrant(options = {}) {
    this.logger.info(`credentials: requesting authorization code grant`);

    if (!options.code) {
      return Promise.reject(new Error(`\`options.code\` is required`));
    }

    return this.spark.request({
      method: `POST`,
      uri: this.config.tokenUrl,
      form: {
        grant_type: `authorization_code`,
        redirect_uri: this.config.redirect_uri,
        code: options.code,
        self_contained_token: true
      },
      auth: {
        user: this.config.client_id,
        pass: this.config.client_secret,
        sendImmediately: true
      },
      shouldRefreshAccessToken: false
    })
      .then((res) => {
        this.spark.credentials.set({supertoken: res.body});
      })
      .catch((res) => {
        if (res.statusCode !== 400) {
          return Promise.reject(res);
        }

        const ErrorConstructor = grantErrors.select(res.body.error);
        return Promise.reject(new ErrorConstructor(res._res || res));
      });
  },

  @oneFlight
  /**
   * Requests a Cisco Spark access token for a user already authenticated into
   * your product.
   *
   * Note: You'll need to supply a jwtRefreshCallback of the form
   * `Promise<jwt> = jwtRefreshCallback(spark)` for automatic token refresh to
   * work.
   *
   * @param {Object} options
   * @param {Object} options.jwt This is a jwt generated by your backend that
   * identifies a user in your system
   * @returns {Promise}
   */
  requestAccessTokenFromJwt({jwt}) {
    return this.spark.request({
      method: `POST`,
      service: `hydra`,
      resource: `jwt/login`,
      headers: {
        authorization: jwt
      }
    })
      .then(({body}) => ({
        access_token: body.token,
        token_type: `Bearer`,
        expires_in: body.expiresIn
      }))
      .then((token) => {
        this.spark.credentials.set({
          supertoken: token
        });
      });
  }
});

export default Authorization;
