// React
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';

// Redux
import { connect } from 'react-redux';

// Lodash
import get from 'lodash/get';

// Intercept
import interceptClient from 'interceptClient';
import drupalSettings from 'drupalSettings';

// UUID
import { v4 as uuidv4 } from 'uuid';

import CloseIcon from '@material-ui/icons/Close';
import {
  AppBar,
  Button,
  Dialog,
  DialogContent,
  DialogContentText,
  DialogTitle,
  Toolbar,
  IconButton,
  Typography,
  Slide,
} from '@material-ui/core';

// Intercept Components
import SelectResource from 'intercept/SelectResource';
import SelectUser from 'intercept/SelectUser';
import InputNumber from 'intercept/Input/InputNumber';
import InputText from 'intercept/Input/InputText';
import RadioGroup from 'intercept/RadioGroup/RadioGroup';
import InputCheckbox from 'intercept/Input/InputCheckbox';

// Formsy
import Formsy, { addValidationRule } from 'formsy-react';

// Local Components
import ReserveRoomConfirmation from './ReserveRoomConfirmation';
import ReservationTeaser from './../../../ReservationTeaser';

const { actions, constants, select, utils } = interceptClient;
const c = constants;

const FIELD_REFRESHMENTS_OPTIONS = [
  {
    key: '1',
    value: 'Yes',
  },
  {
    key: '0',
    value: 'No',
  },
];

const FIELD_PUBLICIZE_DESC = get(
  drupalSettings,
  'intercept.room_reservations.field_publicize.description',
  'I would like to publicize this meeting'
);
const FIELD_PUBLICIZE_OPTIONS = [
  {
    key: '1',
    value: 'Yes',
  },
  {
    key: '0',
    value: 'No',
  },
];

const purposeRequiresExplanation = (meetingPurpose) =>
  meetingPurpose && meetingPurpose.data.attributes.field_requires_explanation;

addValidationRule('isRequired', (values, value) => value !== '');
addValidationRule('isPositive', (values, value) => value > 0);
addValidationRule('isRequiredIfServingRefreshments', (values, value) => true);
addValidationRule('isRequiredIfMeeting', (values, value) => !values.meeting || value !== '');
addValidationRule('isGreaterOrEqualTo', (values, value, min) => min === null || value >= min);
addValidationRule('isLesserOrEqualTo', (values, value, max) => max === null || value <= max);

const buildRoomReservation = (values) => {
  const uuid = uuidv4();
  const output = {
    id: uuid,
    type: c.TYPE_ROOM_RESERVATION,
    attributes: {
      uuid,
      field_attendee_count: values.attendees,
      field_dates: {
        value: moment
          .tz(utils.getDateFromTime(values.start, values.date), utils.getUserTimezone())
          .format(),
        end_value: moment
          .tz(utils.getDateFromTime(values.end, values.date), utils.getUserTimezone())
          .format(),
      },
      field_group_name: values.groupName,
      field_meeting_purpose_details: values.meetingDetails,
      field_refreshments: values.refreshments === '1',
      field_refreshments_description: {
        value: values.refreshmentsDesc,
      },
      field_publicize: values.publicize === '1',
      field_status: 'requested',
      field_agreement: values.agreement,
    },
    relationships: {
      field_event: {
        data: values[c.TYPE_EVENT]
          ? {
              type: c.TYPE_EVENT,
              id: values[c.TYPE_EVENT],
            }
          : null,
      },
      field_room: {
        data: {
          type: c.TYPE_ROOM,
          id: values[c.TYPE_ROOM],
        },
      },
      field_meeting_purpose: {
        data: values[c.TYPE_MEETING_PURPOSE]
          ? {
              type: c.TYPE_MEETING_PURPOSE,
              id: values[c.TYPE_MEETING_PURPOSE],
            }
          : null,
      },
      field_user: {
        data: {
          type: c.TYPE_USER,
          id: values.user,
        },
      },
    },
  };
  return output;
};

