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

import React, { useEffect, useMemo } from 'react';
import Helmet from '@plone/volto/helpers/Helmet/Helmet';
import { useSelector, useDispatch } from 'react-redux';
import filter from 'lodash/filter';
import isEqual from 'lodash/isEqual';
import map from 'lodash/map';
import { Container, Button, Dropdown, Grid, Table } from 'semantic-ui-react';
import { Link, useLocation, useHistory } from 'react-router-dom';
import { createPortal } from 'react-dom';
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
import qs from 'query-string';

import { getDiff } from '@plone/volto/actions/diff/diff';
import { getSchema } from '@plone/volto/actions/schema/schema';
import { getHistory } from '@plone/volto/actions/history/history';
import { getBaseUrl } from '@plone/volto/helpers/Url/Url';
import {
  getBlocksFieldname,
  getBlocksLayoutFieldname,
  hasBlocksData,
} from '@plone/volto/helpers/Blocks/Blocks';
import FormattedDate from '@plone/volto/components/theme/FormattedDate/FormattedDate';
import Icon from '@plone/volto/components/theme/Icon/Icon';
import Toolbar from '@plone/volto/components/manage/Toolbar/Toolbar';
import Unauthorized from '@plone/volto/components/theme/Unauthorized/Unauthorized';
import DiffField from '@plone/volto/components/manage/Diff/DiffField';
import { useClient } from '@plone/volto/hooks/client/useClient';

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

const messages = defineMessages({
  diff: {
    id: 'Diff',
    defaultMessage: 'Diff',
  },
  back: {
    id: 'Back',
    defaultMessage: 'Back',
  },
  split: {
    id: 'Split',
    defaultMessage: 'Split',
  },
  unified: {
    id: 'Unified',
    defaultMessage: 'Unified',
  },
});

/**
 * Diff component.
 * @function Diff
 * @returns {JSX.Element}
 */
