/**
 * History component.
 * @module components/manage/History/History
 */

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Helmet from '@plone/volto/helpers/Helmet/Helmet';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { compose } from 'redux';
import {
  Container as SemanticContainer,
  Dropdown,
  Icon,
  Segment,
  Table,
} from 'semantic-ui-react';
import concat from 'lodash/concat';
import map from 'lodash/map';
import reverse from 'lodash/reverse';
import find from 'lodash/find';
import { createPortal } from 'react-dom';
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import { asyncConnect } from '@plone/volto/helpers/AsyncConnect';

import FormattedDate from '@plone/volto/components/theme/FormattedDate/FormattedDate';
import IconNext from '@plone/volto/components/theme/Icon/Icon';
import Toolbar from '@plone/volto/components/manage/Toolbar/Toolbar';
import Forbidden from '@plone/volto/components/theme/Forbidden/Forbidden';
import Unauthorized from '@plone/volto/components/theme/Unauthorized/Unauthorized';
import {
  getHistory,
  revertHistory,
} from '@plone/volto/actions/history/history';
import { listActions } from '@plone/volto/actions/actions/actions';
import { getBaseUrl } from '@plone/volto/helpers/Url/Url';
import config from '@plone/volto/registry';

import backSVG from '@plone/volto/icons/back.svg';

const messages = defineMessages({
  back: {
    id: 'Back',
    defaultMessage: 'Back',
  },
  history: {
    id: 'History',
    defaultMessage: 'History',
  },
});

/**
 * History class.
 * @class History
 * @extends Component
 */
class History extends Component {
  /**
   * Property types.
   * @property {Object} propTypes Property types.
   * @static
   */
  static propTypes = {
    getHistory: PropTypes.func.isRequired,
    revertHistory: PropTypes.func.isRequired,
    revertRequest: PropTypes.shape({
      loaded: PropTypes.bool,
      loading: PropTypes.bool,
    }).isRequired,
    pathname: PropTypes.string.isRequired,
    entries: PropTypes.arrayOf(
      PropTypes.shape({
        transition_title: PropTypes.string,
        type: PropTypes.string,
        action: PropTypes.string,
        state_title: PropTypes.string,
        time: PropTypes.string,
        comments: PropTypes.string,
        actor: PropTypes.shape({ fullname: PropTypes.string }),
      }),
    ).isRequired,
    title: PropTypes.string.isRequired,
  };

  /**
   * Constructor
   * @method constructor
   * @param {Object} props Component properties
   * @constructs Workflow
   */
  constructor(props) {
    super(props);
    this.onRevert = this.onRevert.bind(this);
    this.state = { isClient: false };
  }

  /**
   * Component did mount
   * @method componentDidMount
   * @returns {undefined}
   */
  componentDidMount() {
    this.props.getHistory(getBaseUrl(this.props.pathname));
    this.setState({ isClient: true });
  }

  /**
   * Component will receive props
   * @method componentWillReceiveProps
   * @param {Object} nextProps Next properties
   * @returns {undefined}
   */
  UNSAFE_componentWillReceiveProps(nextProps) {
    if (this.props.revertRequest.loading && nextProps.revertRequest.loaded) {
      this.props.getHistory(getBaseUrl(this.props.pathname));
    }
  }

  /**
   * On revert
   * @method onRevert
   * @param {object} event Event object
   * @param {number} value Value
   * @returns {undefined}
   */
  onRevert(event, { value }) {
    this.props.revertHistory(getBaseUrl(this.props.pathname), value);
  }

  processHistoryEntries = () => {
    // Getting the history entries from the props
    // No clue why the reverse(concat()) is necessary
    const entries = reverse(concat(this.props.entries));
    let title = entries.length > 0 ? entries[0].state_title : '';
    for (let x = 1; x < entries.length; x += 1) {
      entries[x].prev_state_title = title;
      title = entries[x].state_title || title;
    }
    // We reverse them again
    reverse(entries);

    // We identify the latest 'versioning' entry and mark it
    const current_version = find(entries, (item) => item.type === 'versioning');
    if (current_version) {
      current_version.is_current = true;
    }
    return entries;
  };

