import React, { PureComponent } from 'react';
import { defineMessages, injectIntl } from 'react-intl';
import { Session } from 'meteor/session';
import logger from '/imports/startup/client/logger';
import Styled from './styles';
import Service from './service';
import MeetingRemainingTime from '../notifications-bar/meeting-remaining-time/container';
import MessageFormContainer from './message-form/container';
import VideoService from '/imports/ui/components/video-provider/service';
import { PANELS, ACTIONS } from '../layout/enums';
import { screenshareHasEnded } from '/imports/ui/components/screenshare/service';
import AudioManager from '/imports/ui/services/audio-manager';
import Settings from '/imports/ui/services/settings';
import BreakoutDropdown from '/imports/ui/components/breakout-room/breakout-dropdown/component';
import Users from '/imports/api/users';
import Auth from '/imports/ui/services/auth';
import Header from '/imports/ui/components/common/control-header/component';

const intlMessages = defineMessages({
  breakoutTitle: {
    id: 'app.createBreakoutRoom.title',
    description: 'breakout title',
  },
  breakoutAriaTitle: {
    id: 'app.createBreakoutRoom.ariaTitle',
    description: 'breakout aria title',
  },
  breakoutDuration: {
    id: 'app.createBreakoutRoom.duration',
    description: 'breakout duration time',
  },
  breakoutRoom: {
    id: 'app.createBreakoutRoom.room',
    description: 'breakout room',
  },
  breakoutJoin: {
    id: 'app.createBreakoutRoom.join',
    description: 'label for join breakout room',
  },
  breakoutJoinAudio: {
    id: 'app.createBreakoutRoom.joinAudio',
    description: 'label for option to transfer audio',
  },
  breakoutReturnAudio: {
    id: 'app.createBreakoutRoom.returnAudio',
    description: 'label for option to return audio',
  },
  askToJoin: {
    id: 'app.createBreakoutRoom.askToJoin',
    description: 'label for generate breakout room url',
  },
  generatingURL: {
    id: 'app.createBreakoutRoom.generatingURL',
    description: 'label for generating breakout room url',
  },
  endAllBreakouts: {
    id: 'app.createBreakoutRoom.endAllBreakouts',
    description: 'Button label to end all breakout rooms',
  },
  chatTitleMsgAllRooms: {
    id: 'app.createBreakoutRoom.chatTitleMsgAllRooms',
    description: 'chat title for send message to all rooms',
  },
  alreadyConnected: {
    id: 'app.createBreakoutRoom.alreadyConnected',
    description: 'label for the user that is already connected to breakout room',
  },
  setTimeInMinutes: {
    id: 'app.createBreakoutRoom.setTimeInMinutes',
    description: 'Label for input to set time (minutes)',
  },
  setTimeLabel: {
    id: 'app.createBreakoutRoom.setTimeLabel',
    description: 'Button label to set breakout rooms time',
  },
  setTimeCancel: {
    id: 'app.createBreakoutRoom.setTimeCancel',
    description: 'Button label to cancel set breakout rooms time',
  },
  setTimeHigherThanMeetingTimeError: {
    id: 'app.createBreakoutRoom.setTimeHigherThanMeetingTimeError',
    description: 'Label for error when new breakout rooms time would be higher than remaining time in parent meeting',
  },
});

let prevBreakoutData = {};

class BreakoutRoom extends PureComponent {
  static sortById(a, b) {
    if (a.userId > b.userId) {
      return 1;
    }

    if (a.userId < b.userId) {
      return -1;
    }

    return 0;
  }

