import React from 'react';
import PropTypes from 'prop-types';
import {
  Thread,
  Message,
  localized,
  Utils,
  Actions,
  AttachmentStore,
  MessageStore,
} from 'mailspring-exports';
import { RetinaImg, InjectedComponentSet, InjectedComponent } from 'mailspring-component-kit';

import MessageParticipants from './message-participants';
import MessageItemBody from './message-item-body';
import MessageTimestamp from './message-timestamp';
import MessageControls from './message-controls';

interface MessageItemProps {
  thread: Thread;
  message: Message;
  messages: Message[];
  collapsed: boolean;
  pending: boolean;
  isMostRecent: boolean;
  className: string;
}

interface MessageItemState {
  filePreviewPaths: { [fileId: string]: string };
  downloads: {
    [fileId: string]: null;
  };
  detailedHeaders: boolean;
}

export default class MessageItem extends React.Component<MessageItemProps, MessageItemState> {
  static displayName = 'MessageItem';

  static propTypes = {
    thread: PropTypes.object.isRequired,
    message: PropTypes.object.isRequired,
    messages: PropTypes.array.isRequired,
    collapsed: PropTypes.bool,
    pending: PropTypes.bool,
    isMostRecent: PropTypes.bool,
    className: PropTypes.string,
  };

  _headerEl: HTMLElement;

  constructor(props, context) {
    super(props, context);

    const fileIds = this.props.message.fileIds();
    this.state = {
      // Holds the downloadData (if any) for all of our files. It's a hash
      // keyed by a fileId. The value is the downloadData.
      downloads: AttachmentStore.getDownloadDataForFiles(fileIds),
      filePreviewPaths: AttachmentStore.previewPathsForFiles(fileIds),
      detailedHeaders: AppEnv.config.get('core.reading.detailedHeaders'),
    };
  }

  private _storeUnlisten?: () => void;

  componentDidMount() {
    this._storeUnlisten = AttachmentStore.listen(this._onDownloadStoreChange);
  }

  shouldComponentUpdate(nextProps, nextState) {
    return !Utils.isEqualReact(nextProps, this.props) || !Utils.isEqualReact(nextState, this.state);
  }

  componentWillUnmount() {
    if (this._storeUnlisten) {
      this._storeUnlisten();
    }
  }

  _onClickParticipants = (e: React.MouseEvent<any>) => {
    let el = e.target as HTMLElement;
    while (el !== e.currentTarget) {
      if (el.classList.contains('collapsed-participants')) {
        this.setState({ detailedHeaders: true });
        e.stopPropagation();
        return;
      }
      el = el.parentElement;
    }
    return;
  };

  _onClickHeader = e => {
    this._onToggleCollapsed();
  };

  _onDownloadAll = () => {
    Actions.fetchAndSaveAllFiles(this.props.message.files);
  };

  _onToggleCollapsed = () => {
    if (this.props.isMostRecent) {
      return;
    }
    Actions.toggleMessageIdExpanded(this.props.message.id);
  };

  _onDownloadStoreChange = () => {
    const fileIds = this.props.message.fileIds();
    this.setState({
      downloads: AttachmentStore.getDownloadDataForFiles(fileIds),
      filePreviewPaths: AttachmentStore.previewPathsForFiles(fileIds),
    });
  };

  _renderDownloadAllButton() {
    return (
      <div className="download-all">
        <div className="attachment-number">
          <RetinaImg name="ic-attachments-all-clippy.png" mode={RetinaImg.Mode.ContentIsMask} />
          <span>{`${this.props.message.files.length} ${localized('attachments')}`}</span>
        </div>
        <div className="separator">-</div>
        <div className="download-all-action" onClick={this._onDownloadAll}>
          <RetinaImg name="ic-attachments-download-all.png" mode={RetinaImg.Mode.ContentIsMask} />
          <span>{localized('Download All')}</span>
        </div>
      </div>
    );
  }

  _renderAttachments() {
    const { files = [], body, id } = this.props.message;
    const { filePreviewPaths, downloads } = this.state;
    let attachedFiles = files.filter(
      f => !f.contentId || !(body || '').includes(`cid:${f.contentId}`)
    );

    for (const extension of MessageStore.extensions()) {
      if (!extension.filterMessageFiles) continue;
      attachedFiles = extension.filterMessageFiles({
        message: this.props.message,
        files: attachedFiles,
      });
    }

    return (
      <div>
        {files.length > 1 ? this._renderDownloadAllButton() : null}
        {attachedFiles.length > 0 && (
          <div className="attachments-area">
            <InjectedComponent
              matching={{ role: 'MessageAttachments' }}
              exposedProps={{
                files: attachedFiles,
                messageId: id,
                downloads,
                filePreviewPaths,
                canRemoveAttachments: false,
              }}
            />
          </div>
        )}
      </div>
    );
  }

