import Ember from 'ember';
import Service from 'ember-service';
import StreamModel from 'web-client/models/deprecated-stream';
import injectController from 'web-client/utilities/inject-controller';
import injectService from 'ember-service/inject';
import computed from 'ember-computed';
import { API_VERSION } from 'web-client/models/deprecated-stream';
import RSVP from 'rsvp';

const { compare } = Ember;

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

  promotedChannels: computed(function() {
    return StreamModel.find('featured').load();
  }),

  batchCount: 4,
  loggedOutMaxChannels: 10,
  maxPromotedChannels: 3,
  loggedInMaxChannels: 3,
  refreshWatchedChannels: 0,

  // NOTICE: The login service is deprecated, plesase use the session service.
  // See app/services/SESSION.md for more details.
  login: injectService(),
  isLoggedIn: computed.alias('login.isLoggedIn'),

  channelController: injectController('channel'),
  channelName: computed.alias('channelController.model.name'),

  watchedChannels: computed(function() {
    let store = this.get('store');
    store.findAll('watched-channel').then(() => {
      if (this.isDestroyed) { return; }
      let channels = store.peekAll('watched-channel');
      this.set('watchedChannels', channels.sortBy('timestamp').reverse());
    });

    return []; // default value
  }),

  maxChannels: computed('isLoggedIn', 'loggedOutMaxChannels', 'loggedInMaxChannels', function () {
    let isLoggedIn = this.get('isLoggedIn');

    if (isLoggedIn) {
      return this.get('loggedInMaxChannels');
    }
    return this.get('loggedOutMaxChannels');
  }),

  followedChannels: computed('isLoggedIn', function () {
    let isLoggedIn = this.get('isLoggedIn');

    if (isLoggedIn) {
      return StreamModel.find('live').load();
    }
    return RSVP.resolve([], 'RecommendedChannelsService#followedChannels');
  }),

  _fetchStreams(channelIds, streams=[]) {
    let batchCount = this.get('batchCount');
    let maxChannels = this.get('maxChannels');

    let channelIdBatch = channelIds.slice(0, batchCount);
    channelIds = channelIds.slice(batchCount);

    return this._fetchOrderedStreamsForChannels(channelIdBatch).then((liveStreams) => {
      if (this.isDestroyed) { return; }
      let maxRemaining = maxChannels - streams.length;
      streams.push(...liveStreams.slice(0, maxRemaining));

      if (channelIds.length && streams.length < maxChannels) {
        return this._fetchStreams(channelIds, streams);
      }
      return streams.map(stream => StreamModel.create(stream));
    });
  },

  _fetchOrderedStreamsForChannels(channelIds) {
    let url = `streams?channel=${channelIds.join(',')}`;

    return this.get('api').request('get', url, {}, { version: API_VERSION }).then((response) => {
      if (this.isDestroyed) { return; }
      let streams = response.streams;
      streams.forEach((stream) => {
        // stream.id must match channel.name so that the
        // "recommended channels" sidebar links have the correct url
        stream.id = stream.channel.name;
      });

      let compareByChannelIdPosition = (a, b) => {
        let aPos = channelIds.indexOf(a.channel.name);
        let bPos = channelIds.indexOf(b.channel.name);
        return compare(aPos, bPos);
      };

      return streams.sort(compareByChannelIdPosition);
    });
  },

  recommendedChannels: computed('watchedChannels.[]', function () {
    let ids = this.get('watchedChannels').getEach('id');

    if (ids.length) {
      this._fetchStreams(ids).then((streams) => {
        if (this.isDestroyed) { return; }
        this.set('recommendedChannels', streams);
      });
    }

    return []; // default value
  }),

  channels: computed('isLoggedIn', 'authenticatedChannels', 'nonAuthenticatedChannels', 'channelName', function () {
    let channelName = this.get('channelName');
    let isLoggedIn = this.get('isLoggedIn');

    let channels;
    if (isLoggedIn) {
      channels = this.get('authenticatedChannels');
    } else {
      channels = this.get('nonAuthenticatedChannels');
    }
    channels = this._uniqueChannels(channels);
    channels = this._removeCurrentChannel(channels, channelName);
    return channels.slice(0, this.get('maxChannels'));
  }),

  _uniqueChannels: function (channels) {
    return channels.reduce(function (previousValue, currentValue) {
      let duplicate = previousValue.findBy('channel.name', currentValue.channel.name);

      if (!duplicate) {
        previousValue.push(currentValue);
      }

      return previousValue;
    }, []);
  },

  _removeCurrentChannel: function (channels, currentChannel) {
    return channels.filter(function (channel) {
      return currentChannel !== channel.id;
    });
  },

  /*
  this function concats channels to fit the following rules
  we want to end up with 3 channels
  r: 1, f: 2
  r: 1, f: 1, p: 1
  r: 0, f: 1, p: 2
  r: 0, f: 0, p: 3
  */
  authenticatedChannels: computed('recommendedChannels.[]', 'followedChannels.{content,[]}', 'promotedChannels.{content,[]}', function () {
    let recommendedChannels = this.get('recommendedChannels');
    let followedChannels = this.get('followedChannels.content');
    let promotedChannels = this.get('promotedChannels.content');

    return [].concat(recommendedChannels, followedChannels, promotedChannels);
  }),

  /*
  this function concats channels to fit the following rules
  we ideally want 3-10 recommendedChannels
  less than 3 and we start padding with promotedChannels
  r: 10, p: 0
  r: 3,  p: 0
  r: 2,  p: 1
  r: 1,  p: 2
  r: 0,  p: 3
  */
  nonAuthenticatedChannels: computed('recommendedChannels.[]', 'promotedChannels.{content,length,[]}', function () {
    let recommendedChannels = this.get('recommendedChannels');
    let promotedChannels = this.get('promotedChannels.content');

    recommendedChannels = recommendedChannels.slice(0, this.loggedOutMaxChannels);

    if (recommendedChannels.length < this.maxPromotedChannels) {
      promotedChannels = promotedChannels.slice(0, this.maxPromotedChannels - recommendedChannels.length);
    } else {
      promotedChannels = [];
    }

    return [].concat(recommendedChannels, promotedChannels);
  })
});
