/* global window */

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Immutable from 'immutable';
import get from 'lodash/get';
import ImageGallery from 'react-image-gallery';
import { defineMessages, intlShape } from 'react-intl';
import { baseComponents as inputComponents } from 'cspace-input';
import ImageContainer from '../../containers/media/ImageContainer';
import { getContentPath } from '../../helpers/contentHelpers';

import {
  VIEWER_WINDOW_NAME,
  getImageViewerPath,
} from '../../helpers/blobHelpers';

import styles from '../../../styles/cspace-ui/MediaViewer.css';

// eslint-disable-next-line import/no-webpack-loader-syntax, import/no-unresolved
import '!style-loader!css-loader!react-image-gallery/styles/css/image-gallery.css';

const { MiniButton } = inputComponents;

const messages = defineMessages({
  previous: {
    id: 'mediaViewer.previous',
    description: 'Label of the button to show the previous image in the media viewer.',
    defaultMessage: 'Previous image',
  },
  next: {
    id: 'mediaViewer.next',
    description: 'Label of the button to show the next image in the media viewer.',
    defaultMessage: 'Next image',
  },
});

const renderItem = (item) => (
  <div className="image-gallery-image">
    <ImageContainer
      src={item.snapshot}
      alt={item.snapshotAlt}
      srcSet={item.srcSet}
      sizes={item.sizes}
      title={item.snapshotTitle}
      data-csid={item.blobCsid}
    />
  </div>
);

const renderThumbInner = (item) => (
  <div>
    <ImageContainer
      src={item.thumbnail}
      alt={item.thumbnailAlt}
      title={item.thumbnailTitle}
    />
    {
      item.thumbnailLabel && (
        <div className="image-gallery-thumbnail-label">
          {item.thumbnailLabel}
        </div>
      )
    }
  </div>
);

const sortByPriority = (items, priorityOrder) => {
  if (!priorityOrder) {
    return items;
  }

  const priorityRefNames = Immutable.List.isList(priorityOrder)
    ? priorityOrder
    : Immutable.List.of(priorityOrder);

  const indexByRefName = Immutable.Map(
    priorityRefNames.map((refName, index) => [refName, index]),
  );

  // sortBy is stable, so items not in the priority order keep their relative
  // positions at the end.
  return items.sortBy((item) => indexByRefName.get(item.get('refName'), Infinity));
};

const makeRenderNav = (name, symbol, label) => (onClick, disabled) => (
  <MiniButton name={name} disabled={disabled} onClick={onClick} aria-label={label}>
    {symbol}
  </MiniButton>
);

const propTypes = {
  config: PropTypes.shape({
    listTypes: PropTypes.object,
    recordTypes: PropTypes.object,
  }).isRequired,
  isSearchPending: PropTypes.bool,
  listType: PropTypes.string,
  ownFields: PropTypes.shape({
    ownBlobCsid: PropTypes.string,
    ownAltText: PropTypes.string,
    ownIdentificationNumber: PropTypes.string,
  }),
  priorityOrder: PropTypes.oneOfType(
    [PropTypes.string, PropTypes.instanceOf(Immutable.List)],
  ),
  searchResult: PropTypes.instanceOf(Immutable.Map),
  readRecord: PropTypes.func,
};

const defaultProps = {
  listType: 'common',
  readRecord: () => Promise.resolve(),
};

export default class MediaViewer extends Component {
  constructor() {
    super();

    this.handleImageGalleryClick = this.handleImageGalleryClick.bind(this);
  }

  handleImageGalleryClick(event) {
    const {
      target,
    } = event;

    const {
      config,
    } = this.props;

    if (target.nodeName === 'IMG') {
      this.getPopupImagePath(target.dataset.csid)
        .then((popupImagePath) => {
          window.open(getImageViewerPath(config, popupImagePath), VIEWER_WINDOW_NAME);
        });
    }
  }

  getPopupImagePath(blobCsid) {
    const {
      config,
      readRecord,
    } = this.props;

    const recordTypeConfig = get(config, ['recordTypes', 'blob']);
    const popupSubresource = get(recordTypeConfig, ['content', 'popup', 'subresource']);

    return readRecord(config, recordTypeConfig, undefined, blobCsid)
      .then((blobData) => getContentPath(config, 'blob', undefined, blobCsid, popupSubresource, blobData));
  }

  createGalleryImage(blobCsid, altText, identificationNumber) {
    const {
      config,
    } = this.props;

    const content = get(config, ['recordTypes', 'blob', 'content']);
    const snapshotSubresource = get(content, ['snapshot', 'subresource']);
    const thumbnailSubresource = get(content, ['thumbnail', 'subresource']);

    return {
      blobCsid,
      // note: react-image-gallery requires item.original to be non-null, so it might be best to
      // move from snapshot to original here to keep similar semantics
      original: getContentPath(config, 'blob', undefined, blobCsid, snapshotSubresource),
      snapshot: getContentPath(config, 'blob', undefined, blobCsid, snapshotSubresource),
      snapshotAlt: altText ?? identificationNumber,
      snapshotTitle: identificationNumber,
      thumbnail: getContentPath(config, 'blob', undefined, blobCsid, thumbnailSubresource),
      thumbnailAlt: altText ?? identificationNumber,
      thumbnailTitle: identificationNumber,
    };
  }

  render() {
    const {
      config,
      isSearchPending,
      listType,
      ownFields,
      priorityOrder,
      searchResult,
    } = this.props;

    const { intl } = this.context;

    const renderLeftNav = makeRenderNav(
      'mediaViewerPrev', '<', intl.formatMessage(messages.previous),
    );

    const renderRightNav = makeRenderNav(
      'mediaViewerNext', '>', intl.formatMessage(messages.next),
    );

    const images = [];

    if (ownFields) {
      const { ownBlobCsid, ownAltText, ownIdentificationNumber } = ownFields;
      images.push(this.createGalleryImage(ownBlobCsid, ownAltText, ownIdentificationNumber));
    }

    if (searchResult) {
      const listTypeConfig = config.listTypes[listType];

      const {
        listNodeName,
        itemNodeName,
      } = listTypeConfig;

      const list = searchResult.get(listNodeName);
      const totalItems = parseInt(list.get('totalItems'), 10);

      if (!Number.isNaN(totalItems)) {
        let items = list.get(itemNodeName);

        if (items) {
          if (!Immutable.List.isList(items)) {
            items = Immutable.List.of(items);
          }

          sortByPriority(items, priorityOrder).forEach((item) => {
            const blobCsid = item.get('blobCsid');
            const altText = item.get('altText');
            const identificationNumber = item.get('identificationNumber');

            if (blobCsid) {
              images.push(this.createGalleryImage(blobCsid, altText, identificationNumber));
            }
          });
        }
      }
    }

    if (images.length > 0) {
      return (
        <div className={styles.normal}>
          <ImageGallery
            items={images}
            disableKeyDown
            lazyLoad
            renderLeftNav={renderLeftNav}
            renderRightNav={renderRightNav}
            showThumbnails={images.length > 1}
            showFullscreenButton={false}
            showPlayButton={false}
            onClick={this.handleImageGalleryClick}
            renderItem={renderItem}
            renderThumbInner={renderThumbInner}
          />
        </div>
      );
    }

    if (isSearchPending) {
      return null;
    }

    return (
      <div className={styles.empty} />
    );
  }
}

MediaViewer.propTypes = propTypes;
MediaViewer.defaultProps = defaultProps;
MediaViewer.contextTypes = {
  intl: intlShape,
};
