/* globals Twitch */

import Component from 'ember-component';
import computed from 'ember-computed';
import injectService from 'ember-service/inject';
import { keyboardListener } from 'web-client/utilities/keyboard';
import { assign } from 'ember-platform';

const SETTINGS_TRACKING_KEYS = {
  'enabled': 'autohost_toggle',
  'team_host': 'autohost_team_toggle',
  'recommended_host': 'autohost_similar_channels_toggle'
};

export const MAX_RECOMMENDED_CHANNELS = 9;
export const MAX_ONBOARDING_CHANNELS = 5;
export const MIN_ONBOARDING_CHANNELS = 2;

export const ONBOARDING_SOURCE = 'onboarding';
export const SIMILAR_CHANNELS_SOURCE = 'similar_channels';
export const FAVORITE_CHANNELS_SOURCE = 'favorite_channels';
export const ADDED_BACK_SOURCE = 'added_back';
export const SETTINGS_LOCATION = 'recommendations';
export const ONBOARDING_LOCATION = 'onboarding';

export default Component.extend({
  api: injectService(),
  autohost: injectService(),
  session: injectService(),
  similarChannels: injectService(),
  store: injectService(),
  tracking: injectService(),

  channel: null,

  teamHref: computed('channel.primaryTeamName', function() {
    let primaryTeamName = this.get('channel.primaryTeamName');
    if (primaryTeamName) {
      return Twitch.uri.team(primaryTeamName);
    }
  }),

  favoriteChannels: [],

  onboardingSource: ONBOARDING_SOURCE,
  similarChannelsSource: SIMILAR_CHANNELS_SOURCE,
  onboardingLocation: ONBOARDING_LOCATION,
  settingsLocation: SETTINGS_LOCATION,

  /* Rendering properties */
  showSimilarChannels: false,
  searchOpen: false,
  hasNoAutohostTargets: computed('autohost.isLoaded', function() {
    let hasNoAutohostTargets = this.get('autohost.list').length === 0;

    // Disable onboarding if things are loaded and there are targets
    if (this.get('autohost.isLoaded')) {
      this.set('showOnboarding', hasNoAutohostTargets);
    }

    return hasNoAutohostTargets;
  }),
  showOnboarding: true,
  hasOnboardingChannels: computed('onboardingRecommendedChannels', function() {
    let onboardingRecommendedChannels = this.get('onboardingRecommendedChannels');
    return onboardingRecommendedChannels && onboardingRecommendedChannels.length >= MIN_ONBOARDING_CHANNELS;
  }),
  hasSimilarChannels: computed.notEmpty('similarChannels.similarChannels'),
  orderedHost: computed.equal('autohost.settings.strategy', 'ordered'),
  randomHost: computed.equal('autohost.settings.strategy', 'random'),

  init() {
    this._super(...arguments);

    if (!this.get('channel')){
      let { isAuthenticated, userData } = this.get('session');
      if (isAuthenticated) {
        this.set('login', userData.login);
        this.get('store').findRecord('channel', userData.login).then(channel => {
          if (this.isDestroyed) { return; }
          this.set('channel', channel);
        });
      }
    }

    this.getRecommendedChannels();
  },

  didInsertElement() {
    this._super(...arguments);

    this.addEventListener(...keyboardListener({
      esc: this.closeSearch,
      find: this.openSearch
    }));
  },

  setSettings(key, value) {
    if (this.get('settingsUpdating')) {
      return;
    }

    let autohost = this.get('autohost');
    return autohost.setSettings(key, value).then(() => {
      if (this.isDestroyed) { return; }
      this.set('settingsUpdating', false);
    });
  },

  setAutohostList(idList) {
    if (this.get('listUpdating')) {
      return;
    }

    let autohost = this.get('autohost');

    autohost.setList(idList).then(() => {
      if (this.isDestroyed) { return; }

      this.set('listUpdating', false);
    });
  },

  getRecommendedChannels() {
    this.get('similarChannels').getSimilarChannels().then(() => {
      let api = this.get('api');
      api.authRequest('get', 'channels/favorite').then((payload) => {
        if (this.isDestroyed) { return; }
        this.set('favoriteChannels', payload.favorite_channels);
      }, () => {
        if (this.isDestroyed) { return; }
        this.set('favoriteChannels', []);
      }).then(() => {
        if (this.isDestroyed) { return; }
        this.set('onboardingRecommendedChannels', this.computeRecommendedChannels(MAX_ONBOARDING_CHANNELS));
      });
    });
  },

  closeSearch() {
    this.set('searchOpen', false);
  },

  openSearch() {
    this.set('recommendedChannels', this.computeRecommendedChannels(MAX_RECOMMENDED_CHANNELS));
    this.set('searchOpen', true);
  },

  computeRecommendedChannels(targetNumChannels) {
    // compute recommended channels based on current list
    let listMap = assign({}, this.get('autohost.listMap'));
    let ownerChannelName = this.get('channel.name');

    let favoriteChannels = this.get('favoriteChannels').filter((channel) => {
      return !listMap[channel.name] && ownerChannelName !== channel.name;
    });
    favoriteChannels.forEach((channel) => {
      listMap[channel.name] = true;
    });

    let autoHostedByChannels = this.get('autohost.autoHostedBy').filter((channel) => {
      return !listMap[channel.name];
    });
    autoHostedByChannels.forEach((channel) => {
      listMap[channel.name] = true;
    });

    let similarChannels = this.get('similarChannels.similarChannels').filter((channel) => {
      return !listMap[channel.name];
    });

    this.jitter(favoriteChannels);
    this.jitter(autoHostedByChannels);
    this.jitter(similarChannels);

    // 3 recs per type ideally
    let recSlotsLeft = targetNumChannels;
    let favRecs = [];
    let autohostedByRecs = [];
    let similarRecs = [];
    // Take the top rec from each type in order, continuing until we've filled all 9 slots
    while (recSlotsLeft > 0) {
      if (favoriteChannels.length > 0) {
        favRecs.push({channel: favoriteChannels[0], source: FAVORITE_CHANNELS_SOURCE, subtext: 'Channel you watch'});
        favoriteChannels = favoriteChannels.slice(1);
        recSlotsLeft--;
      }
      if (recSlotsLeft === 0) { break; }
      if (autoHostedByChannels.length > 0) {
        autohostedByRecs.push({channel: autoHostedByChannels[0], source: ADDED_BACK_SOURCE, subtext: 'Auto hosts you'});
        autoHostedByChannels = autoHostedByChannels.slice(1);
        recSlotsLeft--;
      }
      if (recSlotsLeft === 0) { break; }
      if (similarChannels.length > 0) {
        similarRecs.push({channel: similarChannels[0], source: SIMILAR_CHANNELS_SOURCE, subtext: 'Similar channel'});
        similarChannels = similarChannels.slice(1);
        recSlotsLeft--;
      }
      if (favoriteChannels.length + autoHostedByChannels.length + similarChannels.length === 0) { break; }
    }

    return [...favRecs, ...autohostedByRecs, ...similarRecs];
  },

  jitter(array) {
    let i = 0;
    let j = 0;
    let temp = null;

    for (i = 0; i < array.length; i++) {
      // Don't allow complete randomness, only look up to 1/3 through the remaining items
      j = i + Math.floor(Math.random() * Math.min(array.length, array.length - i) / 3);
      temp = array[i];
      array[i] = array[j];
      array[j] = temp;
    }
  },

  actions: {
    toggleSetting(key) {
      let value = !this.get(`autohost.settings.${key}`);

      let bucket = SETTINGS_TRACKING_KEYS[key];
      this.get('tracking').trackEvent({
        event: bucket,
        data: {
          time: Date.now(),
          login: this.get('login'),
          new_value: value
        }
      });

      this.setSettings(key, value);
    },

    setStrategy(value) {
      this.setSettings('strategy', value);
    },

    moveToTop(index) {
      let list = this.get('autohost.list');
      this.setAutohostList([
        list[index],
        ...list.slice(0, index),
        ...list.slice(index+1)
      ].map((user) => user._id));
    },

    moveUp(index) {
      let list = this.get('autohost.list');
      if (index > 0) {
        this.setAutohostList([
          ...list.slice(0, index - 1),
          list[index], list[index-1],
          ...list.slice(index+1)
        ].map((user) => user._id));
      }
    },

    moveDown(index) {
      let list = this.get('autohost.list');
      if (index < list.length - 1) {
        this.setAutohostList([
          ...list.slice(0, index),
          list[index + 1],
          list[index],
          ...list.slice(index+2)
        ].map((user) => user._id));
      }
    },

    remove(index) {
      let list = this.get('autohost.list');

      this.get('tracking').trackEvent({
        event: 'autohost_channel_update',
        data: {
          time: Date.now(),
          login: this.get('login'),
          target_channel: list[index].name,
          action: 'removed'
        }
      });

      this.setAutohostList([
        ...list.slice(0,index),
        ...list.slice(index+1)
      ].map((user) => user._id));
    },

    removeChannel(channel, source, location) {
      let list = this.get('autohost.list');

      this.get('tracking').trackEvent({
        event: 'autohost_channel_update',
        data: {
          time: Date.now(),
          login: this.get('login'),
          target_channel: channel.login || channel.name,
          source: source,
          location: location,
          action: 'removed'
        }
      });

      for (let i = 0; i < list.length; i++) {
        if (list[i]._id === (parseInt(channel.objectID) || channel._id)) {
          this.setAutohostList([
            ...list.slice(0,i),
            ...list.slice(i+1)
          ].map((user) => user._id));
        }
      }
    },

    addChannel(channel, source, location) {
      let list = this.get('autohost.list');

      this.get('tracking').trackEvent({
        event: 'autohost_channel_update',
        data: {
          time: Date.now(),
          login: this.get('login'),
          target_channel: channel.login || channel.name,
          source: source,
          location: location,
          action: 'added'
        }
      });

      let exceedsOnboardingThreshold = this.get('autohost.list').length >= this.get('onboardingRecommendedChannels').length - 1;

      // Adding a channel from somewhere else also dismisses onboarding
      if (source !== ONBOARDING_SOURCE || exceedsOnboardingThreshold) {
        this.set('showOnboarding', false);
      }

      this.setAutohostList(list.map((user) => user._id).concat(parseInt(channel.objectID) || channel._id));
    },

    openSearch() {
      this.openSearch();
    },

    closeSearch() {
      this.closeSearch();
    },

    hideOnboarding() {
      this.set('showOnboarding', false);
    },

    toggleShowSimilarChannels() {
      this.toggleProperty('showSimilarChannels');
    }
  }
});