  /**
   * Render method.
   * @method render
   * @returns {string} Markup for the component.
   */
  render() {
    const historyAction = find(this.props.objectActions, {
      id: 'history',
    });
    const entries = this.processHistoryEntries();

    const Container =
      config.getComponent({ name: 'Container' }).component || SemanticContainer;

    return !historyAction ? (
      <>
        {this.props.token ? (
          <Forbidden
            pathname={this.props.pathname}
            staticContext={this.props.staticContext}
          />
        ) : (
          <Unauthorized
            pathname={this.props.pathname}
            staticContext={this.props.staticContext}
          />
        )}
      </>
    ) : (
      <Container id="page-history">
        <Helmet title={this.props.intl.formatMessage(messages.history)} />
        <Segment.Group raised>
          <Segment className="primary">
            <FormattedMessage
              id="History of {title}"
              defaultMessage="History of {title}"
              values={{
                title: <q>{this.props.title}</q>,
              }}
            />
          </Segment>
          <Segment secondary>
            <FormattedMessage
              id="You can view the history of your item below."
              defaultMessage="You can view the history of your item below."
            />
          </Segment>
          <Table selectable compact singleLine attached>
            <Table.Header>
              <Table.Row>
                <Table.HeaderCell width={1}>
                  <FormattedMessage
                    id="History Version Number"
                    defaultMessage="#"
                  />
                </Table.HeaderCell>
                <Table.HeaderCell width={4}>
                  <FormattedMessage id="What" defaultMessage="What" />
                </Table.HeaderCell>
                <Table.HeaderCell width={4}>
                  <FormattedMessage id="Who" defaultMessage="Who" />
                </Table.HeaderCell>
                <Table.HeaderCell width={4}>
                  <FormattedMessage id="When" defaultMessage="When" />
                </Table.HeaderCell>
                <Table.HeaderCell width={4}>
                  <FormattedMessage
                    id="Change Note"
                    defaultMessage="Change Note"
                  />
                </Table.HeaderCell>
                <Table.HeaderCell />
              </Table.Row>
            </Table.Header>
            <Table.Body>
              {map(entries, (entry) => (
                <Table.Row key={entry.time}>
                  <Table.Cell>
                    {('version' in entry && entry.version > 0 && (
                      <Link
                        className="item"
                        to={`${getBaseUrl(this.props.pathname)}/diff?one=${
                          entry.version - 1
                        }&two=${entry.version}`}
                      >
                        {entry.version}
                      </Link>
                    )) || <span>{entry.version}</span>}
                  </Table.Cell>
                  <Table.Cell>
                    {('version' in entry && entry.version > 0 && (
                      <Link
                        className="item"
                        to={`${getBaseUrl(this.props.pathname)}/diff?one=${
                          entry.version - 1
                        }&two=${entry.version}`}
                      >
                        {entry.transition_title}
                      </Link>
                    )) || (
                      <span>
                        {entry.transition_title}
                        {entry.type === 'workflow' &&
                          ` (${
                            entry.action ? `${entry.prev_state_title} → ` : ''
                          }${entry.state_title})`}
                      </span>
                    )}
                  </Table.Cell>
                  <Table.Cell>{entry.actor.fullname}</Table.Cell>
                  <Table.Cell>
                    <FormattedDate date={entry.time} />
                  </Table.Cell>
                  <Table.Cell>{entry.comments}</Table.Cell>
                  <Table.Cell>
                    {entry.type === 'versioning' && (
                      <Dropdown icon="ellipsis horizontal">
                        <Dropdown.Menu className="left">
                          {'version' in entry && entry.version > 0 && (
                            <Link
                              className="item"
                              to={`${getBaseUrl(
                                this.props.pathname,
                              )}/diff?one=${entry.version - 1}&two=${
                                entry.version
                              }`}
                            >
                              <Icon name="copy" />{' '}
                              <FormattedMessage
                                id="View changes"
                                defaultMessage="View changes"
                              />
                            </Link>
                          )}
                          {'version' in entry && (
                            <Link
                              className="item"
                              to={`${getBaseUrl(this.props.pathname)}?version=${
                                entry.version
                              }`}
                            >
                              <Icon name="eye" />{' '}
                              <FormattedMessage
                                id="View this revision"
                                defaultMessage="View this revision"
                              />
                            </Link>
                          )}
                          {'version' in entry &&
                            entry.may_revert &&
                            !entry.is_current && (
                              <Dropdown.Item
                                value={entry.version}
                                onClick={this.onRevert}
                              >
                                <Icon name="undo" />{' '}
                                <FormattedMessage
                                  id="Revert to this revision"
                                  defaultMessage="Revert to this revision"
                                />
                              </Dropdown.Item>
                            )}
                        </Dropdown.Menu>
                      </Dropdown>
                    )}
                  </Table.Cell>
                </Table.Row>
              ))}
            </Table.Body>
          </Table>
        </Segment.Group>
        {this.state.isClient &&
          createPortal(
            <Toolbar
              pathname={this.props.pathname}
              hideDefaultViewButtons
              inner={
                <Link
                  to={`${getBaseUrl(this.props.pathname)}`}
                  className="item"
                >
                  <IconNext
                    name={backSVG}
                    className="contents circled"
                    size="30px"
                    title={this.props.intl.formatMessage(messages.back)}
                  />
                </Link>
              }
            />,
            document.getElementById('toolbar'),
          )}
      </Container>
    );
  }
}

export default compose(
  injectIntl,
  asyncConnect([
    {
      key: 'actions',
      // Dispatch async/await to make the operation synchronous, otherwise it returns
      // before the promise is resolved
      promise: async ({ location, store: { dispatch } }) =>
        await dispatch(listActions(getBaseUrl(location.pathname))),
    },
  ]),
  connect(
    (state, props) => ({
      objectActions: state.actions.actions.object,
      token: state.userSession.token,
      entries: state.history.entries,
      pathname: props.location.pathname,
      title: state.content.data?.title,
      revertRequest: state.history.revert,
    }),
    { getHistory, revertHistory },
  ),
)(History);