const Transition = React.forwardRef((props, ref) => <Slide direction="up" ref={ref} {...props} />);

const agreementText = get(drupalSettings, 'intercept.room_reservations.agreement_text', undefined);

class ReserveRoomForm extends PureComponent {
  constructor(props) {
    super(props);

    this.state = {
      expand: {
        refreshments: false,
        meeting: false,
        confirm: false,
        findRoom: false,
        findTime: false,
      },
      openAgreementDialog: false,
      openAgreementPublicityDialog: false,
      openDialog: false,
      canSubmit: false,
      uuid: null,
    };

    this.toggleState = this.toggleState.bind(this);
    this.updateValue = this.updateValue.bind(this);
    this.updateValues = this.updateValues.bind(this);
    this.toggleValue = this.toggleValue.bind(this);
    this.onCloseAgreementDialog = this.onCloseAgreementDialog.bind(this);
    this.onCloseAgreementPublicityDialog = this.onCloseAgreementPublicityDialog.bind(this);
    this.onCloseDialog = this.onCloseDialog.bind(this);
    this.onInputChange = this.onInputChange.bind(this);
    this.onOpenAgreementDialog = this.onOpenAgreementDialog.bind(this);
    this.onOpenAgreementPublicityDialog = this.onOpenAgreementPublicityDialog.bind(this);
    this.onOpenDialog = this.onOpenDialog.bind(this);
    this.onValueChange = this.onValueChange.bind(this);
    this.disableButton = this.disableButton.bind(this);
    this.enableButton = this.enableButton.bind(this);
  }

  componentDidUpdate(prevProps) {
    const { values } = this.props;
    const valuesChanged = prevProps.values.attendees !== values.attendees;

    if (valuesChanged) {
      this.forceUpdate();
    }
  }

  onInputChange(key) {
    return (event) => {
      this.updateValue(key, event.target.value);
    };
  }

  onValueChange(key) {
    return (value) => {
      this.updateValue(key, value);
    };
  }

  onOpenDialog = () => {
    this.setState({ openDialog: true });
  };

  onCloseDialog = () => {
    this.setState({ openDialog: false });
  };

  onOpenAgreementDialog = () => {
    this.setState({ openAgreementDialog: true });
  };

  onOpenAgreementPublicityDialog = () => {
    this.setState({ openAgreementPublicityDialog: true });
  };

  onCloseAgreementDialog = () => {
    this.setState({ openAgreementDialog: false });
  };

  onCloseAgreementPublicityDialog = () => {
    this.setState({ openAgreementPublicityDialog: false });
  };

  disableButton() {
    this.setState({ canSubmit: false });
  }

  enableButton() {
    this.setState({ canSubmit: true });
  }

  toggleState(key) {
    return () => {
      this.setState({
        expand: {
          ...this.state.expand,
          [key]: !this.state.expand[key],
        },
      });
    };
  }

  expand(key) {
    return () => {
      this.setState({
        expand: {
          ...this.state.expand,
          [key]: true,
        },
      });
    };
  }

  collapse(key) {
    return () => {
      this.setState({
        expand: {
          ...this.state.expand,
          [key]: false,
        },
      });
    };
  }

  saveEntitytoStore = (values) => {
    const { save } = this.props;
    const entity = buildRoomReservation(values);
    this.setState({
      uuid: entity.id,
    });
    save(entity);
    return entity.id;
  };

  updateValue(key, value) {
    const newValues = { ...this.props.values, [key]: value };
    this.props.onChange(newValues);
  }

  updateValues(value) {
    const newValues = { ...this.props.values, ...value };
    this.props.onChange(newValues);
  }

  toggleValue(key) {
    this.updateValue(key, !this.props.values[key]);
  }

