import OAuth2PasswordGrant from 'ember-simple-auth/authenticators/oauth2-password-grant';
import config from '../config/environment';
import parseBase64 from '../utils/parse-base64';
import Ember from 'ember';
const { RSVP: { Promise }, isEmpty, run, Logger: { warn } } = Ember;

export default OAuth2PasswordGrant.extend({
  serverTokenEndpoint: `${config.apiURL}/api/authenticate`,
  serverTokenRefreshEndpoint: `${config.apiURL}/api/refresh`,

  authenticate: function(identification, password) {
    return new Promise((resolve, reject) => {
      const data = { username: identification, password };
      const serverTokenEndpoint = this.get('serverTokenEndpoint');
      this.makeRequest(serverTokenEndpoint, data).then((response) => {
        run(() => {
          const token = parseBase64(response['access_token']);
          const expiresAt = this._absolutizeExpirationTime(token['exp']);
          this._scheduleAccessTokenRefresh(expiresAt, response['access_token']);
          if (!isEmpty(expiresAt)) {
            response = Ember.merge(response, { 'expires_at': expiresAt });
          }
          resolve(response);
        });
      }, (xhr) => {
        run(null, reject, xhr.responseJSON || xhr.responseText);
      });
    });
  },

  _scheduleAccessTokenRefresh: function(expiresAt, accessToken) {
    if (this.get('refreshAccessTokens')) {
      const now = (new Date()).getTime();
      const offset = (Math.floor(Math.random() * 5) + 5) * 1000;
      if (!isEmpty(accessToken) && !isEmpty(expiresAt) && expiresAt > now - offset) {
        run.cancel(this._refreshTokenTimeout);
        delete this._refreshTokenTimeout;
        if (!Ember.testing) {
          this._refreshTokenTimeout = run.later(this, this._refreshAccessToken, expiresAt, accessToken, expiresAt - now - offset);
        }
      }
    }
  },

  _refreshAccessToken: function(expiresAt, accessToken) {
    const data = { 'token': accessToken };
    const serverTokenRefreshEndpoint = this.get('serverTokenRefreshEndpoint');
    return new Promise((resolve, reject) => {
      this.makeRequest(serverTokenRefreshEndpoint, data).then((response) => {
        run(() => {
          const token = parseBase64(response['access_token']);
          const expiresAt = this._absolutizeExpirationTime(token['exp']);
          const data = Ember.merge(response, { 'expires_at': expiresAt });
          this._scheduleAccessTokenRefresh(expiresAt, response['access_token']);
          this.trigger('sessionDataUpdated', data);
          resolve(data);
        });
      }, (xhr, status, error) => {
        warn(`Access token could not be refreshed - server responded with ${error}.`);
        reject();
      });
    });
  },

  _absolutizeExpirationTime: function(expiresAt) {
    if (!isEmpty(expiresAt)) {
      return new Date(expiresAt * 1000).getTime();
    }
  }
});