/* eslint-disable @typescript-eslint/no-use-before-define */
import {FakeEvent} from '@playkit-js/playkit-js';
import {EventType} from '../../event';
import {AudioSelectedEvent} from '../../event/events/audio-selected-event';
import {CaptionSelectedEvent} from '../../event/events/caption-selected-event';
import {CaptionsStyleSelectedEvent} from '../../event/events/captions-style-selected-event';
import {QualitySelectedEvent} from '../../event/events/quality-selected-event';
import {SeekedEvent} from '../../event/events/seeked-event';
import {SpeedSelectedEvent} from '../../event/events/speed-selected-event';
import {UIVisibilityChangedEvent} from '../../event/events/ui-visibility-changed-event';
import {RewindClickedEvent} from '../../event/events/rewind-clicked';
import {ForwardClickedEvent} from '../../event/events/forward-clicked';
import {VolumeChangedEvent} from '../../event/events/volume-changed';
import {KeyMap} from '../../utils';
import isEqual from '../../utils/is-equal';
import {Component, toChildArray} from 'preact';
import {KalturaPlayer} from '@playkit-js/kaltura-player-js';

/**
 * PlayerProvider component
 *
 * @class EventDispatcherProvider
 * @example <EventDispatcherProvider player={this.player} store={this.store}>...</EventDispatcherProvider>
 * @extends {Component}
 */
class EventDispatcherProvider extends Component<any, any> {
  /**
   * bounded component click handler
   * @param {Object} payload - the click data payload
   * @returns {void}
   * @private
   */
  private _notifyClick = (payload: any): void => onClickableComponentsHandler(this.props.store, payload, this.props.player);
  /**
   * bounded component state change handler
   * @param {Object} payload - the click data payload
   * @returns {void}
   * @private
   */
  private _notifyChange = (payload: any): void => onChangeableComponentsHandler(this.props.store, payload, this.props.player);
  /**
   * bounded hover change  handler
   * @param {Object} payload - the click data payload
   * @returns {void}
   * @private
   */
  private _notifyHoverChange = (payload: any): void => onPlayerHoverStateChangeHandler(this.props.store, payload, this.props.player);
  /**
   * create context player
   * @returns {void}
   */
  public getChildContext() {
    return {
      notifyClick: this._notifyClick,
      notifyChange: this._notifyChange,
      notifyHoverChange: this._notifyHoverChange
    };
  }

  /**
   * render component
   *
   * @returns {React$Element} - component element
   * @memberof EventDispatcherProvider
   */
  render() {
    return (this.props.children && toChildArray(this.props.children)[0]) || null;
  }
}
export {EventDispatcherProvider};

/**
 * Handler for hover state change action.
 * @param {any} store - The redux store.
 * @param {Object} action - The action object.
 * @param {Object} player - The video player.
 * @returns {void}
 */
function onPlayerHoverStateChangeHandler(store: any, action: any, player: KalturaPlayer): void {
  const engineState = store.getState().engine;
  const shellState = store.getState().shell;
  if (!engineState.adBreak && engineState.isPlaying && shellState.playerHover !== action.payload.hover) {
    player.dispatchEvent(new UIVisibilityChangedEvent(action.payload.hover));
  }
}