function Diff() {
  const dispatch = useDispatch();
  const location = useLocation();
  const history = useHistory();
  const isClient = useClient();
  const intl = useIntl();

  const data = useSelector((state) => state.diff.data);
  const historyEntries = useSelector((state) => state.history.entries);
  const schema = useSelector((state) => state.schema.schema);
  const error = useSelector((state) => state.diff.error);
  const title = useSelector((state) => state.content.data?.title);
  const type = useSelector((state) => state.content.data?.['@type']);

  const pathname = location.pathname;
  const searchParams = qs.parse(location.search);
  const one = searchParams.one;
  const two = searchParams.two;
  const view = searchParams.view || 'split';

  useEffect(() => {
    if (type) {
      dispatch(getSchema(type));
    }
    if (pathname) {
      dispatch(getHistory(getBaseUrl(pathname)));
    }
    if (pathname && one && two) {
      dispatch(getDiff(getBaseUrl(pathname), one, two));
    }
  }, [pathname, one, two, type, dispatch]);

  /**
   * On select view handler
   * @method onSelectView
   * @param {object} event Event object
   * @param {string} value Value
   * @returns {undefined}
   */
  const onSelectView = (event, { value }) => {
    history.push(`${pathname}?one=${one}&two=${two}&view=${value}`);
  };

  /**
   * On change one handler
   * @method onChangeOne
   * @param {object} event Event object
   * @param {string} value Value
   * @returns {undefined}
   */
  const onChangeOne = (event, { value }) => {
    history.push(`${pathname}?one=${value}&two=${two}&view=${view}`);
  };

  /**
   * On change two handler
   * @method onChangeTwo
   * @param {object} event Event object
   * @param {string} value Value
   * @returns {undefined}
   */
  const onChangeTwo = (event, { value }) => {
    history.push(`${pathname}?one=${one}&two=${value}&view=${view}`);
  };

  const versions = useMemo(
    () =>
      map(
        filter(historyEntries, (entry) => 'version' in entry),
        (entry, index) => ({
          text: (
            <>
              {index === 0 ? 'Current' : entry.version}&nbsp;(
              <FormattedDate date={entry.time} long className="text" />, &nbsp;
              {entry.actor.fullname})
            </>
          ),
          value: `${entry.version}`,
          key: `${entry.version}`,
        }),
      ),
    [historyEntries],
  );

  return error?.status === 401 ? (
    <Unauthorized />
  ) : (
    <Container id="page-diff">
      <Helmet title={intl.formatMessage(messages.diff)} />
      <h1>
        <FormattedMessage
          id="Difference between revision {one} and {two} of {title}"
          defaultMessage="Difference between revision {one} and {two} of {title}"
          values={{
            one,
            two,
            title,
          }}
        />
      </h1>
      <Grid>
        <Grid.Column width={9}>
          <p className="description">
            <FormattedMessage
              id="You can view the difference of the revisions below."
              defaultMessage="You can view the difference of the revisions below."
            />
          </p>
        </Grid.Column>
        <Grid.Column width={3} textAlign="right">
          <Button.Group>
            {map(
              [
                {
                  id: 'split',
                  label: intl.formatMessage(messages.split),
                },
                {
                  id: 'unified',
                  label: intl.formatMessage(messages.unified),
                },
              ],
              (viewOption) => (
                <Button
                  type="button"
                  key={viewOption.id}
                  value={viewOption.id}
                  active={view === viewOption.id}
                  onClick={onSelectView}
                >
                  {viewOption.label}
                </Button>
              ),
            )}
          </Button.Group>
        </Grid.Column>
      </Grid>
      {historyEntries.length > 0 && (
        <Table basic="very">
          <Table.Header>
            <Table.Row>
              <Table.HeaderCell width={6}>
                <FormattedMessage id="Base" defaultMessage="Base" />
                <Dropdown
                  onChange={onChangeOne}
                  value={one}
                  selection
                  fluid
                  options={versions}
                />
              </Table.HeaderCell>
              <Table.HeaderCell width={6}>
                <FormattedMessage id="Compare" defaultMessage="Compare" />
                <Dropdown
                  onChange={onChangeTwo}
                  value={two}
                  selection
                  fluid
                  options={versions}
                />
              </Table.HeaderCell>
            </Table.Row>
          </Table.Header>
        </Table>
      )}
      {schema &&
        data.length > 0 &&
        map(schema.fieldsets, (fieldset) =>
          map(
            fieldset.fields,
            (field) =>
              !isEqual(data[0][field], data[1][field]) &&
              field !== getBlocksFieldname(data[0]) &&
              field !== getBlocksLayoutFieldname(data[0]) && (
                <DiffField
                  key={field}
                  one={data[0][field]}
                  two={data[1][field]}
                  schema={schema.properties[field]}
                  view={view}
                />
              ),
          ),
        )}
      {schema &&
        data.length > 0 &&
        hasBlocksData(data[0]) &&
        (!isEqual(
          data[0][getBlocksFieldname(data[0])],
          data[1][getBlocksFieldname(data[1])],
        ) ||
          !isEqual(
            data[0][getBlocksLayoutFieldname(data[0])],
            data[1][getBlocksLayoutFieldname(data[1])],
          )) && (
          <DiffField
            one={data[0][getBlocksFieldname(data[0])]}
            two={data[1][getBlocksFieldname(data[1])]}
            contentOne={data[0]}
            contentTwo={data[1]}
            schema={schema.properties[getBlocksFieldname(data[0])]}
            view={view}
          />
        )}
      {isClient &&
        createPortal(
          <Toolbar
            pathname={pathname}
            hideDefaultViewButtons
            inner={
              <Link to={`${getBaseUrl(pathname)}/historyview`} className="item">
                <Icon
                  name={backSVG}
                  className="contents circled"
                  size="30px"
                  title={intl.formatMessage(messages.back)}
                />
              </Link>
            }
          />,
          document.getElementById('toolbar'),
        )}
    </Container>
  );
}

export default Diff;