  agreementLabel() {
    return (
      <React.Fragment>
        I agree to the{' '}
        <a onClick={this.onOpenAgreementDialog} role="link" tabIndex="0">
          terms of service
        </a>
        .
        <Dialog
          open={this.state.openAgreementDialog}
          onCancel={this.onCloseAgreementDialog}
          onClose={this.onCloseAgreementDialog}
          TransitionComponent={Transition}
          className="dialog dialog--fullscreen"
        >
          <DialogTitle id="responsive-dialog-title">Terms of Service</DialogTitle>
          <DialogContent>
            <DialogContentText>
              <span dangerouslySetInnerHTML={{ __html: agreementText }} />
            </DialogContentText>
          </DialogContent>
        </Dialog>
      </React.Fragment>
    );
  }

  agreementLabelRichland() {
    return (
      <React.Fragment>
        <div id="terms">
          <small>
            <p><b>Please note all meetings are open to the public. Rooms will only be held for 30 minutes after the reservation start-time and then will be released for use by other customers.</b></p>
            <p>I have read and agree to the <a onClick={this.onOpenAgreementDialog} role="link" tabIndex="0">Terms of Service</a>, including but not limited to Library spaces not being used for:<br />&nbsp;</p>
          </small>
          <Dialog
            open={this.state.openAgreementDialog}
            onCancel={this.onCloseAgreementDialog}
            onClose={this.onCloseAgreementDialog}
            TransitionComponent={Transition}
            className="dialog dialog--fullscreen"
          >
            <DialogTitle id="responsive-dialog-title">Terms of Service</DialogTitle>
            <DialogContent>
              <DialogContentText>
                <span dangerouslySetInnerHTML={{ __html: agreementText }} />
              </DialogContentText>
            </DialogContent>
          </Dialog>
        </div>
      </React.Fragment>
    );
  }

  agreementPublicityRichland() {
    return (
      <React.Fragment>
        <p className="publicity">I have read the <a onClick={this.onOpenAgreementPublicityDialog} role="link" tabIndex="0">Meeting Room Publicity Best Practices</a>, and agree that:</p>
        <Dialog
          open={this.state.openAgreementPublicityDialog}
          onCancel={this.onCloseAgreementPublicityDialog}
          onClose={this.onCloseAgreementPublicityDialog}
          TransitionComponent={Transition}
          className="dialog dialog--fullscreen"
        >
          <DialogTitle id="responsive-dialog-title">Meeting Room Publicity Best Practices</DialogTitle>
          <DialogContent>
            <DialogContentText>
              <p>We're glad you've chosen Richland Library as the place for your meeting. To help you share your event clearly and accurately, please keep these best practices in mind when promoting your meeting.</p>
              <h3>What is considered promotion?</h3>
              <p>Promotion includes any way you share your meeting with others, such as:</p>
              <ul>
                <li>Social media (including Facebook events, Instagram posts or stories, etc.)</li>
                <li>Creating flyers</li>
                <li>Using registration platforms like Eventbrite</li>
                <li>Adding it to community calendars</li>
              </ul>
              <p>All meetings must be free and open to the public. Please make sure your promotions don't suggest that Richland Library is sponsoring or endorsing your event.</p>
              <h3>How should I list Richland Library's name and address?</h3>
              <p>List Richland Library only as the meeting location and always use the full name of the library location where your meeting will take place.</p>
              <p>For example:</p>
              <ul>
                <li>Richland Library Main</li>
                <li>Richland Library Ballentine</li>
                <li>Richland Library Cooper</li>
                <li>Richland Library Wheatley</li>
              </ul>
              <h3>Can I use Richland Library's logo?</h3>
              <p>No. Please don't use Richland Library's logo on your materials, since that could suggest endorsement.</p>
              <h3>Related Policies</h3>
              <ul>
                <li><a href="/photo-video-and-media-policy">Richland Library Photo, Video and Media Policy</a></li>
              </ul> 
            </DialogContentText>
          </DialogContent>
        </Dialog>
      </React.Fragment>
    );
  }

  handleChange = (model) => {
    this.setState({ selectedOption: model.publicize });
  };