function onCaptionsStyleSelected(store: any, action: any, player: KalturaPlayer): void {
  const currentStyles = player.textStyle;
  const newStyles = action.payload?.textStyle;
  if (action.payload?.source) {
    const presetEventMap = {
      'Advanced_captions_preset_minimalist': EventType.USER_SELECTED_CAPTIONS_PRESET_MINIMALIST,
      'Advanced_captions_preset_high_contrast': EventType.USER_SELECTED_CAPTIONS_PRESET_HIGH_CONTRAST,
      'Advanced_captions_preset_classic_tv_style': EventType.USER_SELECTED_CAPTIONS_PRESET_CLASSIC_TV_STYLE,
      'Advanced_captions_preset_easy_reading': EventType.USER_SELECTED_CAPTIONS_PRESET_EASY_READING,
      'Advanced_captions_preset_early_readers': EventType.USER_SELECTED_CAPTIONS_PRESET_EARLY_READERS,
      'Advanced_captions_preset_night_mode': EventType.USER_SELECTED_CAPTIONS_PRESET_NIGHT_MODE,
      'Advanced_captions_custom': EventType.USER_SELECTED_CAPTIONS_CUSTOM,
    };

    const eventType = presetEventMap[action.payload.source];

    if (eventType) {
      player.dispatchEvent(new FakeEvent(eventType));
      console.log("Preset beacon fired:", eventType);
    }
  }

  [
    {key: 'fontSize', eventType: EventType.USER_SELECTED_CAPTIONS_SIZE},
    {key: 'fontWeight', eventType: EventType.USER_SELECTED_CAPTIONS_WEIGHT},
    {key: 'textAlign', eventType: EventType.USER_SELECTED_CAPTIONS_ALIGNMENT},
    {key: 'fontFamily', eventType: EventType.USER_SELECTED_CAPTIONS_FONT_FAMILY},
    {key: 'fontOpacity', eventType: EventType.USER_SELECTED_CAPTIONS_FONT_OPACITY},
    {key: 'backgroundOpacity', eventType: EventType.USER_SELECTED_CAPTIONS_BACKGROUND_OPACITY},
    {key: 'backgroundColor', eventType: EventType.USER_SELECTED_CAPTIONS_BACKGROUND_COLOR, collection: player.TextStyle.StandardColors},
    {key: 'fontColor', eventType: EventType.USER_SELECTED_CAPTIONS_FONT_COLOR, collection: player.TextStyle.StandardColors}
  ].map(({key, eventType, collection}) => {
    if (collection && !isEqual(currentStyles?.[key], newStyles?.[key])) {
      Object.keys(collection).some(collectionKey => {
        if (isEqual(collection[collectionKey], newStyles?.[key])) {
          player.dispatchEvent(new FakeEvent(eventType, collectionKey.toLowerCase()));
          return true;
        }
      });
    } else if (currentStyles?.[key] !== newStyles?.[key]) {
      player.dispatchEvent(new FakeEvent(eventType, newStyles?.[key]));
    }
    player.dispatchEvent(new CaptionsStyleSelectedEvent(newStyles));
  });
}

/**
 * Handler for changeable components actions.
 * @param {any} store - The redux store.
 * @param {Object} action - The action object.
 * @param {Object} player - The video player.
 * @returns {void}
 */
function onChangeableComponentsHandler(store: any, action: any, player: KalturaPlayer): void {
  switch (action.name) {
    case 'Volume':
      player.dispatchEvent(new VolumeChangedEvent(action.payload.volume));
      break;

    case 'SeekBarPlaybackContainer':
      player.dispatchEvent(new SeekedEvent(action.payload.from, action.payload.to));
      break;

    case 'SeekBarLivePlaybackContainer':
      player.dispatchEvent(new SeekedEvent(action.payload.from, action.payload.to));
      break;

    case 'ActivePreset':
      player.dispatchEvent(new FakeEvent(EventType.UI_PRESET_CHANGE, action.payload));
      break;

    default:
      break;
  }
}

/**
 * Handler for clickable components actions.
 * @param {any} store - The redux store.
 * @param {Object} action - The action object.
 * @param {Object} player - The video player.
 * @returns {void}
 */
function onClickableComponentsHandler(store: any, action: any, player: KalturaPlayer): void {
  switch (action.name) {
    case 'Keyboard':
      keyboardHandlers[action.payload.key](store, action, player);
      break;

    case 'OverlayAction':
      onOverlayActionClicked(store, action, player);
      break;

    case 'CVAAOverlay':
      onCaptionsStyleSelected(store, action, player);
      break;

    case 'Fullscreen':
      onFullScreenClicked(store, action, player);
      break;

    case 'CaptionsMenu':
      onCaptionsClick(store, action, player);
      break;

    case 'AudioMenu':
      onAudioClicked(store, action, player);
      break;

    case 'QualityMenu':
      onQualityClicked(store, action, player);
      break;

    case 'SpeedMenu':
      onSpeedClicked(store, action, player);
      break;

    case 'AudioDescriptionMenu':
    case 'AudioDesc':
    case 'AudioDescMini':
    case 'AudioDescriptionUpdater':
      onAudioDescriptionClicked(store, action, player);
      break;

    case 'Shell':
      player.dispatchEvent(new FakeEvent(EventType.UI_CLICKED));
      break;

    case 'Rewind':
      player.dispatchEvent(new RewindClickedEvent(action.payload.from, action.payload.to));
      break;

    case 'Forward':
      player.dispatchEvent(new ForwardClickedEvent(action.payload.from, action.payload.to));
      break;

    case 'LiveTag':
      player.dispatchEvent(new FakeEvent(EventType.USER_CLICKED_LIVE_TAG));
      break;
    case 'Logo':
      player.dispatchEvent(new FakeEvent(EventType.USER_CLICKED_LOGO, action.payload));
      break;

    case 'PrePlaybackPlayOverlay':
    case 'PlayPause':
      onPlayPauseClicked(store, action, player);
      break;

    case 'Volume':
      onVolumeClicked(store, action, player);
      break;

    case 'PictureInPicture':
      onPictureInPictureClicked(store, action, player);
      break;

    case 'ClosedCaptions':
      onClosedCaptionsClicked(store, action, player);
      break;
  }
}

