/* globals i18n, _, Twitch */

import Component from 'ember-component';
import injectService from 'ember-service/inject';
import $ from 'jquery';
import computed from 'ember-computed';
import env from 'web-client/config/environment';
import { assign } from 'ember-platform';

const POLL_INTERVAL = env.delay.dashboard.pollInterval;

const DRAGULA_CONFIG = {
  options: {
    copy: false,
    revertOnSpill: false,
    removeOnSpill: false,

    // live-widget components can only be dragged by the header
    moves(el, container, handle) {
      return $(handle).hasClass('js-live-widget__header');
    }
  },
  enabledEvents: ['drag', 'drop']
};

export const DEFAULT_DASHBOARD_CONFIG = [
  ['js-dashboard-announcement-module', 'js-dashboard-edit-broadcast', 'js-dashboard-stream-health', 'js-dashboard-run-commercial', 'js-dashboard-stream-delay'],
  ['js-dashboard-channel-feed', 'js-dashboard-video-preview', 'js-dashboard-stream-stats', 'js-dashboard-host-mode'],
  ['js-dashboard-chat-module']
];

export const COMPONENT_PATHS = {
  'dashboards/live/edit-broadcast': 'js-dashboard-edit-broadcast',
  'dashboards/live/stream-health': 'js-dashboard-stream-health',
  'dashboards/live/stream-delay': 'js-dashboard-stream-delay',
  'dashboards/live/chat-module': 'js-dashboard-chat-module',
  'dashboards/live/host-mode': 'js-dashboard-host-mode',
  'dashboards/live/stream-stats': 'js-dashboard-stream-stats',
  'dashboards/live/video-preview': 'js-dashboard-video-preview',
  'dashboards/live/run-commercial': 'js-dashboard-run-commercial',
  'dashboards/live/announcement-module': 'js-dashboard-announcement-module',
  'dashboards/live/channel-feed': 'js-dashboard-channel-feed'
};

const EDIT_BROADCAST_NAME = i18n('Stream Information');
const STREAM_HEALTH_NAME = i18n('Stream Health');
const STREAM_DELAY_NAME = i18n('Stream Delay');
const CHAT_MODULE_NAME = i18n('Chat');
const HOST_MODE_NAME = i18n('Host');
const STREAM_STATS_NAME = i18n('Stats');
const VIDEO_PREVIEW_NAME = i18n('Video Preview');
const RUN_COMMERCIAL_NAME = i18n('Run Commercial');
const ANNOUNCEMENT_MODULE_NAME = i18n('Announcements');
const CHANNEL_FEED_MODULE_NAME = i18n('Channel Feed');