  constructor(props) {
    super(props);
    this.renderBreakoutRooms = this.renderBreakoutRooms.bind(this);
    this.getBreakoutURL = this.getBreakoutURL.bind(this);
    this.hasBreakoutUrl = this.hasBreakoutUrl.bind(this);
    this.getBreakoutLabel = this.getBreakoutLabel.bind(this);
    this.renderDuration = this.renderDuration.bind(this);
    this.transferUserToBreakoutRoom = this.transferUserToBreakoutRoom.bind(this);
    this.changeSetTime = this.changeSetTime.bind(this);
    this.showSetTimeForm = this.showSetTimeForm.bind(this);
    this.resetSetTimeForm = this.resetSetTimeForm.bind(this);
    this.renderUserActions = this.renderUserActions.bind(this);
    this.returnBackToMeeeting = this.returnBackToMeeeting.bind(this);
    this.closePanel = this.closePanel.bind(this);
    this.handleClickOutsideDurationContainer = this.handleClickOutsideDurationContainer.bind(this);
    this.state = {
      requestedBreakoutId: '',
      waiting: false,
      generated: false,
      joinedAudioOnly: false,
      breakoutId: '',
      visibleSetTimeForm: false,
      visibleSetTimeHigherThanMeetingTimeError: false,
      newTime: 15,
    };
  }

  componentDidMount() {
    if (this.panel) this.panel.firstChild.focus();
  }

  componentDidUpdate() {
    const {
      getBreakoutRoomUrl,
      setBreakoutAudioTransferStatus,
      isMicrophoneUser,
      isReconnecting,
      breakoutRooms,
    } = this.props;

    const {
      waiting,
      requestedBreakoutId,
      joinedAudioOnly,
      generated,
    } = this.state;

    if (breakoutRooms.length === 0) {
      return this.closePanel();
    }

    if (waiting && !generated) {
      const breakoutUrlData = getBreakoutRoomUrl(requestedBreakoutId);

      if (!breakoutUrlData) return false;
      if (breakoutUrlData.redirectToHtml5JoinURL !== ''
        && breakoutUrlData.redirectToHtml5JoinURL !== prevBreakoutData.redirectToHtml5JoinURL) {
        prevBreakoutData = breakoutUrlData;

        Session.set('lastBreakoutIdOpened', requestedBreakoutId);
        window.open(breakoutUrlData.redirectToHtml5JoinURL, '_blank');
        setTimeout(() => {
          this.setState({ generated: true, waiting: false });
        }, 1000);
      }
    }

    if (joinedAudioOnly && (!isMicrophoneUser || isReconnecting)) {
      this.clearJoinedAudioOnly();
      setBreakoutAudioTransferStatus({
        breakoutMeetingId: '',
        status: AudioManager.BREAKOUT_AUDIO_TRANSFER_STATES.DISCONNECTED,
      });
    }
    return true;
  }

  getBreakoutURL(breakoutId) {
    const { requestJoinURL, getBreakoutRoomUrl } = this.props;
    const { waiting } = this.state;
    const breakoutRoomUrlData = getBreakoutRoomUrl(breakoutId);
    if (!breakoutRoomUrlData && !waiting) {
      this.setState(
        {
          waiting: true,
          generated: false,
          requestedBreakoutId: breakoutId,
        },
        () => requestJoinURL(breakoutId),
      );
    }

    if (breakoutRoomUrlData) {

      Session.set('lastBreakoutIdOpened', breakoutId);
      window.open(breakoutRoomUrlData.redirectToHtml5JoinURL, '_blank');
      this.setState({ waiting: false, generated: false });
    }
    return null;
  }

  hasBreakoutUrl(breakoutId) {
    const { getBreakoutRoomUrl } = this.props;
    const { requestedBreakoutId, generated } = this.state;

    const breakoutRoomUrlData = getBreakoutRoomUrl(breakoutId);

    if ((generated && requestedBreakoutId === breakoutId) || breakoutRoomUrlData) {
      return true;
    }

    return false;
  }

  getBreakoutLabel(breakoutId) {
    const { intl } = this.props;
    const hasBreakoutUrl = this.hasBreakoutUrl(breakoutId)

    if (hasBreakoutUrl) {
      return intl.formatMessage(intlMessages.breakoutJoin);
    }

    return intl.formatMessage(intlMessages.askToJoin);
  }

  clearJoinedAudioOnly() {
    this.setState({ joinedAudioOnly: false });
  }

  changeSetTime(event) {
    const newSetTime = Number.parseInt(event.target.value, 10) || 0;
    this.setState({ newTime: newSetTime >= 0 ? newSetTime : 0 });
  }