  render() {
    const {
      availabilityQuery,
      combinedValues,
      event,
      hasConflict,
      meetingPurpose,
      room,
      roomCapacity,
      values,
    } = this.props;
    const { uuid } = this.state;
    const showMeetingPurposeExplanation = !!purposeRequiresExplanation(meetingPurpose);
    const { selectedOption } = this.state;

    let content = null;
    let contact = null;
    let refreshments = null;

    this.form = React.createRef();

    // Show the reservation teaser if it has successfully saved.
    if (uuid && get(this.props.getRoomReservation(uuid), 'state.saved') === true) {
      content = <ReservationTeaser id={uuid} />;
    } else {
      // Contact Information display for customers
      if (drupalSettings.intercept.user.telephone && drupalSettings.intercept.user.email) {
        contact = (
          <div className="l--subsection">
            <h3 className="section-title section-title--secondary">
              Your Current Contact Information
            </h3>
            <small>
              Telephone: {drupalSettings.intercept.user.telephone}
              <br />
              Email: {drupalSettings.intercept.user.email}
              <br />
              <em>
                Need to update your info? After finishing your reservation visit My Account &gt;
                Settings.
              </em>
            </small>
          </div>
        );
      }
      // Refreshments text display for customers
      if (drupalSettings.intercept.room_reservations.refreshments_text) {
        refreshments = (
          <div className="l--subsection">
            <h3 className="section-title section-title--secondary">Refreshments</h3>
            <RadioGroup
              label={drupalSettings.intercept.room_reservations.refreshments_text}
              value={values.refreshments}
              onChange={this.onValueChange('refreshments')}
              name="refreshments"
              required
              options={FIELD_REFRESHMENTS_OPTIONS}
            />
            <div className="form-item">
              <InputText
                label="Please describe your light refreshments."
                value={values.refreshmentsDesc}
                onChange={this.onValueChange('refreshmentsDesc')}
                name="refreshmentDesc"
                required={values.refreshments === '1'}
                disabled={values.refreshments !== '1'}
              />
            </div>
          </div>
        );
      }
      content = (
        <Formsy
          className="form__main"
          ref={this.form}
          onValidSubmit={this.onOpenDialog}
          onValid={this.enableButton}
          onInvalid={this.disableButton}
          onChange={this.handleChange}
        >
          <div className="l--2-col">
            <div className="l__main">
              <div className="l__primary">
                <div className="l--subsection">
                  <h3 className="section-title section-title--secondary">Reservation Details</h3>
                  <div className="form-item">
                    <InputNumber
                      label="Number of Attendees"
                      value={values.attendees}
                      onChange={this.onValueChange('attendees')}
                      min={roomCapacity.min}
                      // Disable max because manually input of an out of range value has
                      // unexpected side effects with validation since the underlying input sets
                      // an out of range value to null.
                      // max={roomCapacity.max}
                      name={'attendees'}
                      int
                      required
                      validations={{
                        isPositive: true,
                        isLesserOrEqualTo: roomCapacity.max,
                        isGreaterOrEqualTo: roomCapacity.min,
                      }}
                      validationErrors={{
                        isPositive: 'Attendees must be a positive number',
                        isLesserOrEqualTo: `The maximum capacity of this room is ${roomCapacity.max}`,
                        isGreaterOrEqualTo: `The minimum capacity of this room is ${roomCapacity.min}`,
                      }}
                      helperText={
                        roomCapacity.max &&
                        `This room holds ${roomCapacity.min} to ${roomCapacity.max} people`
                      }
                    />
                  </div>
                  <div className="form-item">
                    <InputText
                      label="Group Name"
                      onChange={this.onValueChange('groupName')}
                      value={values.groupName}
                      name="groupName"
                      helperText={'Help others find you by name.'}
                    />
                  </div>
                  <div className="form-item">
                    <SelectResource
                      type={c.TYPE_MEETING_PURPOSE}
                      name={c.TYPE_MEETING_PURPOSE}
                      handleChange={this.onInputChange(c.TYPE_MEETING_PURPOSE)}
                      value={values.meetingPurpose}
                      label={'Purpose for using this room'}
                      required
                    />
                  </div>
                  <div className="form-item">
                    <InputText
                      label="Description"
                      onChange={this.onValueChange('meetingDetails')}
                      value={values.meetingDetails}
                      name="meetingDetails"
                      required={showMeetingPurposeExplanation}
                    />
                  </div>
                </div>
                {contact}
              </div>
              <div className="l__secondary">
                <div className="l--subsection">
                  <h3 className="section-title section-title--secondary">Account</h3>
                  <SelectUser
                    label="Reserved For"
                    value={values.user}
                    onChange={(value) => this.onValueChange('user')(value.uuid)}
                    name={'user'}
                  />
                </div>
                {refreshments}
                <div className="l--subsection">
                  <h3 className="section-title section-title--secondary">Publicize</h3>
                  <RadioGroup
                    label={FIELD_PUBLICIZE_DESC}
                    value={values.publicize}
                    onChange={this.onValueChange('publicize')}
                    name="publicize"
                    required
                    options={FIELD_PUBLICIZE_OPTIONS}
                  />
                  {agreementText && agreementText.includes('Richland') && selectedOption === "1" && 
                    <div className="form-item">
                      <small>
                        {this.agreementPublicityRichland()}
                        <InputCheckbox
                          label="I will not use the Richland Library logo on promotional materials or imply an endorsement or sponsorship for this meeting. *"
                          checked={values.publicityEndorsement}
                          onChange={() => this.toggleValue('publicityEndorsement')}
                          value={values.publicityEndorsement}
                          name="publicityEndorsement"
                          required={values.publicize === '1'}
                          disabled={values.publicize !== '1'}
                        />
                        <InputCheckbox
                          label="I will list Richland Library solely as the site of the meeting by using the address. *"
                          checked={values.publicityAddress}
                          onChange={() => this.toggleValue('publicityAddress')}
                          value={values.publicityAddress}
                          name="publicityAddress"
                          required={values.publicize === '1'}
                          disabled={values.publicize !== '1'}
                        />
                      </small>
                    </div>
                  }
                </div>
                {agreementText && agreementText.includes('Richland') &&
                  <div className="l--subsection">
                    <h2 className="section-title section-title--secondary">Terms of Service</h2>
                    {this.agreementLabelRichland()}
                    <InputCheckbox
                      label="Groups soliciting, selling, charging admission or asking for donations *"
                      checked={values.agreementCustom1}
                      onChange={() => this.toggleValue('agreementCustom1')}
                      required
                      value={values.agreementCustom1}
                      name="agreementCustom1"
                    />
                    <InputCheckbox
                      label="Conducting open call interviews, auditions or rehearsals *"
                      checked={values.agreementCustom2}
                      onChange={() => this.toggleValue('agreementCustom2')}
                      required
                      value={values.agreementCustom2}
                      name="agreementCustom2"
                    />
                    <InputCheckbox
                      label="Delivery of direct, hands-on healthcare and wellness services, including examinations, hands-on demos, or treatments *"
                      checked={values.agreementCustom3}
                      onChange={() => this.toggleValue('agreementCustom3')}
                      required
                      value={values.agreementCustom3}
                      name="agreementCustom3"
                    />
                    <Button
                      variant="contained"
                      size="large"
                      color="primary"
                      type="submit"
                      className="button button--primary"
                      disabled={!this.state.canSubmit || hasConflict || !room || !values.agreementCustom1 || !values.agreementCustom2 || !values.agreementCustom3}
                      >
                      Next
                    </Button>
                  </div>
                }
                {agreementText && !agreementText.includes('Richland') &&
                  <div className="l--subsection">
                    <h2 className="section-title section-title--secondary">Terms of Service</h2>
                    <InputCheckbox
                      label={this.agreementLabel()}
                      checked={values.agreement}
                      onChange={() => this.toggleValue('agreement')}
                      required
                      value={values.agreement}
                      name="agreement"
                    />
                    <Button
                      variant="contained"
                      size="large"
                      color="primary"
                      type="submit"
                      className="button button--primary"
                      disabled={!this.state.canSubmit || hasConflict || !room || !values.agreement}
                      >
                      Next
                    </Button>
                  </div>
                }
              </div>
            </div>
          </div>
        </Formsy>
      );
    }

    return (
      <div className="form">
        {content}
        <ReserveRoomConfirmation
          open={this.state.openDialog}
          onCancel={this.onCloseDialog}
          onConfirm={() =>
            this.saveEntitytoStore({
              ...combinedValues,
              [c.TYPE_ROOM]: room,
              [c.TYPE_EVENT]: event,
            })
          }
          values={{
            ...combinedValues,
            [c.TYPE_ROOM]: room,
            [c.TYPE_EVENT]: event,
          }}
          availabilityQuery={availabilityQuery}
        />

        <Dialog
          fullScreen
          open={this.state.expand.findRoom}
          onClose={() => {}}
          TransitionComponent={Transition}
          className="dialog dialog--fullscreen"
        >
          <AppBar className={'dialog__app-bar app-bar'}>
            <Toolbar>
              <IconButton color="inherit" onClick={this.collapse('findRoom')} aria-label="Close">
                <CloseIcon />
              </IconButton>
              <Typography variant="title" color="inherit" className={'app-bar_heading'}>
                Find a Room
              </Typography>
            </Toolbar>
          </AppBar>
        </Dialog>
      </div>
    );
  }
}

