import { cloneDeep } from 'lodash-es';
import moment from 'moment';
import PropTypes from 'prop-types';
import React from 'react';
import {
  Button,
  Glyphicon,
  Label,
  OverlayTrigger,
  Table,
  Tooltip,
} from 'react-bootstrap';
import LazyLoad from 'react-lazyload';
import {
  ANNOTATION,
  EVENT_CATEGORY_COMMANDLINE,
  EVENT_CATEGORY_COMMENT,
  EVENT_CATEGORY_DEBUG,
  EVENT_CATEGORY_ERROR,
  EVENT_CATEGORY_INFO,
  NOTIFICATION,
} from '../../../constants/eventTypes';
import {
  convertImagesToThumbnails,
  getContent,
  getOriginalEvent,
  getPreviousVersionNumber,
} from '../../../helpers/eventHelpers';
import LogbookPager from '../LogbookPager';
import TagListInLine from '../Tag/TagListInLine';
import EventIcon from './EventIcon';
import { makeFullScreen } from '../../../helpers';
import styles from './EventList.module.css';

// Expose on window to use as `onClick` handler on images
window.makeFullScreen = makeFullScreen;

/**
 * The list of the all events
 */
class EventList extends React.Component {
  collapse(items) {
    const collapsed = [];
    for (let i = 0; i < items.length; i++) {
      const event = items[i];
      if (event.category) {
        if (
          event.category.toLowerCase() === EVENT_CATEGORY_COMMANDLINE &&
          !event.previousVersionEvent
        ) {
          const lastEvent = collapsed[collapsed.length - 1];
          if (lastEvent && lastEvent.category) {
            if (
              lastEvent.category.toLowerCase() === EVENT_CATEGORY_COMMANDLINE
            ) {
              if (!lastEvent.events) {
                lastEvent.events = [event];
              } else {
                lastEvent.events.push(event);
              }
              continue;
            }
          } else {
            lastEvent.events = [event];
          }
        }
      }
      collapsed.push(event);
    }
    return collapsed;
  }

  /** Returns the list of items to be displayed in the table: events + days */
  getItems() {
    const eventsCopy = cloneDeep(this.props.events);
    const items = [];
    let lastDate = null; // format DDMMYYYY
    for (let i = 0; i < eventsCopy.length; i++) {
      const date = moment(getOriginalEvent(eventsCopy[i]).creationDate).format(
        'MMMM Do YYYY'
      );
      if (date !== lastDate) {
        lastDate = date;
        items.push({ text: date, type: 'date', anchor: date });
      }

      items.push(eventsCopy[i]);
    }
    /** Add shadow to events that are before a date */
    for (let i = 0; i < items.length; i++) {
      if (items[i].type === 'date') {
        if (items[i - 1]) {
          items[i - 1].shadowBottomBorder = true;
        }
      }
    }
    /** Add shadow to the last one */
    if (items.length > 0) {
      items[items.length - 1].shadowBottomBorder = true;
    }
    return this.collapse(items);
  }

  render() {
    if (!this.props.events || this.props.events.length === 0) {
      return null;
    }

    return (
      <div>
        <Table responsive style={{ fontSize: 12, borderCollapse: 'collapse' }}>
          <tbody>
            {this.getItems().map((event, index) => {
              if (event.type === 'date') {
                return (
                  <tr
                    key={index}
                    style={{
                      backgroundColor: '#f0f0f6',
                      color: 'gray',
                      height: 50,
                    }}
                  >
                    <td
                      id={event.anchor}
                      style={{
                        textAlign: 'center',
                        fontSize: 18,
                        fontWeight: 'bold',
                      }}
                      colSpan={6}
                    >
                      {event.text}
                      <br />
                    </td>
                  </tr>
                );
              }

              return (
                <Event
                  key={index}
                  event={event}
                  logbookContext={this.props.logbookContext}
                  onEventClicked={this.props.onEventClicked}
                />
              );
            })}
          </tbody>
        </Table>

        <div style={{ position: 'relative', height: 45 }}>
          <div
            style={{
              position: 'absolute',
              left: '50%',
              marginLeft: '-125px',
              width: 250,
            }}
          >
            <LogbookPager
              activePage={this.props.activePage}
              eventCount={this.props.eventCountBySelectionFilter}
              onPageClicked={this.props.onPageClicked}
            />
          </div>
        </div>
      </div>
    );
  }
}