export default Component.extend({
  ingest: injectService(),
  layout: injectService(),
  session: injectService(),
  storage: injectService(),
  tracking: injectService(),

  // passed in properties
  channel: null,

  // internal controller variables
  dashboardConfig: null,
  dragulaConfig: DRAGULA_CONFIG,
  prevConfig: null,

  isLive: false,
  qualityCode: null,
  reasonCodes: null,

  isChannelOwner: computed('channel.id', 'session.userData.login', function () {
    let userLogin = this.get('session.userData.login');
    let broadcasterLogin = this.get('channel.id');
    return userLogin === broadcasterLogin;
  }),

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

    this.pollTask(next => {
      if (this.isDestroyed) { return; }

      let ingest = this.get('ingest');
      let channelID = this.get('channel._id');

      ingest.getHealth({ channelID }).then(response => {
        if (this.isDestroyed) { return; }

        this.set('isLive', true);
        this.set('qualityCode', response.health_code);
        this.set('reasonCodes', response.health_reason_codes);
      }, () => {
        if (this.isDestroyed) { return; }

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

      this.runTask(next, POLL_INTERVAL);
    }, 'live-container#update-quality');

    let dashboardConfig;

    try {
      // Retrieve the dashboard column configuration from local storage:
      let dashboardConfigurationData = this.get('storage.dashboardConfiguration');
      let storedConfig = JSON.parse(dashboardConfigurationData);

      // If the stored dashboard is null, undefined, or corrupted, provide default configuation:
      if (!storedConfig || typeof storedConfig === 'undefined') {
        storedConfig = DEFAULT_DASHBOARD_CONFIG.map(configColumn => {
          return configColumn.map(className => this._convertClassToComponent(className));
        });
      }

      // Add in any components missing from the stored config
      storedConfig = this._addMissingComponents(storedConfig);

      dashboardConfig = storedConfig.map(configColumn => configColumn.map(this._convertStorageToDashboardConfig));
    } catch(e) {
      // If the an error occurs while retrieving or parsing, provide default configuration:
      dashboardConfig = DEFAULT_DASHBOARD_CONFIG.map(configColumn => {
        let componentColumn = configColumn.map(className => this._convertClassToComponent(className));

        return componentColumn.map(this._convertStorageToDashboardConfig);
      });
    } finally {
      this.set('dashboardConfig', dashboardConfig);
    }
  },

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

    // this.$('.js-dashboard-edit-column').hide();
    this.$('.js-dashboard-container').on('mousewheel', function(event) {
      // We don't want to scroll below zero or above the width and height
      let maxX = this.scrollWidth - this.offsetWidth;

      // If this event looks like it will scroll beyond the bounds of the
      // element, prevent it and set the scroll to the boundary manually
      if (this.scrollLeft + event.deltaX < 0 || this.scrollLeft + event.deltaX > maxX) {
        event.preventDefault();
        // Manual ly set the scroll to the boundary
        this.scrollLeft = Math.max(0, Math.min(maxX, this.scrollLeft + event.deltaX));
      }
    });
  },

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

    this.$('.js-dashboard-container').off('mousewheel');
  },

  actions: {
    /*
      Action handler for when a user drag and drops component. It will scan all
      the columns and save the new dashboard configuration to storage.
    */
    saveConfig(target, source = null) {
      let newDashboardConfig = [];
      let prevConfig = this.get('storage.dashboardConfiguration');

      if (!prevConfig) {
        prevConfig = DEFAULT_DASHBOARD_CONFIG.map(configColumn => {
          return configColumn.map(className => this._convertClassToComponent(className));
        });

        prevConfig = JSON.stringify(prevConfig);
      }

      let targetWidgetClasses = this.$(target).find('[class*=\'js-dashboard\']').attr('class').split(' ');
      let targetName = targetWidgetClasses.find(className => className.indexOf('js-dashboard') >= 0);

      this.$('.js-dashboard-column').each((index, column) => {
        let newColumn = [];

        this.$(column).find('[class*=\'js-dashboard\']').each((compIndex, component) => {
          let componentClasses = this.$(component).attr('class').split(' ');

          componentClasses.forEach(componentClass => {
            if (componentClass.indexOf('js-dashboard') >= 0) {
              let closestClasses = this.$(component).closest('.js-live-widget-wrapper').attr('class').split(' ');
              let isCollapsed = closestClasses.indexOf('dash-widget-wrapper--collapsed') >= 0 || closestClasses.indexOf('dash-widget-full-wrapper--collapsed') >= 0;
              let newComponent = this._convertClassToComponent(componentClass, isCollapsed);
              newColumn.push(newComponent);
            }
          });
        });

        newDashboardConfig.push(newColumn);
      });

      this.set('storage.dashboardConfiguration', JSON.stringify(newDashboardConfig));

      this.send('trackDashboardEvent', 'live_dashboard_interaction', {
        action: source ? 'move' : 'collapse',
        target_widget_name: targetName,
        start_dashboard_configuration: prevConfig,
        end_dashboard_configuration: JSON.stringify(newDashboardConfig),
        viewport_width: window.innerWidth,
        viewport_height: window.innerHeight,
        collapse_left: this.get('layout.isLeftColumnClosedByUserAction'),
        collapse_right: this.get('layout.isRightColumnClosedByUserAction')
      });
    },

    trackDashboardEvent(event, eventData = {}) {
      let dashboardConfig = this.get('storage.dashboardConfiguration');

      if (!dashboardConfig) {
        dashboardConfig = DEFAULT_DASHBOARD_CONFIG.map(configColumn => {
          return configColumn.map(className => this._convertClassToComponent(className));
        });

        dashboardConfig = JSON.stringify(dashboardConfig);
      }

      let services = ['spade'];
      let data = {
        browser: window.navigator.userAgent,
        dashboard_configuration: dashboardConfig,
        channel: this.get('channel.id'),
        channel_id: this.get('channel._id'),
        client_time: Math.floor(new Date().getTime() / 1000),
        login: this.get('session.userData.login'),
        user_id: this.get('session.userData.id'),
        language: this.get('channel.broadcasterLanguage'),
        receieved_languaged: Twitch.receivedLanguage,
        preferred_language: Twitch.preferredLanguage,
        is_staff: this.get('session.userData.is_staff'),
        is_live: this.get('isLive'),
        url: window.location.href
      };

      data = assign(data, eventData);

      this.get('tracking').trackEvent({ services, event, data });
    }
  },

  /*
    Takes an input dashboard component and assigns the necessary data fields
    such that it will render properly in the UI. This is data that we don't
    want to expose to users who may pry into localStorage
  */
  _convertStorageToDashboardConfig(component) {
    switch(component.path) {
      case 'dashboards/live/edit-broadcast': {
        return assign(component, {
          name: EDIT_BROADCAST_NAME,
          partnerOnly: false,
          noPad: false,
          fullCol: false,
          hasSettings: false
        });
      }
      case 'dashboards/live/stream-health': {
        return assign(component, {
          name: STREAM_HEALTH_NAME,
          partnerOnly: false,
          noPad: false,
          fullCol: false,
          hasSettings: false
        });
      }
      case 'dashboards/live/stream-delay': {
        return assign(component, {
          name: STREAM_DELAY_NAME,
          partnerOnly: false,
          noPad: false,
          fullCol: false,
          hasSettings: false
        });
      }
      case 'dashboards/live/chat-module': {
        return assign(component, {
          name: CHAT_MODULE_NAME,
          partnerOnly: false,
          noPad: true,
          fullCol: true,
          hasSettings: false
        });
      }
      case 'dashboards/live/host-mode': {
        return assign(component, {
          name: HOST_MODE_NAME,
          partnerOnly: false,
          noPad: false,
          fullCol: false,
          hasSettings: true,
          settingsComponentPath: 'dashboards/live/host-mode/host-settings'
        });
      }
      case 'dashboards/live/stream-stats': {
        return assign(component, {
          name: STREAM_STATS_NAME,
          partnerOnly: false,
          noPad: false,
          fullCol: false,
          hasSettings: false
        });
      }
      case 'dashboards/live/video-preview': {
        return assign(component, {
          name: VIDEO_PREVIEW_NAME,
          partnerOnly: false,
          noPad: true,
          fullCol: false,
          hasSettings: false
        });
      }
      case 'dashboards/live/run-commercial': {
        return assign(component, {
          name: RUN_COMMERCIAL_NAME,
          partnerOnly: true,
          noPad: false,
          fullCol: false,
          hasSettings: false
        });
      }
      case 'dashboards/live/announcement-module': {
        return assign(component, {
          name: ANNOUNCEMENT_MODULE_NAME,
          partnerOnly: false,
          noPad: false,
          fullCol: false,
          hasSetting: false
        });
      }
      case 'dashboards/live/channel-feed': {
        return assign(component, {
          name: CHANNEL_FEED_MODULE_NAME,
          partnerOnly: false,
          noPad: false,
          fullCol: false,
          hasSetting: false
        });
      }
      default: {
        break;
      }
    }
  },

  /*
    Takes a js class name retrieved from the UI and returns the corresponding
    component path name. Collapsed state of the component is also recorded.
    This info is used to save dashboard layout into localStorage
  */
  _convertClassToComponent(className, isCollapsed = false) {
    let componentNames = _.invert(COMPONENT_PATHS);
    let path = componentNames[className];

    if (!path) {
      return;
    }

    return { path, isCollapsed };
  },

  /*
    Adds any missing components back into the dashboard configuration. This is
    useful for when there are new components or if a user transitions from a
    non-partner dashboard to a partner dashboard. Takes in the stored config as
    the parameter and will append any missing components to the bottom of column
    one. Exception with announcement module being at the top of column one.

    With the introduction of channel feed, we want to place the module at the
    top of the middle column
  */
  _addMissingComponents(config) {
    // flatten the config into an object mapping of paths
    let configPaths = config.reduce((prev, curr) => [...prev, ...curr]).reduce((obj, component) => {
      obj[component.path] = true;
      return obj;
    }, {});

    Object.keys(COMPONENT_PATHS).forEach(componentPath => {
      if (!configPaths[componentPath]) {
        let componentName = COMPONENT_PATHS[componentPath];
        let missingComponent = this._convertClassToComponent(componentName);

        if (componentName === 'js-dashboard-announcement-module') {
          config[0] = [missingComponent, ...config[0]];
        } else if (componentName === 'js-dashboard-channel-feed') {
          config[1] = [missingComponent, ...config[1]];
        } else {
          config[0].push(missingComponent);
        }
      }
    });

    return config;
  }
});