ReserveRoomForm.propTypes = {
  availabilityQuery: PropTypes.object.isRequired,
  values: PropTypes.shape({
    agreement: PropTypes.bool,
    agreementCustom1: PropTypes.bool,
    agreementCustom2: PropTypes.bool,
    agreementCustom3: PropTypes.bool,
    attendees: PropTypes.number,
    groupName: PropTypes.string,
    meetingDetails: PropTypes.string,
    [c.TYPE_MEETING_PURPOSE]: PropTypes.string,
    refreshments: PropTypes.string,
    refreshmentsDesc: PropTypes.string,
    publicize: PropTypes.string,
    publicityAddress: PropTypes.bool,
    publicityEndorsement: PropTypes.bool,
    user: PropTypes.string,
  }),
  onChange: PropTypes.func.isRequired,
  save: PropTypes.func.isRequired,
  meetingPurpose: PropTypes.object,
  combinedValues: PropTypes.object,
  room: PropTypes.string,
  roomCapacity: PropTypes.shape({
    min: PropTypes.number,
    max: PropTypes.number,
  }).isRequired,
  event: PropTypes.string,
  hasConflict: PropTypes.bool,
};

ReserveRoomForm.defaultProps = {
  combinedValues: {},
  values: {
    agreement: true,
    agreementCustom1: true,
    agreementCustom2: true,
    agreementCustom3: true,
    attendees: null,
    groupName: '',
    meetingPurpose: '',
    meetingDetails: '',
    refreshments: '',
    refreshmentsDesc: '',
    publicize: '',
    publicityAddress: false,
    publicityEndorsement: false,
    user: drupalSettings.intercept.user.uuid,
  },
  meetingPurpose: null,
  room: null,
  event: null,
  hasConflict: false,
};

const mapStateToProps = (state, ownProps) => ({
  meetingPurpose: ownProps.values[c.TYPE_MEETING_PURPOSE]
    ? select.record(
        select.getIdentifier(c.TYPE_MEETING_PURPOSE, ownProps.values[c.TYPE_MEETING_PURPOSE])
      )(state)
    : null,
  roomCapacity: ownProps.room
    ? select.roomCapacity(ownProps.room)(state)
    : {
        min: 0,
        max: null,
      },
  getRoomReservation: (uuid) => select.roomReservation(uuid)(state),
});

const mapDispatchToProps = (dispatch) => ({
  save: (data) => {
    dispatch(actions.add(data, c.TYPE_ROOM_RESERVATION, data.id));
  },
});

export default connect(mapStateToProps, mapDispatchToProps)(ReserveRoomForm);
