import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { defineMessages, injectIntl, intlShape } from 'react-intl';
import _ from 'lodash';
import cx from 'classnames';
import browser from 'browser-detect';
import Button from '/imports/ui/components/button/component';
import { Session } from 'meteor/session';
import Modal from '/imports/ui/components/modal/fullscreen/component';
import { withModalMounter } from '/imports/ui/components/modal/service';
import HoldButton from '/imports/ui/components/presentation/presentation-toolbar/zoom-tool/holdButton/component';
import SortList from './sort-user-list/component';
import styles from './styles';

const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;

const intlMessages = defineMessages({
  breakoutRoomTitle: {
    id: 'app.createBreakoutRoom.title',
    description: 'modal title',
  },
  breakoutRoomDesc: {
    id: 'app.createBreakoutRoom.modalDesc',
    description: 'modal description',
  },
  confirmButton: {
    id: 'app.createBreakoutRoom.confirm',
    description: 'confirm button label',
  },
  dismissLabel: {
    id: 'app.presentationUploder.dismissLabel',
    description: 'used in the button that close modal',
  },
  numberOfRooms: {
    id: 'app.createBreakoutRoom.numberOfRooms',
    description: 'number of rooms label',
  },
  duration: {
    id: 'app.createBreakoutRoom.durationInMinutes',
    description: 'duration time label',
  },
  randomlyAssign: {
    id: 'app.createBreakoutRoom.randomlyAssign',
    description: 'randomly assign label',
  },
  roomName: {
    id: 'app.createBreakoutRoom.roomName',
    description: 'room intl to name the breakout meetings',
  },
  freeJoinLabel: {
    id: 'app.createBreakoutRoom.freeJoin',
    description: 'free join label',
  },
  roomLabel: {
    id: 'app.createBreakoutRoom.room',
    description: 'Room label',
  },
  leastOneWarnBreakout: {
    id: 'app.createBreakoutRoom.leastOneWarnBreakout',
    description: 'warn message label',
  },
  notAssigned: {
    id: 'app.createBreakoutRoom.notAssigned',
    description: 'Not assigned label',
  },
  breakoutRoomLabel: {
    id: 'app.createBreakoutRoom.breakoutRoomLabel',
    description: 'breakout room label',
  },
  addParticipantLabel: {
    id: 'app.createBreakoutRoom.addParticipantLabel',
    description: 'add Participant label',
  },
  nextLabel: {
    id: 'app.createBreakoutRoom.nextLabel',
    description: 'Next label',
  },
  backLabel: {
    id: 'app.audio.backLabel',
    description: 'Back label',
  },
  invitationTitle: {
    id: 'app.invitation.title',
    description: 'isInvitationto breakout title',
  },
  invitationConfirm: {
    id: 'app.invitation.confirm',
    description: 'Invitation to breakout confirm button label',
  },
  minusRoomTime: {
    id: 'app.createBreakoutRoom.minusRoomTime',
    description: 'aria label for btn to decrease room time',
  },
  addRoomTime: {
    id: 'app.createBreakoutRoom.addRoomTime',
    description: 'aria label for btn to increase room time',
  },
  record: {
    id: 'app.createBreakoutRoom.record',
    description: 'label for checkbox to allow record',
  },
  roomTime: {
    id: 'app.createBreakoutRoom.roomTime',
    description: 'used to provide current room time for aria label',
  },
  numberOfRoomsIsValid: {
    id: 'app.createBreakoutRoom.numberOfRoomsError',
    description: 'Label an error message',
  },
});

const MIN_BREAKOUT_ROOMS = 2;
const MAX_BREAKOUT_ROOMS = 8;

const propTypes = {
  intl: intlShape.isRequired,
  isInvitation: PropTypes.bool.isRequired,
  meetingName: PropTypes.string.isRequired,
  users: PropTypes.arrayOf(PropTypes.object).isRequired,
  createBreakoutRoom: PropTypes.func.isRequired,
  getUsersNotAssigned: PropTypes.func.isRequired,
  getBreakouts: PropTypes.func.isRequired,
  sendInvitation: PropTypes.func.isRequired,
  mountModal: PropTypes.func.isRequired,
  isBreakoutRecordable: PropTypes.bool.isRequired,
};