  _renderFooterStatus() {
    return (
      <InjectedComponentSet
        className="message-footer-status"
        matching={{ role: 'MessageFooterStatus' }}
        exposedProps={{
          message: this.props.message,
          thread: this.props.thread,
          detailedHeaders: this.state.detailedHeaders,
        }}
      />
    );
  }

  _renderHeader() {
    const { message, thread, messages, pending } = this.props;

    return (
      <header
        ref={el => (this._headerEl = el)}
        className={`message-header ${pending && 'pending'}`}
        onClick={this._onClickHeader}
      >
        <InjectedComponent
          matching={{ role: 'MessageHeader' }}
          exposedProps={{ message: message, thread: thread, messages: messages }}
        />
        <div className="pending-spinner" style={{ position: 'absolute', marginTop: -2 }}>
          <RetinaImg width={18} name="sending-spinner.gif" mode={RetinaImg.Mode.ContentPreserve} />
        </div>
        <div className="message-header-right">
          <MessageTimestamp
            className="message-time"
            isDetailed={this.state.detailedHeaders}
            date={message.date}
          />
          <InjectedComponentSet
            className="message-header-status"
            matching={{ role: 'MessageHeaderStatus' }}
            exposedProps={{
              message: message,
              thread: thread,
              detailedHeaders: this.state.detailedHeaders,
            }}
          />
          <MessageControls thread={thread} message={message} />
        </div>
        <MessageParticipants
          from={message.from}
          onClick={this._onClickParticipants}
          isDetailed={this.state.detailedHeaders}
        />
        <MessageParticipants
          to={message.to}
          cc={message.cc}
          bcc={message.bcc}
          replyTo={message.replyTo.filter(c => !message.from.find(fc => fc.email === c.email))}
          onClick={this._onClickParticipants}
          isDetailed={this.state.detailedHeaders}
        />
        {this._renderSubject()}
        {this._renderFolder()}
        {this._renderHeaderDetailToggle()}
      </header>
    );
  }

  _renderHeaderDetailToggle() {
    if (this.props.pending) {
      return null;
    }
    if (this.state.detailedHeaders) {
      return (
        <div
          className="header-toggle-control"
          style={{ top: 18, left: -14 }}
          onClick={e => {
            this.setState({ detailedHeaders: false });
            e.stopPropagation();
          }}
        >
          <RetinaImg
            name={'message-disclosure-triangle-active.png'}
            mode={RetinaImg.Mode.ContentIsMask}
          />
        </div>
      );
    }

    return (
      <div
        className="header-toggle-control inactive"
        style={{ top: 18 }}
        onClick={e => {
          this.setState({ detailedHeaders: true });
          e.stopPropagation();
        }}
      >
        <RetinaImg name={'message-disclosure-triangle.png'} mode={RetinaImg.Mode.ContentIsMask} />
      </div>
    );
  }

  _renderSubject() {
    if (!this.state.detailedHeaders) {
      return false;
    }

    const subject = this.props.message.subject;

    return (
      <div className="header-row">
        <div className="header-label">{localized('Subject')}:&nbsp;</div>
        <div className="header-name">{subject}</div>
      </div>
    );
  }

  _renderFolder() {
    if (!this.state.detailedHeaders) {
      return false;
    }

    const folder = this.props.message.folder;
    if (!folder || folder.role === 'al') {
      return false;
    }

    return (
      <div className="header-row">
        <div className="header-label">{localized('Folder')}:&nbsp;</div>
        <div className="header-name">{folder.displayName}</div>
      </div>
    );
  }

  _renderCollapsed() {
    const {
      message: { snippet, from, files, date, draft },
      className,
    } = this.props;

    const attachmentIcon = Utils.showIconForAttachments(files) ? (
      <div className="collapsed-attachment" />
    ) : null;

    return (
      <div className={className} onClick={this._onToggleCollapsed}>
        <div className="message-item-white-wrap">
          <div className="message-item-area">
            <div className="collapsed-from">
              {from &&
                from[0] &&
                from[0].displayName({ compact: !AppEnv.config.get('core.reading.detailedNames') })}
            </div>
            <div className="collapsed-snippet">{snippet}</div>
            {draft && (
              <div className="Collapsed-draft">
                <RetinaImg
                  name="icon-draft-pencil.png"
                  className="draft-icon"
                  mode={RetinaImg.Mode.ContentPreserve}
                />
              </div>
            )}
            <div className="collapsed-timestamp">
              <MessageTimestamp date={date} />
            </div>
            {attachmentIcon}
          </div>
        </div>
      </div>
    );
  }

  _renderFull() {
    return (
      <div className={this.props.className}>
        <div className="message-item-white-wrap">
          <div className="message-item-area">
            {this._renderHeader()}
            <MessageItemBody message={this.props.message} downloads={this.state.downloads} />
            {this._renderAttachments()}
            {this._renderFooterStatus()}
          </div>
        </div>
      </div>
    );
  }

  render() {
    return this.props.collapsed ? this._renderCollapsed() : this._renderFull();
  }
}