  handleClickOutsideDurationContainer(e) {
    if (this.durationContainerRef) {
      const { x, right, y, bottom } = this.durationContainerRef.getBoundingClientRect();

      if (e.clientX < x || e.clientX > right || e.clientY < y || e.clientY > bottom) {
        this.resetSetTimeForm();
      }
    }
  }

  showSetTimeForm() {
    this.setState({ visibleSetTimeForm: true });
    window.addEventListener('click', this.handleClickOutsideDurationContainer);
  }

  showSetTimeHigherThanMeetingTimeError(show) {
    this.setState({ visibleSetTimeHigherThanMeetingTimeError: show });
  }

  resetSetTimeForm() {
    this.setState({ visibleSetTimeForm: false, newTime: 5 });
    window.removeEventListener('click', this.handleClickOutsideDurationContainer);
  }

  transferUserToBreakoutRoom(breakoutId) {
    const { transferUserToMeeting, meetingId } = this.props;
    transferUserToMeeting(meetingId, breakoutId);
    this.setState({ joinedAudioOnly: true, breakoutId });
  }

  returnBackToMeeeting(breakoutId) {
    const { transferUserToMeeting, meetingId } = this.props;
    transferUserToMeeting(breakoutId, meetingId);
    this.setState({ joinedAudioOnly: false, breakoutId });
  }

  closePanel() {
    const { layoutContextDispatch } = this.props;

    layoutContextDispatch({
      type: ACTIONS.SET_SIDEBAR_CONTENT_IS_OPEN,
      value: false,
    });
    layoutContextDispatch({
      type: ACTIONS.SET_SIDEBAR_CONTENT_PANEL,
      value: PANELS.NONE,
    });
  }

  renderUserActions(breakoutId, joinedUsers, shortName) {
    const {
      isMicrophoneUser,
      amIModerator,
      amIPresenter,
      intl,
      isUserInBreakoutRoom,
      forceExitAudio,
      rejoinAudio,
      setBreakoutAudioTransferStatus,
      getBreakoutAudioTransferStatus,
      sendUserUnshareWebcam,
    } = this.props;

    const {
      joinedAudioOnly,
      breakoutId: _stateBreakoutId,
      requestedBreakoutId,
      waiting,
    } = this.state;

    const {
      breakoutMeetingId: currentAudioTransferBreakoutId,
      status,
    } = getBreakoutAudioTransferStatus();

    const isInBreakoutAudioTransfer = status
      === AudioManager.BREAKOUT_AUDIO_TRANSFER_STATES.CONNECTED;

    const stateBreakoutId = _stateBreakoutId || currentAudioTransferBreakoutId;
    const moderatorJoinedAudio = isMicrophoneUser && amIModerator;
    const disable = waiting && requestedBreakoutId !== breakoutId;
    const hasBreakoutUrl = this.hasBreakoutUrl(breakoutId);
    const dataTest = `${hasBreakoutUrl ? 'join' : 'askToJoin'}${shortName.replace(' ', '')}`;

    const audioAction = joinedAudioOnly || isInBreakoutAudioTransfer
      ? () => {
        setBreakoutAudioTransferStatus({
          breakoutMeetingId: breakoutId,
          status: AudioManager.BREAKOUT_AUDIO_TRANSFER_STATES.RETURNING,
        });
        this.returnBackToMeeeting(breakoutId);
        return logger.info({
          logCode: 'breakoutroom_return_main_audio',
          extraInfo: { logType: 'user_action' },
        }, 'Returning to main audio (breakout room audio closed)');
      }
      : () => {
        setBreakoutAudioTransferStatus({
          breakoutMeetingId: breakoutId,
          status: AudioManager.BREAKOUT_AUDIO_TRANSFER_STATES.CONNECTED,
        });
        this.transferUserToBreakoutRoom(breakoutId);
        return logger.info({
          logCode: 'breakoutroom_join_audio_from_main_room',
          extraInfo: { logType: 'user_action' },
        }, 'joining breakout room audio (main room audio closed)');
      };
    return (
      <Styled.BreakoutActions>
        {
          isUserInBreakoutRoom(joinedUsers)
            ? (
              <Styled.AlreadyConnected data-test="alreadyConnected">
                {intl.formatMessage(intlMessages.alreadyConnected)}
              </Styled.AlreadyConnected>
            )
            : (
              <Styled.JoinButton
                label={this.getBreakoutLabel(breakoutId)}
                data-test={dataTest}
                aria-label={`${this.getBreakoutLabel(breakoutId)} ${shortName}`}
                onClick={() => {
                  this.getBreakoutURL(breakoutId);
                  // leave main room's audio,
                  // and stops video and screenshare when joining a breakout room
                  forceExitAudio();
                  logger.info({
                    logCode: 'breakoutroom_join',
                    extraInfo: { logType: 'user_action' },
                  }, 'joining breakout room closed audio in the main room');
                  VideoService.storeDeviceIds();
                  VideoService.exitVideo(sendUserUnshareWebcam);
                  if (amIPresenter) screenshareHasEnded();

                  Tracker.autorun((c) => {
                    const selector = {
                      meetingId: breakoutId,
                    };

                    const query = Users.find(selector, {
                      fields: {
                        loggedOut: 1,
                        extId: 1,
                      },
                    });

                    const observeLogOut = (user) => {
                      if (user?.loggedOut && user?.extId?.startsWith(Auth.userID)) {
                        rejoinAudio();
                        c.stop();
                      }
                    }

                    query.observe({
                      added: observeLogOut,
                      changed: observeLogOut,
                    });
                  });
                }}
                disabled={disable}
              />
            )
        }
        {
          moderatorJoinedAudio
            ? [
              ('|'),
              (
                <Styled.AudioButton
                  label={
                    stateBreakoutId === breakoutId
                      && (joinedAudioOnly || isInBreakoutAudioTransfer)
                      ? intl.formatMessage(intlMessages.breakoutReturnAudio)
                      : intl.formatMessage(intlMessages.breakoutJoinAudio)
                  }
                  disabled={stateBreakoutId !== breakoutId && joinedAudioOnly}
                  key={`join-audio-${breakoutId}`}
                  onClick={audioAction}
                />
              ),
            ]
            : null
        }
      </Styled.BreakoutActions>
    );
  }