/**
 * Handler for play-pause clicked actions.
 * @param {any} store - The redux store.
 * @param {Object} action - The action object.
 * @param {Object} player - The video player.
 * @returns {void}
 */
function onPlayPauseClicked(store: any, action: any, player: KalturaPlayer): void {
  const engineState = store.getState().engine;
  if (engineState.adBreak) {
    engineState.adIsPlaying
      ? player.dispatchEvent(new FakeEvent(EventType.USER_CLICKED_PAUSE))
      : player.dispatchEvent(new FakeEvent(EventType.USER_CLICKED_PLAY));
  } else {
    engineState.isPlaying
      ? player.dispatchEvent(new FakeEvent(EventType.USER_CLICKED_PAUSE))
      : player.dispatchEvent(new FakeEvent(EventType.USER_CLICKED_PLAY));
  }
}

/**
 * Handler for volume clicked actions.
 * @param {any} store - The redux store.
 * @param {Object} action - The action object.
 * @param {Object} player - The video player.
 * @returns {void}
 */
function onVolumeClicked(store: any, action: any, player: KalturaPlayer): void {
  const engineState = store.getState().engine;
  engineState.muted
    ? player.dispatchEvent(new FakeEvent(EventType.USER_CLICKED_MUTE))
    : player.dispatchEvent(new FakeEvent(EventType.USER_CLICKED_UNMUTE));
}

/**
 * Handler for captions menu clicked actions.
 * @param {any} store - The redux store.
 * @param {Object} action - The action object.
 * @param {Object} player - The video player.
 * @returns {void}
 */
function onCaptionsClick(store: any, action: any, player: KalturaPlayer): void {
  if (action.payload.type === player.Track.TEXT) {
    player.dispatchEvent(new CaptionSelectedEvent(action.payload.track));
  }
}

/**
 * Handler for audio menu clicked actions.
 * @param {any} store - The redux store.
 * @param {Object} action - The action object.
 * @param {Object} player - The video player.
 * @returns {void}
 */
function onAudioClicked(store: any, action: any, player: KalturaPlayer): void {
  if (action.payload.type === player.Track.AUDIO) {
    player.dispatchEvent(new AudioSelectedEvent(action.payload.track));
  }
}

/**
 * Handler for Quality menu clicked actions.
 * @param {any} store - The redux store.
 * @param {Object} action - The action object.
 * @param {Object} player - The video player.
 * @returns {void}
 */
function onQualityClicked(store: any, action: any, player: KalturaPlayer): void {
  if (action.payload.type === player.Track.VIDEO) {
    player.dispatchEvent(new QualitySelectedEvent(action.payload.track));
  }
}

/**
 * Handler for Speed menu clicked actions.
 * @param {any} store - The redux store.
 * @param {Object} action - The action object.
 * @param {Object} player - The video player.
 * @returns {void}
 */
function onSpeedClicked(store: any, action: any, player: KalturaPlayer): void {
  if (action.payload.type === 'speed') {
    player.dispatchEvent(new SpeedSelectedEvent(action.payload.speed));
  }
}

/**
 * Handler for AudioDescription menu clicked actions.
 * @param {any} store - The redux store.
 * @param {Object} action - The action object.
 * @param {Object} player - The video player.
 * @returns {void}
 */
function onAudioDescriptionClicked(store: any, action: any, player: KalturaPlayer): void {
  if (action.payload.type === 'advancedAudioDescription') {
    player.dispatchEvent(new FakeEvent(EventType.USER_CLICKED_ADVANCED_AUDIO_DESCRIPTION, action.payload));
  }
  if (action.payload.type === player.Track.AUDIO) {
    onAudioClicked(store, action, player);
  }
}