class BreakoutRoom extends PureComponent {
  constructor(props) {
    super(props);
    this.changeNumberOfRooms = this.changeNumberOfRooms.bind(this);
    this.changeDurationTime = this.changeDurationTime.bind(this);
    this.changeUserRoom = this.changeUserRoom.bind(this);
    this.increaseDurationTime = this.increaseDurationTime.bind(this);
    this.decreaseDurationTime = this.decreaseDurationTime.bind(this);
    this.onCreateBreakouts = this.onCreateBreakouts.bind(this);
    this.setRoomUsers = this.setRoomUsers.bind(this);
    this.setFreeJoin = this.setFreeJoin.bind(this);
    this.getUserByRoom = this.getUserByRoom.bind(this);
    this.onAssignRandomly = this.onAssignRandomly.bind(this);
    this.onInviteBreakout = this.onInviteBreakout.bind(this);
    this.renderUserItemByRoom = this.renderUserItemByRoom.bind(this);
    this.renderRoomsGrid = this.renderRoomsGrid.bind(this);
    this.renderBreakoutForm = this.renderBreakoutForm.bind(this);
    this.renderCheckboxes = this.renderCheckboxes.bind(this);
    this.renderRoomSortList = this.renderRoomSortList.bind(this);
    this.renderDesktop = this.renderDesktop.bind(this);
    this.renderMobile = this.renderMobile.bind(this);
    this.renderButtonSetLevel = this.renderButtonSetLevel.bind(this);
    this.renderSelectUserScreen = this.renderSelectUserScreen.bind(this);
    this.renderTitle = this.renderTitle.bind(this);
    this.handleDismiss = this.handleDismiss.bind(this);
    this.setInvitationConfig = this.setInvitationConfig.bind(this);
    this.setRecord = this.setRecord.bind(this);
    this.blurDurationTime = this.blurDurationTime.bind(this);
    this.removeRoomUsers = this.removeRoomUsers.bind(this);
    this.renderErrorMessages = this.renderErrorMessages.bind(this);
    this.renderJoinedUsers = this.renderJoinedUsers.bind(this);

    this.state = {
      numberOfRooms: MIN_BREAKOUT_ROOMS,
      seletedId: '',
      users: [],
      durationTime: 15,
      freeJoin: false,
      formFillLevel: 1,
      roomSelected: 0,
      preventClosing: true,
      valid: true,
      record: false,
      numberOfRoomsIsValid: true,
      breakoutJoinedUsers: null,
    };

    this.btnLevelId = _.uniqueId('btn-set-level-');
  }

  componentDidMount() {
    const { isInvitation, breakoutJoinedUsers } = this.props;
    this.setRoomUsers();
    if (isInvitation) {
      this.setInvitationConfig();
    }
    if (isInvitation) {
      this.setState({
        breakoutJoinedUsers,
      });
    }
  }

  componentDidUpdate(prevProps, prevstate) {
    const { numberOfRooms } = this.state;
    const { users } = this.props;
    const { users: prevUsers } = prevProps;
    if (numberOfRooms < prevstate.numberOfRooms) {
      this.resetUserWhenRoomsChange(numberOfRooms);
    }
    const usersCount = users.length;
    const prevUsersCount = prevUsers.length;
    if (usersCount > prevUsersCount) {
      this.setRoomUsers();
    }

    if (usersCount < prevUsersCount) {
      this.removeRoomUsers();
    }
  }

  onCreateBreakouts() {
    const {
      createBreakoutRoom,
      meetingName,
      intl,
    } = this.props;
    const {
      users,
      freeJoin,
      record,
      numberOfRoomsIsValid,
    } = this.state;

    if (users.length === this.getUserByRoom(0).length && !freeJoin) {
      this.setState({ valid: false });
      return;
    }

    if (!numberOfRoomsIsValid) {
      return;
    }

    this.setState({ preventClosing: false });
    const { numberOfRooms, durationTime } = this.state;
    const rooms = _.range(1, numberOfRooms + 1).map(value => ({
      users: this.getUserByRoom(value).map(u => u.userId),
      name: intl.formatMessage(intlMessages.roomName, {
        0: meetingName,
        1: value,
      }),
      freeJoin,
      sequence: value,
    }));

    createBreakoutRoom(rooms, durationTime, record);
    Session.set('isUserListOpen', true);
  }

