/* global Twitch */

import RSVP from 'rsvp';
import EmberObject from 'ember-object';
import run from 'ember-runloop';
import Service from 'ember-service';
import computed from 'ember-computed';
import injectService from 'ember-service/inject';
import { UnauthenticatedError } from 'web-client/utilities/error';
import { JTV_USER_PICTURES_404_USER_70X70_URL } from 'web-client/utilities/urls/static-cdn';

export default Service.extend({
  api: injectService(),
  store: injectService(),

  userLogo: computed('isAuthenticated', 'userModel.logo', function() {
    return this.isAuthenticated ? this.get('userModel.logo') : JTV_USER_PICTURES_404_USER_70X70_URL;
  }),

  init() {
    this._super(...arguments);
    this.userData = null;
    this.isAuthenticated = null;
    this.userModel = null;
    this._currentUserPromise = null;
  },

  initializeSession() {
    return this.getCurrentUser().then((userData) => {
      if (this.isDestroyed) { return; }

      this.userData = userData;
      this.isAuthenticated = true;

      let temporaryUser = EmberObject.create({
        id: userData.login,
        login: userData.login,
        displayName: userData.name,
        logo: userData.logo || JTV_USER_PICTURES_404_USER_70X70_URL,
        email: userData.email
      });

      this.set('userModel', temporaryUser);

      this.get('store').findRecord('user', userData.login).then((user) => {
        this.set('userModel', user);
      });
    }, (error) => {
      if (this.isDestroyed || (error instanceof UnauthenticatedError)) {
        // This is an expected error that means the user is unauthenticated.
        this.isAuthenticated = false;
        return;
      }
      throw error;
    });
  },

  getCurrentUser() {
    /*
     * Memoized promise for getting the current user.
     */
    if (!this._currentUserPromise) {
      this._currentUserPromise = new RSVP.Promise((resolve, reject) => {

        /*
         * Twitch.user provides peek() for a sync look at the current state. If
         * available use peek, as allow us to avoid creating an RSVP promise that
         * would schedule after the runloop and rendering.
         */
        let { isAuthenticated, userData } = Twitch.user.peek();

        if (isAuthenticated) {
          /* Immediately resolve */
          resolve(userData);
        } else if (isAuthenticated === false) {
          /* Immediately throw unauthenticated */
          throw new UnauthenticatedError();
        } else {
          /* Attempt authentication with the async API */
          // FIXME: This call does not work with Ember's testing waiters.
          Twitch.user().then(run.bind(null, resolve), (error) => {
            if (error.status === 401) {
              /* Convert auth errors into a nicer class */
              run(null, reject, new UnauthenticatedError());
            } else {
              run(null, reject, error);
            }
          });
        }

      }, 'TwitchSessionService#getCurrentUser');
    }

    return this._currentUserPromise;
  },

  /*
   * Centralized logic for getting the oauth_token of the current user.
   *
   * Affords us the ability to provide this via a different mechanism in the
   * future.
   */
  getOAuthToken() {
    return this.getCurrentUser().then((user) => {
      return user.chat_oauth_token;
    }, (error) => {
      if (error instanceof UnauthenticatedError)  {
        return "";
      }
      throw error;
    });
  },

  validateAuth(params = {}) {
    return this.get('api').request('get', '/api/users/auth_validator', params);
  },

  /*
   * Call a provided callback if there is a user.
   *
   * TODO: Instead of sprinkling the `getCurrentUser()` and `Twitch.user()`
   * calls througout the codebase, we should be running the current user
   * check once at the routing layer. Then pretty much all calls to this
   * method can be replaced with a check for `session.currentUser`.
   *
   */
  withCurrentUser(callback) {
    this.getCurrentUser().then((user) => {
      callback(user);
    }, (error) => {
      if (error instanceof UnauthenticatedError) {
        // This is an expected error that means the user is unauthenticated.
        return;
      }
      throw error;
    });
  }
});