/**
 * Handler for fullscreen clicked actions.
 * @param {any} store - The redux store.
 * @param {Object} action - The action object.
 * @param {Object} player - The video player.
 * @returns {void}
 */
function onFullScreenClicked(store: any, action: any, player: KalturaPlayer): void {
  player.isFullscreen()
    ? player.dispatchEvent(new FakeEvent(EventType.USER_EXITED_FULL_SCREEN))
    : player.dispatchEvent(new FakeEvent(EventType.USER_ENTERED_FULL_SCREEN));
}

/**
 * Handler for PictureInPicture clicked actions.
 * @param {any} store - The redux store.
 * @param {Object} action - The action object.
 * @param {Object} player - The video player.
 * @returns {void}
 */
function onPictureInPictureClicked(store: any, action: any, player: KalturaPlayer): void {
  player.isInPictureInPicture()
    ? player.dispatchEvent(new FakeEvent(EventType.USER_EXITED_PICTURE_IN_PICTURE))
    : player.dispatchEvent(new FakeEvent(EventType.USER_ENTERED_PICTURE_IN_PICTURE));
}

/**
 * Handler for overlay clicked actions.
 * @param {any} store - The redux store.
 * @param {Object} action - The action object.
 * @param {Object} player - The video player.
 * @returns {void}
 */
function onOverlayActionClicked(store: any, action: any, player: KalturaPlayer): void {
  if (action.payload.type === 'PlayPause') {
    onPlayPauseClicked(store, action, player);
  } else if (action.payload.type === 'Fullscreen') {
    onFullScreenClicked(store, action, player);
  }
}

/**
 * Handler for CC clicked actions.
 * @param {any} store - The redux store.
 * @param {Object} action - The action object.
 * @param {Object} player - The video player.
 * @returns {void}
 */
function onClosedCaptionsClicked(store: any, action: any, player: KalturaPlayer): void {
  const {payload: ccOn} = action;
  const textTrack = player.getActiveTracks()?.[player.Track.TEXT];
  player.dispatchEvent(new FakeEvent(ccOn ? EventType.USER_HID_CAPTIONS : EventType.USER_SHOWED_CAPTIONS, {language: textTrack?.language}));
}

/**
 * Keyboard handler object.
 * Maps key code to its event dispatching logic.
 * @type {Object}
 */
const keyboardHandlers: {[key: number]: Function} = {
  [KeyMap.SPACE]: (store, action, player) => {
    onPlayPauseClicked(store, action, player);
  },
  [KeyMap.UP]: (store, action, player) => {
    player.dispatchEvent(new VolumeChangedEvent(action.payload.volume));
  },
  [KeyMap.DOWN]: (store, action, player) => {
    player.dispatchEvent(new VolumeChangedEvent(action.payload.volume));
  },
  [KeyMap.F]: (store, action, player) => {
    onFullScreenClicked(store, action, player);
  },
  [KeyMap.P]: (store, action, player) => {
    onPictureInPictureClicked(store, action, player);
  },
  [KeyMap.ESC]: (store, action, player) => {
    onFullScreenClicked(store, action, player);
  },
  [KeyMap.LEFT]: (store, action, player) => {
    player.dispatchEvent(new SeekedEvent(action.payload.from, action.payload.to));
  },
  [KeyMap.RIGHT]: (store, action, player) => {
    player.dispatchEvent(new SeekedEvent(action.payload.from, action.payload.to));
  },
  [KeyMap.HOME]: (store, action, player) => {
    player.dispatchEvent(new SeekedEvent(action.payload.from, action.payload.to));
  },
  [KeyMap.END]: (store, action, player) => {
    player.dispatchEvent(new SeekedEvent(action.payload.from, action.payload.to));
  },
  [KeyMap.M]: (store, action, player) => {
    onVolumeClicked(store, action, player);
  },
  [KeyMap.SEMI_COLON]: (store, action, player) => {
    player.dispatchEvent(new SpeedSelectedEvent(action.payload.speed));
  },
  [KeyMap.PERIOD]: (store, action, player) => {
    player.dispatchEvent(new SpeedSelectedEvent(action.payload.speed));
  },
  [KeyMap.COMMA]: (store, action, player) => {
    player.dispatchEvent(new SpeedSelectedEvent(action.payload.speed));
  },
  [KeyMap.C]: (store, action, player) => {
    player.dispatchEvent(new CaptionSelectedEvent(action.payload.track));
  }
};