  onInviteBreakout() {
    const { getBreakouts, sendInvitation } = this.props;
    const { users } = this.state;
    const breakouts = getBreakouts();
    if (users.length === this.getUserByRoom(0).length) {
      this.setState({ valid: false });
      return;
    }

    breakouts.forEach((breakout) => {
      const { breakoutId } = breakout;
      const breakoutUsers = this.getUserByRoom(breakout.sequence);
      breakoutUsers.forEach(user => sendInvitation(breakoutId, user.userId));
    });

    this.setState({ preventClosing: false });
  }

  onAssignRandomly() {
    const { numberOfRooms } = this.state;
    const { users } = this.state;
    // We only want to assign viewers so filter out the moderators. We also want to get
    // all users each run so that clicking the button again will reshuffle
    const viewers = users.filter(user => !user.isModerator);
    // We want to keep assigning users until all viewers have been assigned a room
    while (viewers.length > 0) {
      // We cycle through the rooms picking one user for each room so that the rooms
      // will have an equal number of people in each one
      for (let i = 1; i <= numberOfRooms && viewers.length > 0; i += 1) {
        // Select a random user for the room
        const userIdx = Math.floor(Math.random() * (viewers.length));
        this.changeUserRoom(viewers[userIdx].userId, i);
        // Remove the picked user so they aren't selected again
        viewers.splice(userIdx, 1);
      }
    }
  }


  setInvitationConfig() {
    const { getBreakouts } = this.props;
    this.setState({
      numberOfRooms: getBreakouts().length,
      formFillLevel: 2,
    });
  }

  setRoomUsers() {
    const { users, getUsersNotAssigned } = this.props;
    const { users: stateUsers } = this.state;
    const stateUsersId = stateUsers.map(user => user.userId);
    const roomUsers = getUsersNotAssigned(users)
      .filter(user => !stateUsersId.includes(user.userId))
      .map(user => ({
        userId: user.userId,
        userName: user.name,
        isModerator: user.role === ROLE_MODERATOR,
        room: 0,
      }));

    this.setState({
      users: [
        ...stateUsers,
        ...roomUsers,
      ],
    });
  }

  setFreeJoin(e) {
    this.setState({ freeJoin: e.target.checked, valid: true });
  }

  setRecord(e) {
    this.setState({ record: e.target.checked });
  }

  getUserByRoom(room) {
    const { users } = this.state;
    return users.filter(user => user.room === room);
  }

  getUsersByRoomSequence(sequence) {
    const { breakoutJoinedUsers } = this.state;
    if (!breakoutJoinedUsers) return [];
    return breakoutJoinedUsers.filter(room => room.sequence === sequence)[0].joinedUsers || [];
  }

  removeRoomUsers() {
    const { users } = this.props;
    const { users: stateUsers } = this.state;
    const userIds = users.map(user => user.userId);
    const removeUsers = stateUsers.filter(user => userIds.includes(user.userId));

    this.setState({
      users: removeUsers,
    });
  }

  handleDismiss() {
    const { mountModal } = this.props;

    return new Promise((resolve) => {
      mountModal(null);

      this.setState({
        preventClosing: false,
      }, resolve);
    });
  }

  resetUserWhenRoomsChange(rooms) {
    const { users } = this.state;
    const filtredUsers = users.filter(u => u.room > rooms);
    filtredUsers.forEach(u => this.changeUserRoom(u.userId, 0));
  }

  changeUserRoom(userId, room) {
    const { users } = this.state;

    const idxUser = users.findIndex(user => user.userId === userId);

    const usersCopy = [...users];

    usersCopy[idxUser].room = room;

    this.setState({
      users: usersCopy,
      valid: this.getUserByRoom(0).length !== users.length,
    });
  }

  increaseDurationTime() {
    const { durationTime } = this.state;
    this.setState({ durationTime: (1 * durationTime) + 1 });
  }

  decreaseDurationTime() {
    const { durationTime } = this.state;
    const number = ((1 * durationTime) - 1);
    this.setState({ durationTime: number < 1 ? 1 : number });
  }

  changeDurationTime(event) {
    this.setState({ durationTime: Number.parseInt(event.target.value, 10) || '' });
  }