  renderBreakoutRooms() {
    const {
      breakoutRooms,
      intl,
    } = this.props;

    const {
      waiting,
      requestedBreakoutId,
    } = this.state;

    const { animations } = Settings.application;
    const roomItems = breakoutRooms.map((breakout) => (
      <Styled.BreakoutItems key={`breakoutRoomItems-${breakout.breakoutId}`}>
        <Styled.Content key={`breakoutRoomList-${breakout.breakoutId}`}>
          <Styled.BreakoutRoomListNameLabel data-test={breakout.shortName} aria-hidden>
            {breakout.isDefaultName
              ? intl.formatMessage(intlMessages.breakoutRoom, { 0: breakout.sequence })
              : breakout.shortName}
            <Styled.UsersAssignedNumberLabel>
              (
              {breakout.joinedUsers.length}
              )
            </Styled.UsersAssignedNumberLabel>
          </Styled.BreakoutRoomListNameLabel>
          {waiting && requestedBreakoutId === breakout.breakoutId ? (
            <span>
              {intl.formatMessage(intlMessages.generatingURL)}
              <Styled.ConnectingAnimation animations={animations}/>
            </span>
          ) : this.renderUserActions(
            breakout.breakoutId,
            breakout.joinedUsers,
            breakout.shortName,
          )}
        </Styled.Content>
        <Styled.JoinedUserNames
          data-test={`userNameBreakoutRoom-${breakout.shortName}`}
        >
          {breakout.joinedUsers
            .sort(BreakoutRoom.sortById)
            .filter((value, idx, arr) => !(value.userId === (arr[idx + 1] || {}).userId))
            .sort(Service.sortUsersByName)
            .map((u) => u.name)
            .join(', ')}
        </Styled.JoinedUserNames>
      </Styled.BreakoutItems>
    ));

    return (
      <Styled.BreakoutColumn>
        <Styled.BreakoutScrollableList data-test="breakoutRoomList">
          {roomItems}
        </Styled.BreakoutScrollableList>
      </Styled.BreakoutColumn>
    );
  }

