import Ember from 'ember';
import injectService from 'ember-service/inject';
import AVAILABILITY from '../../utilities/availability';
import computed from 'ember-computed';
import Service from 'ember-service';
import on from 'ember-evented/on';
import { copy } from 'ember-metal/utils';
import { A as emberA } from 'ember-array/utils';

const { compare } = Ember;

export default Service.extend({
  session: injectService(),
  store: injectService(),
  pubsub: injectService('twitch-friends-list/pubsub'),

  maxShowFriends: 20,
  _users: null,
  _friends: computed.filterBy('_users', 'isFriend'),
  isLoading: true,

  friends: computed('_friends.@each.isOnline', '_friends.@each.displayName', '_friends.@each.activities', function() {
    let friends = this.get('_friends');
    return this.sort(friends, this._availabilitySort.bind(this));
  }),

  alphabeticalFriends: computed('_friends.@each.displayName', function () {
    let friends = this.get('_friends');
    return this.sort(friends, this._alphabeticalSort);
  }),

  streamingFriends: computed('onlineFriends.@each.activities', function() {
    let friends = this.get('onlineFriends');
    return friends.filterBy('activities.firstObject.type', 'broadcasting');
  }),

  watchingFriendsOnline: computed('onlineFriends.@each.activities', function() {
    let friends = this.get('onlineFriends');
    return friends.filterBy('activities.firstObject.type', 'watching');
  }),

  watchingFriendsIdle: computed('idleFriends.@each.activities', function() {
    let friends = this.get('idleFriends');
    return friends.filterBy('activities.firstObject.type', 'watching');
  }),

  noActivityFriendsOnline: computed('onlineFriends.[]', function () {
    let friends = this.get('onlineFriends');
    return friends.filterBy('activities.firstObject.type', 'none');
  }),

  noActivityFriendsIdle: computed('idleFriends.[]', function () {
    let friends = this.get('idleFriends');
    return friends.filterBy('activities.firstObject.type', 'none');
  }),

  isEmpty: computed.readOnly('friends.isEmpty'),

  onlineCount: computed('friends.@each.isOnline', function() {
    return this.get('friends').filterBy('isOnline').length;
  }),

  onlineFriends: computed('friends.[]', function () {
    return emberA(this.get('friends')).filterBy('isOnline', true);
  }),

  idleFriends: computed('friends.[]', function () {
    return emberA(this.get('friends')).filterBy('isIdle', true);
  }),

  offlineFriends: computed('friends.[]', function () {
    return emberA(this.get('friends')).filterBy('isOffline', true);
  }),

  watchingPrioritizedFriends: computed('watchingFriendsOnline.[]', 'watchingFriendsIdle.[]', 'streamingFriends.[]', 'noActivityFriendsOnline.[]', 'noActivityFriendsIdle.[]', function() {
    return this.get('watchingFriendsOnline').concat(this.get('watchingFriendsIdle'), this.get('streamingFriends'), this.get('noActivityFriendsOnline'), this.get('noActivityFriendsIdle'));
  }),

  displayableFriends: computed('watchingPrioritizedFriends.[]', function () {
    return this.get('watchingPrioritizedFriends').slice(0, this.get('maxShowFriends'));
  }),

  _setUsers: on('init', function() {
    let store = this.get('store'),
        users = store.peekAll('friends-list-user');
    this.set('_users', users);
  }),

  _self: computed('_users.[]', function() {
    let users = this.get('_users');
    let selfId = this.get('session.userData.id').toString();
    return users.filterBy('id', selfId);
  }),

  self: computed('_self.@each.availability', '_self.@each.isOnline', '_self.@each.displayName', '_self.@each.activities', function() {
    if (this.get('_self').length) {
      return this.get('_self')[0];
    }
    return null;
  }),

  initPubSub: on('init', function () {
    this.get('pubsub').on('acceptedMessage', this, 'fetchFriends');
  }),

  // services are rarely (if ever!) destroyed, since they are singletons
  // however, aggressive teardown will prevent leaking between tests
  // and allow the option for the `requests` service to be manually destroyed if necessary
  destroyPubSub: on('willDestroy', function () {
    this.get('pubsub').off('acceptedMessage', this, 'fetchFriends');
  }),

  showMoreFriends() {
    let maxShowFriends = this.get('maxShowFriends');
    if (this.get('displayableFriends.length') < maxShowFriends) {
      this.set('maxShowFriends', maxShowFriends + 20);
    }
  },

  fetchFriends(friend) {
    const store = this.get('store');
    store.query('friends-list-user', {
      type: 'friends'
    }).then(() => {
      return store.query('friends-list-user', {
        ids: [friend.user_id],
        type: 'user-info'
      });
    }).then(() => {
      this._setUsers();
    });
  },

  search(query) {
    let friends = this.get('friends');

    if (query) {
      friends = friends.filter(friend => {
        let displayName = friend.get('displayName');
        return displayName &&
               displayName.toLowerCase().indexOf(query) !== -1;
      });
    }

    return this.sort(friends, this._availabilitySort.bind(this));
  },

  _availabilitySortCompare(a, b) {
    let x = a.get('availability'),
        y = b.get('availability');

    if (!x || !y) {
      return 0;
    }

    return AVAILABILITY[x] - AVAILABILITY[y];
  },

  _alphabeticalSort(f1, f2) {
    return compare(
      f1.get('displayName'),
      f2.get('displayName')
    );
  },

  _availabilitySort(f1, f2)  {
    let sorted = this._availabilitySortCompare(f1, f2);
    if (sorted !== 0) {
      return sorted;
    }

    return this._alphabeticalSort(f1, f2);
  },

  sort(friends, sortFn) {
    return copy(friends).sort(sortFn);
  }
});