  blurDurationTime(event) {
    const value = Number.parseInt(event.target.value, 10);
    this.setState({ durationTime: !(value <= 0) ? value : 1 });
  }

  changeNumberOfRooms(event) {
    const numberOfRooms = Number.parseInt(event.target.value, 10);
    this.setState({
      numberOfRooms,
      numberOfRoomsIsValid: numberOfRooms <= MAX_BREAKOUT_ROOMS
      && numberOfRooms >= MIN_BREAKOUT_ROOMS,
    });
  }

  renderRoomsGrid() {
    const { intl, isInvitation } = this.props;
    const {
      valid,
      numberOfRooms,
    } = this.state;
    const rooms = (numberOfRooms > MAX_BREAKOUT_ROOMS
      || numberOfRooms < MIN_BREAKOUT_ROOMS)
      ? 0 : numberOfRooms;
    const allowDrop = (ev) => {
      ev.preventDefault();
    };

    const drop = room => (ev) => {
      ev.preventDefault();
      const data = ev.dataTransfer.getData('text');
      this.changeUserRoom(data, room);
      this.setState({ seletedId: '' });
    };

    return (
      <div className={styles.boxContainer} key="rooms-grid-">
        <div className={!valid ? styles.changeToWarn : null}>
          <p className={styles.freeJoinLabel}>
            {intl.formatMessage(intlMessages.notAssigned, { 0: this.getUserByRoom(0).length })}
          </p>
          <div className={styles.breakoutBox} onDrop={drop(0)} onDragOver={allowDrop}>
            {this.renderUserItemByRoom(0)}
          </div>
          <span className={valid ? styles.dontShow : styles.leastOneWarn}>
            {intl.formatMessage(intlMessages.leastOneWarnBreakout)}
          </span>
        </div>
        {
          _.range(1, rooms + 1).map(value => (
            <div key={`room-${value}`}>
              <p className={styles.freeJoinLabel}>
                {intl.formatMessage(intlMessages.roomLabel, { 0: (value) })}
              </p>
              <div className={styles.breakoutBox} onDrop={drop(value)} onDragOver={allowDrop}>
                {this.renderUserItemByRoom(value)}
                {isInvitation && this.renderJoinedUsers(value)}
              </div>
            </div>
          ))
        }
      </div>
    );
  }

  renderBreakoutForm() {
    const {
      intl,
      isInvitation,
    } = this.props;
    const {
      numberOfRooms,
      durationTime,
      numberOfRoomsIsValid,
    } = this.state;
    if (isInvitation) return null;

    return (
      <React.Fragment key="breakout-form">
        <div className={styles.breakoutSettings}>
          <div>
            <p
              className={cx(styles.labelText, !numberOfRoomsIsValid
                && styles.withError)}
              aria-hidden
            >
              {intl.formatMessage(intlMessages.numberOfRooms)}
            </p>
            <select
              id="numberOfRooms"
              name="numberOfRooms"
              className={cx(styles.inputRooms, !numberOfRoomsIsValid
                && styles.errorBorder)}
              value={numberOfRooms}
              onChange={this.changeNumberOfRooms}
              aria-label={intl.formatMessage(intlMessages.numberOfRooms)}
            >
              {
                _.range(MIN_BREAKOUT_ROOMS, MAX_BREAKOUT_ROOMS + 1).map(item => (<option key={_.uniqueId('value-')}>{item}</option>))
              }
            </select>
          </div>
          <label htmlFor="breakoutRoomTime">
            <p className={styles.labelText} aria-hidden>
              {intl.formatMessage(intlMessages.duration)}
            </p>
            <div className={styles.durationArea}>
              <input
                type="number"
                className={styles.duration}
                min="1"
                value={durationTime}
                onChange={this.changeDurationTime}
                onBlur={this.blurDurationTime}
                aria-label={intl.formatMessage(intlMessages.duration)}
              />
              <HoldButton
                key="decrease-breakout-time"
                exec={this.decreaseDurationTime}
                minBound={MIN_BREAKOUT_ROOMS}
                value={durationTime}
                className={styles.btnStyle}
              >
                <Button
                  label={intl.formatMessage(intlMessages.minusRoomTime)}
                  aria-label={
                    `${intl.formatMessage(intlMessages.minusRoomTime)} ${intl.formatMessage(intlMessages.roomTime, { 0: durationTime - 1 })}`
                  }
                  icon="substract"
                  onClick={() => {}}
                  hideLabel
                  circle
                  size="sm"
                />
              </HoldButton>
              <HoldButton
                key="increase-breakout-time"
                exec={this.increaseDurationTime}
                className={styles.btnStyle}
              >
                <Button
                  label={intl.formatMessage(intlMessages.addRoomTime)}
                  aria-label={
                    `${intl.formatMessage(intlMessages.addRoomTime)} ${intl.formatMessage(intlMessages.roomTime, { 0: durationTime + 1 })}`
                  }
                  icon="add"
                  onClick={() => {}}
                  hideLabel
                  circle
                  size="sm"
                />
              </HoldButton>
            </div>
          </label>
          <Button
            data-test="randomlyAssign"
            label={intl.formatMessage(intlMessages.randomlyAssign)}
            className={styles.randomlyAssignBtn}
            onClick={this.onAssignRandomly}
            size="sm"
            color="default"
            disabled={!numberOfRoomsIsValid}
          />
        </div>
        <span className={!numberOfRoomsIsValid
          ? styles.withError : styles.dontShow}
        >
          {intl.formatMessage(intlMessages.numberOfRoomsIsValid)}
        </span>
      </React.Fragment>
    );
  }

