import PropTypes from 'prop-types';
import { omit } from 'ramda';
import React, { Component } from 'react';
import { Form } from 'semantic-ui-react';

import { withSite } from '../../addons/Site';
import { validateEmail } from '../../lib';
import Context from './Context';

/* Interface */
const propTypes = {
  ...Form.propTypes,

  /** Identity provider to use for resetting the password. */
  identityProvider: PropTypes.string,

  /** Marks the form as invalid (incomplete). */
  invalid: PropTypes.bool,

  /** Event handler called when request is sucessful. */
  onResetRequest: PropTypes.func,

  /**
   * StoryShare `Site`.
   * @private
   */
  site: PropTypes.object.isRequired,
};

/* Defaults */
const defaultProps = {
  identityProvider: 'email',
};

/**
 * A password reset request form
 * Renders an SUI `<Form>` that can contain other components to build the
 * dynamic form.
 * Reset request forms must include the `<ResetRequestForm.EmailInput>`
 * component.
 * We recommend including the following components to display error details:
 *  - `<ResetRequestForm.ErrorMessage>`
 *  - `<ResetRequestForm.InvalidEmailMessage>`
 * Any submit button will work, but `<ResetRequestForm.Button>` disables itself
 * until the required data is entered.
 * All props are passed down to SUI's `<Form>`
 * @returns {Object} component
 */
export class ResetRequestForm extends Component {
  /**
   * Handles input changes by updating value in local state
   * The following components have `<Input>` boxes that need to propagate their
   * changes to the top level:
   *  - `<RegisterForm.EmailInput>`
   * @param {ChangeEvent} event - React's original SyntheticEvent.
   * @param {Object} data - All props and a proposed value.
   * @param {string} data.name - `<Input>` field name.
   * @param {value} data.value - Proposed value.
   * @returns {void}
   */
  handleChange = (event, { name, value }) => this.setState({ [name]: value });

  /**
   * Checks whether the email is correctly entered
   * Used internally before submission, but also by `<ResetRequestForm.Button>`
   * which disables itself when the form is incomplete.
   * @returns {bool} whether the form is complete
   */
  isFormValid = () => {
    const { invalid } = this.props;
    const { email } = this.state;
    return !!(!invalid && email.trim());
  };

  /**
   * Component state
   * Also provides the public API to child components using `Context`.
   * N.B.: must be defined _after_ `handleChange` and `isFormValid` as it
   * exposes those class properties but they aren't hoisted.
   */
  state = {
    /**
     * Entered email address
     * Used by `<ResetRequestForm.EmailInput>`
     * @public
     * @type {string}
     */
    email: '',

    /**
     * Error message
     * Used by `<ResetRequestForm.ErrorMessage>` and
     * `<ResetRequestForm.InvalidEmailMessage>`.
     * @public
     * @type {'email'|'error'|null}
     */
    error: null,

    /**
     * Handles input changes by updating value in local state
     * Exposed in `state` for use by `<ResetRequestForm.EmailInput>`.
     * @public
     * @type {func}
     */
    handleChange: this.handleChange,

    /**
     * Checks whether the email is correctly entered
     * Exposed in `state` for use by `<ResetRequestForm.Button>`.
     * @public
     * @type {func}
     */
    isFormValid: this.isFormValid,

    /**
     * Whether or not we're in the process of requesting reset
     * @private
     * @type {bool}
     */
    isLoading: false,
  };

  /**
   * Handles form submission (requests a password reset)
   * @returns {Promise<void>} resolves when complete
   */
  handleSubmit = async () => {
    if (!this.isFormValid()) {
      return;
    }

    // New submission attempt: reset error state and display loading indicator
    this.setState({ error: null, isLoading: true });

    const { identityProvider, onResetRequest, site } = this.props;
    const { email } = this.state;

    if (!validateEmail(email.trim())) {
      this.setState({ error: 'email', isLoading: false });
      return;
    }

    try {
      await site.req(`identityprovider/${identityProvider}/resetrequest`)
        .setParams({
          email: email.trim(),
          return_to: window.location.href,
        })
        .promise()
        .create();

      this.setState({ isLoading: false });

      if (onResetRequest) {
        onResetRequest();
      }
    } catch (err) {
      this.setState({ error: 'error', isLoading: false });
    }
  };

  render = () => (
    <Context.Provider value={ this.state }>
      <Form
        { ...omit([
          'identityProvider',
          'invalid',
          'onResetRequest',
          'site',
        ], this.props) }
        error={ this.props.error || !!this.state.error }
        loading={ this.props.loading || this.state.isLoading }
        onSubmit={ this.handleSubmit }
      />
    </Context.Provider>
  );
}

ResetRequestForm.defaultProps = defaultProps;
ResetRequestForm.propTypes = propTypes;

export default withSite(ResetRequestForm);
