import Component from 'ember-component';
import $ from 'jquery';
import injectService from 'ember-service/inject';
import { bind, debounce } from 'ember-runloop';
import computed from 'ember-computed';
import observer from 'ember-metal/observer';
import run from 'ember-runloop';
import { htmlSafe } from 'ember-string';
import computedEqualProperty from 'web-client/utilities/computed-equal-property';
import SendRouteAction from 'web-client/mixins/send-route-action';

const PUBSUB_STREAM_UP = 'stream-up';
const PUBSUB_STREAM_DOWN = 'stream-down';

// how much of the player has to be scrolled out of
// view before it collapses to mini mode
const PLAYER_SHRINK_THRESHOLD_FACTOR = 0.5;
// number of pixels from the top of the window the user
// must scroll before the player restores to full size
const PLAYER_RESTORE_BUFFER_SPACE = 5;

export default Component.extend(SendRouteAction, {
  pubsub: injectService(),
  scrollActivity:  injectService(),
  session: injectService(),
  layout: injectService(),
  persistentPlayer: injectService(),
  experiments: injectService(),
  presence: injectService('twitch-presence/presence'),
  routing: injectService('-routing'),

  isFixed: true,
  isSmallBar: true,
  channel: null,
  isAtStartPosition: true,
  isEditingBanner: null,
  bannerShowBalloon: false,
  avatarShowBalloon: false,
  pubsubStreamStatus: '',

  playerIsPersistent: false,
  targetChannel: computed.or('channel.hostModeTarget', 'channel'),
  isFullSizePlayerPage: computed.readOnly('persistentPlayer.canBeFullSize'),

  isSubNavExperiment: null,

  isChannelIndex: computed.equal('routing.currentPath', 'user.channel.index.index'),

  placeholderStyle: computed('layout.fullSizePlayerDimensions', function() {
    let dimensions = this.get('layout.fullSizePlayerDimensions');
    return htmlSafe(`width: ${dimensions.width}px; height: ${dimensions.height}px;`);
  }),

  init() {
    this._super(...arguments);
    /*
     * Services in Ember are lazily injected, so we must call this.get('routing')
     * in order to observe it and its children.
     */
    this.get('routing');

    this.get('pubsub').on(`video-playback.${this.get('channel.name')}`, this, this.handlePubsub);

    this.get('experiments').getExperimentValue('DETERMINATION').then(value => {
      if (this.isDestroyed) { return; }
      this.set('playerIsPersistent', value === 'yes');
    });

    this.get('experiments').getExperimentValue('CPR_SUB_NAV_V2').then(value => {
      this.set('isSubNavExperiment', value === 'new');
    });

  },

  didInsertElement() {
    this.$scrollContainer = $('#main_col .tse-scroll-content');

    let $channelCover = this.$('.js-cn-cover');

    this.channelCoverHeight = $channelCover.length > 0 ? $channelCover.height() : 0;

    // TODO:  This is a layout-level div, probably move it to that level at some point.
    // TODO:  This chunk of scroll behavior could likely be externalized into a separate component bundled with top-bar.
    run.scheduleOnce('afterRender', this, 'resetScroll');

    this.get('scrollActivity').subscribe(this, this.$scrollContainer, bind(this, 'handleScroll'));
  },

  willDestroyElement() {
    this.get('scrollActivity').unsubscribe(this);
  },

  handlePubsub(payload) {
    switch (payload.type) {
      case PUBSUB_STREAM_UP:
      case PUBSUB_STREAM_DOWN:
        this.set('pubsubStreamStatus', payload.type);
        break;
    }
  },

  handleScroll() {
    if (this.isDestroyed || this.isDestroying) { return; }

    let scrollTopValue = this.$scrollContainer.scrollTop();

    if (this.get('isSubNavExperiment')) {
      if (!this.get('isChannelIndex')) {
        this.set('isSmallBar', scrollTopValue < this.channelCoverHeight);
      }
    } else {
      this.set('isFixed', scrollTopValue > (this.channelCoverHeight - 1));
    }
    this.set('isAtStartPosition', scrollTopValue === this.channelCoverHeight);

    if (this.get('playerIsPersistent')) {
      this.positionPlayerOnScroll();
    }
  },

  resetScroll() {
    if (this.isDestroying) {
      return;
    }

    this.get('layout').resetScroll();
    this.set('isFixed', true);
  },

  scrollTo(scrollValue) {
    if (this.isDestroying) {
      return;
    }

    this.get('layout').scrollTo(scrollValue);
  },

  positionPlayerOnScroll() {
    if (!this.get('isFullSizePlayerPage')) {
        return;
    }

    let $playerPlaceholder = $('.player-placeholder');

    if ($playerPlaceholder.length < 1) {
      return;
    }

    let playerHeight = this.get('layout.fullSizePlayerDimensions.height');
    let playerOffsetTop = $playerPlaceholder.offset().top;
    let playerOffsetBottom = playerOffsetTop + playerHeight;
    let playerShrinkThreshold = playerHeight - playerHeight * PLAYER_SHRINK_THRESHOLD_FACTOR;
    let playerRestoreThreshold = playerShrinkThreshold + PLAYER_RESTORE_BUFFER_SPACE;

    if (playerOffsetBottom < playerShrinkThreshold && this.get('persistentPlayer.shouldPersist')) {
      this.sendRouteAction('shrinkPlayer', 'minimize');
    } else if (playerOffsetBottom > playerRestoreThreshold) {
      this.sendRouteAction('anchorPlayer', 'maximize');
    }
  },

  shouldShowTopBar: computed('isSubNavExperiment', 'isChannelIndex', function() {
    return (this.get('isSubNavExperiment') && !this.get('isChannelIndex')) || !this.get('isSubNavExperiment');
  }),

  // TODO: This can be removed once the public Ember Routing Service exposes transition lifecycle events
  transitionHandler: observer('routing.currentPath', function() {
    run.scheduleOnce('afterRender', this, 'resetScroll');
  }),

  // TODO:  isEditable, isChannelOwner, isLive, and isLiveAccordingToKraken
  //        will likely be used beyond the scope of this component, and should
  //        perhaps be made into a service
  isChannelOwner: computedEqualProperty('session.userData.login', 'channel.id'),

  // TODO:  Maybe restrict this to channel/live page only?
  isEditable: computed('channel.id', function () {
    let { isAuthenticated, userData } = this.get('session');
    return isAuthenticated && (
      userData.login === this.get('channel.id') || // owns this channel
      userData.is_admin ||
      userData.is_staff
    );
  }),

  isLiveStatusReady: computed('channel.stream', 'channel.stream.isLoaded', 'pubsubStreamStatus', 'targetChannel.playerIsLive', function() {
    let streamLoaded = this.get('channel.stream.isLoaded');
    let pubsubStatus = this.get('pubsubStreamStatus');
    let isLivePlayer = this.get('targetChannel.playerIsLive');
    return streamLoaded || pubsubStatus !== '' || isLivePlayer !== undefined;
  }),

  isLiveAccordingToKraken: computed('channel.stream', 'channel.stream.isLoaded', 'channel.stream.isOffline', function () {
    if (!this.get('channel.stream.isLoaded')) {
      return false;
    }
    return !this.get('channel.stream.isOffline');
  }),

  isLive: computed('isLiveAccordingToKraken', 'pubsubStreamStatus', 'targetChannel.playerIsLive', function () {
    let isLiveAccordingToKraken = this.get('isLiveAccordingToKraken');
    let pubsubStatus = this.get('pubsubStreamStatus');
    let isLivePlayer = this.get('targetChannel.playerIsLive');

    if (isLivePlayer !== undefined) {
      return isLivePlayer;
    }

    if (pubsubStatus === PUBSUB_STREAM_UP) {
      return true;
    } else if (pubsubStatus === PUBSUB_STREAM_DOWN) {
      return false;
    }

    return isLiveAccordingToKraken;
  }),


  canGenerateClips: computed.and('isLive', 'session.isAuthenticated'),

  isPlaylistAccordingToKraken: computed('channel.stream', 'channel.stream.isLoaded', 'channel.stream.is_playlist', function () {
    if (!this.get('channel.stream.isLoaded')) {
      return false;
    }
    return !!this.get('channel.stream.is_playlist');
  }),

  isPlaylist: computed('isPlaylistAccordingToKraken', 'channel.playerIsPlaylist', function () {
    let isPlaylistAccordingToKraken = this.get('isPlaylistAccordingToKraken'),
        isPlaylistPlayer = this.get('channel.playerIsPlaylist');

    return isPlaylistPlayer !== undefined ? isPlaylistPlayer : isPlaylistAccordingToKraken;
  }),

  onContentChange: observer('channel.stream', 'targetChannel.game', 'channel.hostModeTarget', 'isLive', 'isChannelIndex', function() {
    /* debounce a call to `this.updatePresence(false)` by 1 second */
    debounce(this, this.updatePresence, false, 1000);
  }),

  updatePresence(calledFromRouter) {
    if (this.isDestroyed || !this.get('session.isAuthenticated')) { return; }

    let hostedChannel = this.get('channel.hostModeTarget');

    if (calledFromRouter || (!this.get('isLive') && !hostedChannel) || this.get('isPlaylist') || !this.get('isChannelIndex')) {
      return this.get('presence').updateLatestActivity();
    }

    let presence = {
      type: 'watching',
      game: this.get('channel.game') || ' ',
      channel_display_name: this.get('channel.display_name'),
      channel_login: this.get('channel.id'),
      channel_id: this.get('channel._id').toString()
    };

    if (hostedChannel) {
      presence.game = hostedChannel.get('game');
      presence.hosted_channel_display_name = hostedChannel.get('displayName');
      presence.hosted_channel_login = hostedChannel.get('name');
      presence.hosted_channel_id = hostedChannel.get('_id').toString();
    }

    this.get('presence').updateLatestActivity(presence);
  },

  actions: {
    scrollForUser(routeName) {
      if (this.get('routing.currentRouteName') === routeName) {
        if (!this.get('isSubNavExperiment')) {
          let scrollValue = this.get('isAtStartPosition') ? 0 : this.channelCoverHeight;
          run.scheduleOnce('afterRender', this, 'scrollTo', scrollValue);
        }
        return true;
      }
    },

    toggleEditingMode(isEditing) {
      if (isEditing) {
        run.scheduleOnce('afterRender', this, 'scrollTo', 0);
      } else {
        this.set('avatarShowBalloon', false);
        this.set('bannerShowBalloon', false);
      }
      this.set('isEditingBanner', isEditing);
    },

    handleExitFullscreen() {
      this.resetScroll();
    }
  }
});