  renderSelectUserScreen() {
    const {
      users,
      roomSelected,
      breakoutJoinedUsers,
    } = this.state;
    const { isInvitation } = this.props;

    return (
      <SortList
        confirm={() => this.setState({ formFillLevel: 2 })}
        users={users}
        room={roomSelected}
        breakoutJoinedUsers={isInvitation && breakoutJoinedUsers}
        onCheck={this.changeUserRoom}
        onUncheck={userId => this.changeUserRoom(userId, 0)}
      />
    );
  }

  renderCheckboxes() {
    const { intl, isInvitation, isBreakoutRecordable } = this.props;
    if (isInvitation) return null;
    const {
      freeJoin,
      record,
    } = this.state;
    return (
      <div className={styles.checkBoxesContainer} key="breakout-checkboxes">
        <label htmlFor="freeJoinCheckbox" className={styles.freeJoinLabel} key="free-join-breakouts">
          <input
            type="checkbox"
            id="freeJoinCheckbox"
            className={styles.freeJoinCheckbox}
            onChange={this.setFreeJoin}
            checked={freeJoin}
            aria-label={intl.formatMessage(intlMessages.freeJoinLabel)}
          />
          <span aria-hidden>{intl.formatMessage(intlMessages.freeJoinLabel)}</span>
        </label>
        {
          isBreakoutRecordable ? (
            <label htmlFor="recordBreakoutCheckbox" className={styles.freeJoinLabel} key="record-breakouts">
              <input
                id="recordBreakoutCheckbox"
                type="checkbox"
                className={styles.freeJoinCheckbox}
                onChange={this.setRecord}
                checked={record}
                aria-label={intl.formatMessage(intlMessages.record)}
              />
              <span aria-hidden>
                {intl.formatMessage(intlMessages.record)}
              </span>
            </label>
          ) : null
        }
      </div>
    );
  }

  renderUserItemByRoom(room) {
    const {
      valid,
      seletedId,
    } = this.state;
    const dragStart = (ev) => {
      ev.dataTransfer.setData('text', ev.target.id);
      this.setState({ seletedId: ev.target.id });

      if (!valid) {
        this.setState({ valid: true });
      }
    };


    const dragEnd = () => {
      this.setState({ seletedId: '' });
    };

    return this.getUserByRoom(room)
      .map(user => (
        <p
          id={user.userId}
          key={user.userId}
          className={cx(
            styles.roomUserItem,
            seletedId === user.userId ? styles.selectedItem : null,
          )
          }
          draggable
          onDragStart={dragStart}
          onDragEnd={dragEnd}
        >
          {user.userName}
        </p>));
  }

  renderJoinedUsers(room) {
    return this.getUsersByRoomSequence(room)
      .map(user => (
        <p
          id={user.userId}
          key={user.userId}
          disabled
          className={cx(
            styles.roomUserItem,
            styles.disableItem,
          )
          }
        >
          {user.name}
          <span className={styles.lockIcon} />
        </p>
      ));
  }