EventList.propTypes = {
  /** the array of unsorted events as provided by the ICAT+ server */
  events: PropTypes.array.isRequired,
  /** the logbook context */
  logbookContext: PropTypes.object,
  /** Callback function triggered when the user clicks the edit icon */
  onEventCliked: PropTypes.func,
};

export default EventList;

/** React component which renders an event. Here 'event can be the classical event as found in the logbook but
 * could also be a list of event corresponding to a collapsed line containing several events
 */
export class Event extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      collapsed: true,
    };
    this.handleClick = this.handleClick.bind(this);
  }

  render() {
    let events = [this.props.event];

    if (this.props.event.events && !this.state.collapsed) {
      events = events.concat(this.props.event.events);
    }

    const getButtonIcon = (event) => {
      if (this.props.logbookContext.isReleased) {
        return <Glyphicon glyph="eye-open" />;
      }
      if (getPreviousVersionNumber(event) === 0) {
        return <Glyphicon glyph="pencil" style={{ width: 10 }} />;
      }
      return <Glyphicon glyph="pencil" style={{ width: 10 }} />;
    };
    return events.map((event, index) => (
      <tr key={index} style={{ backgroundColor: '#f0f0f6' }}>
        <td style={{ width: 16, border: 0 }}>
          <EventIcon event={event} />
        </td>
        <td
          className={styles.borderTopSeparatorBetweenEvents}
          style={{ width: 16 }}
        >
          <OverlayTrigger
            placement="right"
            overlay={
              <Tooltip id="tooltip">
                {' '}
                <p>
                  {' '}
                  Events created on{' '}
                  {moment(getOriginalEvent(event).creationDate).format(
                    'MMMM Do YYYY, h:mm:ss a'
                  )}{' '}
                </p>{' '}
              </Tooltip>
            }
          >
            <span style={{ cursor: 'pointer', color: 'black', margin: 0 }}>
              {moment(getOriginalEvent(event).creationDate).format(
                moment.HTML5_FMT.TIME_SECONDS
              )}{' '}
            </span>
          </OverlayTrigger>
        </td>
        <td
          className={styles.borderTopSeparatorBetweenEvents}
          style={{ width: 30 }}
        >
          <Button
            bsStyle="default"
            bsSize="small"
            style={{ width: 30, position: 'static', padding: 0 }}
            onClick={() => this.props.onEventClicked(event)}
          >
            {getButtonIcon(event)}
          </Button>
        </td>
        <td
          className={styles.borderTopSeparatorBetweenEvents}
          style={{ width: 16 }}
        />
        <td
          className={styles.borderTopSeparatorBetweenEventsInline}
          style={{
            paddingBottom: 0,
            backgroundColor: 'white',
            boxShadow:
              '6px 0 5px -5px rgba(100, 100, 100, 0.8), -6px 0 5px -5px rgba(100, 100, 100, 0.8)',
          }}
        >
          <div style={{ marginLeft: 20, backgroundColor: 'white' }}>
            <EventTextBox event={event} />

            {event.events && this.state.collapsed && (
              <Label
                style={{
                  color: 'blue',
                  backgroundColor: '#f8f8f8',
                  cursor: 'pointer',
                }}
                onClick={this.handleClick}
              >
                .... {event.events.length} command lines more
              </Label>
            )}
          </div>
        </td>
        <td
          className={styles.borderTopSeparatorBetweenEvents}
          style={{ width: 200 }}
        >
          <TagListInLine tags={event.tag} />
        </td>
      </tr>
    ));
  }

  handleClick() {
    this.setState({ collapsed: !this.state.collapsed });
  }

  getUncollapsedEvents() {
    return (
      <tbody>
        {' '}
        {this.props.event.events.map((event) =>
          this.getEventContentBody(event)
        )}
      </tbody>
    );
  }
}

Event.protypes = {
  /** A classical event or a structure representing a collapsed line containing several similar events */
  event: PropTypes.object,
  /** Context in which the logbook is run */
  logbookContext: PropTypes.object,
  /** Callback function triggered which the user clicks a link to edit/consult the detailed event */
  onEventClicked: PropTypes.func,
};