  renderDuration() {
    const {
      intl,
      breakoutRooms,
      amIModerator,
      isMeteorConnected,
      setBreakoutsTime,
      isNewTimeHigherThanMeetingRemaining,
    } = this.props;
    const {
      newTime,
      visibleSetTimeForm,
      visibleSetTimeHigherThanMeetingTimeError,
    } = this.state;
    return (
      <Styled.DurationContainer
        centeredText={!visibleSetTimeForm}
        ref={(ref) => this.durationContainerRef = ref}
      >
        <Styled.Duration>
          <MeetingRemainingTime
            messageDuration={intlMessages.breakoutDuration}
            breakoutRoom={breakoutRooms[0]}
            fromBreakoutPanel
          />
        </Styled.Duration>
        {amIModerator && visibleSetTimeForm ? (
          <Styled.SetTimeContainer>
            <label htmlFor="inputSetTimeSelector" >
              {intl.formatMessage(intlMessages.setTimeInMinutes)}
            </label>
            <br />
            <Styled.FlexRow>
              <Styled.SetDurationInput
                id="inputSetTimeSelector"
                type="number"
                min="1"
                value={newTime}
                onChange={this.changeSetTime}
                aria-label={intl.formatMessage(intlMessages.setTimeInMinutes)}
              />
              &nbsp;
              &nbsp;
              <Styled.EndButton
                data-test="sendButtonDurationTime"
                color="primary"
                disabled={!isMeteorConnected}
                size="sm"
                label={intl.formatMessage(intlMessages.setTimeLabel)}
                onClick={() => {
                  this.showSetTimeHigherThanMeetingTimeError(false);

                  if (isNewTimeHigherThanMeetingRemaining(newTime)) {
                    this.showSetTimeHigherThanMeetingTimeError(true);
                  } else if (setBreakoutsTime(newTime)) {
                    this.resetSetTimeForm();
                  }
                }}
              />
            </Styled.FlexRow>
            {visibleSetTimeHigherThanMeetingTimeError ? (
              <Styled.WithError>
                {intl.formatMessage(intlMessages.setTimeHigherThanMeetingTimeError)}
              </Styled.WithError>
            ) : null}
          </Styled.SetTimeContainer>
        ) : null}
      </Styled.DurationContainer>
    );
  }

  render() {
    const {
      isMeteorConnected,
      intl,
      endAllBreakouts,
      amIModerator,
      isRTL,
    } = this.props;
    return (
      <Styled.Panel ref={(n) => this.panel = n} onCopy={(e) => { e.stopPropagation(); }}>
        <Header
          leftButtonProps={{
            'aria-label': intl.formatMessage(intlMessages.breakoutAriaTitle),
            label: intl.formatMessage(intlMessages.breakoutTitle),
            onClick: () => {
              this.closePanel();
            },
          }}
          customRightButton={amIModerator && (
            <BreakoutDropdown
              openBreakoutTimeManager={this.showSetTimeForm}
              endAllBreakouts={() => {
                this.closePanel();
                endAllBreakouts();
              }}
              isMeteorConnected={isMeteorConnected}
              amIModerator={amIModerator}
              isRTL={isRTL}
            />
          )}
        />
        {this.renderDuration()}
        {amIModerator
          ? (
            <MessageFormContainer
              {...{
                title: intl.formatMessage(intlMessages.chatTitleMsgAllRooms),
              }}
              chatId="breakouts"
              chatTitle={intl.formatMessage(intlMessages.chatTitleMsgAllRooms)}
              disabled={!isMeteorConnected}
              connected={isMeteorConnected}
              locked={false}
            />
          ) : null }
        {amIModerator ? <Styled.Separator /> : null }
        {this.renderBreakoutRooms()}
      </Styled.Panel>
    );
  }
}

export default injectIntl(BreakoutRoom);
