import clsx from 'clsx';
import React, { useEffect, useState } from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import { useHistory } from 'react-router-dom';
import ReactSwipeableViews from 'react-swipeable-views';

import ExtendedVideoPlayer from 'soapbox/components/extended-video-player';
import Icon from 'soapbox/components/icon';
import IconButton from 'soapbox/components/icon-button';
import Audio from 'soapbox/features/audio';
import Video from 'soapbox/features/video';
import { useAppDispatch } from 'soapbox/hooks';

import ImageLoader from '../image-loader';

import type { List as ImmutableList } from 'immutable';
import type { Attachment, Status } from 'soapbox/types/entities';

const messages = defineMessages({
  close: { id: 'lightbox.close', defaultMessage: 'Close' },
  previous: { id: 'lightbox.previous', defaultMessage: 'Previous' },
  next: { id: 'lightbox.next', defaultMessage: 'Next' },
});

interface IMediaModal {
  media: ImmutableList<Attachment>
  status?: Status
  index: number
  time?: number
  onClose: () => void
}

const MediaModal: React.FC<IMediaModal> = (props) => {
  const {
    media,
    status,
    onClose,
    time = 0,
  } = props;

  const intl = useIntl();
  const history = useHistory();
  const dispatch = useAppDispatch();

  const [index, setIndex] = useState<number | null>(null);
  const [navigationHidden, setNavigationHidden] = useState(false);

  const handleSwipe = (index: number) => {
    setIndex(index % media.size);
  };

  const handleNextClick = () => {
    setIndex((getIndex() + 1) % media.size);
  };

  const handlePrevClick = () => {
    setIndex((media.size + getIndex() - 1) % media.size);
  };

  const handleChangeIndex: React.MouseEventHandler<HTMLButtonElement> = (e) => {
    const index = Number(e.currentTarget.getAttribute('data-index'));
    setIndex(index % media.size);
  };

  const handleKeyDown = (e: KeyboardEvent) => {
    switch (e.key) {
      case 'ArrowLeft':
        handlePrevClick();
        e.preventDefault();
        e.stopPropagation();
        break;
      case 'ArrowRight':
        handleNextClick();
        e.preventDefault();
        e.stopPropagation();
        break;
    }
  };

  useEffect(() => {
    window.addEventListener('keydown', handleKeyDown, false);

    return () => {
      window.removeEventListener('keydown', handleKeyDown);
    };
  }, [index]);

  const getIndex = () => index !== null ? index : props.index;

  const toggleNavigation = () => {
    setNavigationHidden(!navigationHidden);
  };

  const handleStatusClick: React.MouseEventHandler = e => {
    if (status && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
      e.preventDefault();

      dispatch((_, getState) => {
        const account = typeof status.account === 'string' ? getState().accounts.get(status.account) : status.account;
        if (!account) return;

        history.push(`/@${account.acct}/posts/${status?.id}`);
        onClose();
      });
    }
  };

  const handleCloserClick: React.MouseEventHandler = ({ target }) => {
    const whitelist = ['zoomable-image'];
    const activeSlide = document.querySelector('.media-modal .react-swipeable-view-container > div[aria-hidden="false"]');

    const isClickOutside = target === activeSlide || !activeSlide?.contains(target as Element);
    const isWhitelisted = whitelist.some(w => (target as Element).classList.contains(w));

    if (isClickOutside || isWhitelisted) {
      onClose();
    }
  };

  let pagination: React.ReactNode[] = [];

  const leftNav = media.size > 1 && (
    <button
      tabIndex={0}
      className='media-modal__nav media-modal__nav--left'
      onClick={handlePrevClick}
      aria-label={intl.formatMessage(messages.previous)}
    >
      <Icon src={require('@tabler/icons/arrow-left.svg')} />
    </button>
  );

  const rightNav = media.size > 1 && (
    <button
      tabIndex={0}
      className='media-modal__nav  media-modal__nav--right'
      onClick={handleNextClick}
      aria-label={intl.formatMessage(messages.next)}
    >
      <Icon src={require('@tabler/icons/arrow-right.svg')} />
    </button>
  );

  if (media.size > 1) {
    pagination = media.toArray().map((item, i) => (
      <li className='media-modal__page-dot' key={i}>
        <button
          tabIndex={0}
          className={clsx('media-modal__button', {
            'media-modal__button--active': i === getIndex(),
          })}
          onClick={handleChangeIndex}
          data-index={i}
        >
          {i + 1}
        </button>
      </li>
    ));
  }

  const isMultiMedia = media.map((image) => image.type !== 'image').toArray();

  const content = media.map((attachment, i) => {
    const width  = (attachment.meta.getIn(['original', 'width']) || undefined) as number | undefined;
    const height = (attachment.meta.getIn(['original', 'height']) || undefined) as number | undefined;

    const link = (status && (
      <a href={status.url} onClick={handleStatusClick}>
        <FormattedMessage id='lightbox.view_context' defaultMessage='View context' />
      </a>
    ));

    if (attachment.type === 'image') {
      return (
        <ImageLoader
          previewSrc={attachment.preview_url}
          src={attachment.url}
          width={width}
          height={height}
          alt={attachment.description}
          key={attachment.url}
          onClick={toggleNavigation}
        />
      );
    } else if (attachment.type === 'video') {
      return (
        <Video
          preview={attachment.preview_url}
          blurhash={attachment.blurhash}
          src={attachment.url}
          width={width}
          height={height}
          startTime={time}
          detailed
          autoFocus={i === getIndex()}
          link={link}
          alt={attachment.description}
          key={attachment.url}
          visible
        />
      );
    } else if (attachment.type === 'audio') {
      return (
        <Audio
          src={attachment.url}
          alt={attachment.description}
          poster={attachment.preview_url !== attachment.url ? attachment.preview_url : (status?.getIn(['account', 'avatar_static'])) as string | undefined}
          backgroundColor={attachment.meta.getIn(['colors', 'background']) as string | undefined}
          foregroundColor={attachment.meta.getIn(['colors', 'foreground']) as string | undefined}
          accentColor={attachment.meta.getIn(['colors', 'accent']) as string | undefined}
          duration={attachment.meta.getIn(['original', 'duration'], 0) as number | undefined}
          key={attachment.url}
        />
      );
    } else if (attachment.type === 'gifv') {
      return (
        <ExtendedVideoPlayer
          src={attachment.url}
          muted
          controls={false}
          width={width}
          height={height}
          key={attachment.preview_url}
          alt={attachment.description}
          onClick={toggleNavigation}
        />
      );
    }

    return null;
  }).toArray();

  // you can't use 100vh, because the viewport height is taller
  // than the visible part of the document in some mobile
  // browsers when it's address bar is visible.
  // https://developers.google.com/web/updates/2016/12/url-bar-resizing
  const swipeableViewsStyle: React.CSSProperties = {
    width: '100%',
    height: '100%',
  };

  const containerStyle: React.CSSProperties = {
    alignItems: 'center', // center vertically
  };

  const navigationClassName = clsx('media-modal__navigation', {
    'media-modal__navigation--hidden': navigationHidden,
  });

  return (
    <div className='modal-root__modal media-modal'>
      <div
        className='media-modal__closer'
        role='presentation'
        onClick={handleCloserClick}
      >
        <ReactSwipeableViews
          style={swipeableViewsStyle}
          containerStyle={containerStyle}
          onChangeIndex={handleSwipe}
          index={getIndex()}
        >
          {content}
        </ReactSwipeableViews>
      </div>

      <div className={navigationClassName}>
        <IconButton
          className='media-modal__close'
          title={intl.formatMessage(messages.close)}
          src={require('@tabler/icons/x.svg')}
          onClick={onClose}
        />

        {leftNav}
        {rightNav}

        {(status && !isMultiMedia[getIndex()]) && (
          <div className={clsx('media-modal__meta', { 'media-modal__meta--shifted': media.size > 1 })}>
            <a href={status.url} onClick={handleStatusClick}>
              <FormattedMessage id='lightbox.view_context' defaultMessage='View context' />
            </a>
          </div>
        )}

        <ul className='media-modal__pagination'>
          {pagination}
        </ul>
      </div>
    </div>
  );
};

export default MediaModal;