  renderRoomSortList() {
    const { intl, isInvitation } = this.props;
    const { numberOfRooms } = this.state;
    const onClick = roomNumber => this.setState({ formFillLevel: 3, roomSelected: roomNumber });
    return (
      <div className={styles.listContainer}>
        <span>
          {
            new Array(numberOfRooms).fill(1).map((room, idx) => (
              <div className={styles.roomItem}>
                <h2 className={styles.itemTitle}>
                  {intl.formatMessage(intlMessages.breakoutRoomLabel, { 0: idx + 1 })}
                </h2>
                <Button
                  className={styles.itemButton}
                  label={intl.formatMessage(intlMessages.addParticipantLabel)}
                  size="lg"
                  ghost
                  color="primary"
                  onClick={() => onClick(idx + 1)}
                />
              </div>
            ))
          }
        </span>
        { isInvitation || this.renderButtonSetLevel(1, intl.formatMessage(intlMessages.backLabel))}
      </div>
    );
  }

  renderErrorMessages() {
    const {
      intl,
    } = this.props;
    const {
      valid,
      numberOfRoomsIsValid,
    } = this.state;
    return (
      <React.Fragment>
        {!valid
          && (
          <span className={styles.withError}>
            {intl.formatMessage(intlMessages.leastOneWarnBreakout)}
          </span>)}
        {!numberOfRoomsIsValid
          && (
          <span className={styles.withError}>
            {intl.formatMessage(intlMessages.numberOfRoomsIsValid)}
          </span>)}
      </React.Fragment>
    );
  }

  renderDesktop() {
    return [
      this.renderBreakoutForm(),
      this.renderCheckboxes(),
      this.renderRoomsGrid(),
    ];
  }

  renderMobile() {
    const { intl } = this.props;
    const { formFillLevel } = this.state;
    if (formFillLevel === 2) {
      return [
        this.renderErrorMessages(),
        this.renderRoomSortList(),
      ];
    }

    if (formFillLevel === 3) {
      return [
        this.renderErrorMessages(),
        this.renderSelectUserScreen(),
      ];
    }

    return [
      this.renderErrorMessages(),
      this.renderBreakoutForm(),
      this.renderCheckboxes(),
      this.renderButtonSetLevel(2, intl.formatMessage(intlMessages.nextLabel)),
    ];
  }

  renderButtonSetLevel(level, label) {
    return (
      <Button
        color="primary"
        size="lg"
        label={label}
        onClick={() => this.setState({ formFillLevel: level })}
        key={this.btnLevelId}
      />
    );
  }

  renderTitle() {
    const { intl } = this.props;
    return (
      <p className={styles.subTitle}>
        {intl.formatMessage(intlMessages.breakoutRoomDesc)}
      </p>
    );
  }

  render() {
    const { intl, isInvitation } = this.props;
    const {
      preventClosing,
      valid,
      numberOfRoomsIsValid,
    } = this.state;

    const BROWSER_RESULTS = browser();
    const isMobileBrowser = BROWSER_RESULTS.mobile || BROWSER_RESULTS.os.includes('Android');

    return (
      <Modal
        title={
          isInvitation
            ? intl.formatMessage(intlMessages.invitationTitle)
            : intl.formatMessage(intlMessages.breakoutRoomTitle)
        }
        confirm={
          {
            label: isInvitation
              ? intl.formatMessage(intlMessages.invitationConfirm)
              : intl.formatMessage(intlMessages.confirmButton),
            callback: isInvitation ? this.onInviteBreakout : this.onCreateBreakouts,
            disabled: !valid || !numberOfRoomsIsValid,
          }
        }
        dismiss={{
          callback: this.handleDismiss,
          label: intl.formatMessage(intlMessages.dismissLabel),
        }}
        preventClosing={preventClosing}
      >
        <div className={styles.content}>
          {isInvitation || this.renderTitle()}
          {isMobileBrowser ? this.renderMobile() : this.renderDesktop()}
        </div>
      </Modal>
    );
  }
}

BreakoutRoom.propTypes = propTypes;

export default withModalMounter(injectIntl(BreakoutRoom));