/** React component which renders the box containing the text of the event. For annotations, the box renders a single text. For notifications, the box could render 2 texts: the original text and the last comment if it exists */
class EventTextBox extends React.Component {
  render() {
    function getText(event) {
      return getContent(event.content, 'html')
        ? convertImagesToThumbnails(getContent(event.content, 'html'))
        : convertImagesToThumbnails(getContent(event.content, 'plainText'));
    }

    let text = getText(this.props.event);

    if (!text) {
      return (
        <div style={{ fontStyle: 'italic', color: '#888' }}>
          {' '}
          There is no content{' '}
        </div>
      );
    }

    text = text.replace(/<img/g, "<img onClick='makeFullScreen(this)'  ");

    if (this.props.event.type === ANNOTATION) {
      return (
        <div
          className={this.getTextColorClassNameByCategory(
            EVENT_CATEGORY_COMMENT
          )}
        >
          <LazyLoadedText text={text} />
        </div>
      );
    }

    if (this.props.event.type === NOTIFICATION) {
      if (this.props.event.previousVersionEvent) {
        return (
          <>
            <div
              className={this.getTextColorClassNameByCategory(
                this.props.event.category
              )}
            >
              {this.props.event.category.toLowerCase() ===
              EVENT_CATEGORY_COMMANDLINE ? (
                <pre>
                  <LazyLoadedText
                    text={
                      getOriginalEvent(this.props.event)
                        ? getContent(
                            getOriginalEvent(this.props.event).content,
                            'plainText'
                          )
                        : null
                    }
                  />
                </pre>
              ) : (
                <pre className="whitePre">
                  <LazyLoadedText
                    text={
                      getOriginalEvent(this.props.event)
                        ? getContent(
                            getOriginalEvent(this.props.event).content,
                            'plainText'
                          )
                        : null
                    }
                  />
                </pre>
              )}
            </div>
            <div
              className={this.getTextColorClassNameByCategory(
                EVENT_CATEGORY_COMMENT
              )}
            >
              <LazyLoadedText text={text} />
            </div>
          </>
        );
      }

      // get plainText content of the no-version event
      text = this.props.event
        ? getContent(this.props.event.content, 'plainText')
        : null;
      return (
        <div
          className={this.getTextColorClassNameByCategory(
            this.props.event.category
          )}
        >
          {this.props.event.category.toLowerCase() ===
          EVENT_CATEGORY_COMMANDLINE ? (
            <pre>
              <LazyLoadedText text={text} />
            </pre>
          ) : (
            <pre className="whitePre">
              <LazyLoadedText text={text} />
            </pre>
          )}
        </div>
      );
    }
  }

  /**
   * get bootstrap css class defining font color by event category
   * @param {string} category an event category
   * @returns {string} bootstrap css class name
   */
  getTextColorClassNameByCategory(category) {
    if (category.toLowerCase() === EVENT_CATEGORY_COMMENT) {
      return 'text-primary';
    }

    if (category.toLowerCase() === EVENT_CATEGORY_ERROR) {
      return 'text-danger';
    }

    if (category.toLowerCase() === EVENT_CATEGORY_INFO) {
      return '';
    }

    if (category.toLowerCase() === EVENT_CATEGORY_DEBUG) {
      return 'text-muted';
    }
    return 'text-primary';
  }
}

EventTextBox.propTypes = {
  /** Event from which to extract the texts */
  event: PropTypes.object,
};

/** React component which renders a lazyloaded text when necessary. At the moment it is lazyloaded when the text has at least an `img` tag. When there are no images, the text is rendered as such. */
class LazyLoadedText extends React.Component {
  render() {
    if (this.props.text) {
      if (this.props.text.indexOf('img') !== -1) {
        return (
          <LazyLoad once>
            <div dangerouslySetInnerHTML={{ __html: this.props.text }} />
          </LazyLoad>
        );
      }
      return <div dangerouslySetInnerHTML={{ __html: this.props.text }} />;
    }
    return <div />;
  }
}

LazyLoadedText.propTypes = {
  /** Text to be rendered. It can be a strig containing HTML code or simply a text */
  text: PropTypes.string,
};
