import React from 'react';
import PropTypes from 'prop-types';
import { equals } from 'ramda';
import { I18n as I18nDecorator } from '@procore/core-react';
import moment from 'moment';
import cx from 'classnames';

import FilterAvailable from './FilterAvailable';
import FilterCategoryCheckbox from './FilterCategoryCheckbox';
import FilterCheckbox from './FilterCheckbox';
import FilterLocation from './FilterLocation';
import FilterCalendar from './FilterCalendar/';
import FilterCalendarWithPreMenu from './FilterCalendarWithPreMenu/';

import baseStyles from './Filters.scss';
import composeStyles from '../../shared/stylesheetComposer';

const DEFAULT_COMPONENTS = {
  calendar: ({ calendar }) => (<FilterCalendar {...calendar} />),
  calendarWithPreMenu: ({ calendarWithPreMenu }) => (<FilterCalendarWithPreMenu {...calendarWithPreMenu} />),
  location: ({ location }) => (<FilterLocation {...location} />),
  categoryCheckbox: ({ categoryCheckbox }) => (<FilterCategoryCheckbox {...categoryCheckbox} />),
  checkbox: ({ checkbox }) => (<FilterCheckbox {...checkbox} />)
};

function getSavedActiveAndInactive({ availableFilters, savedFilters, focusedDropdown }) {
  return availableFilters.reduce((acc, filter, index) => {
    (
      (savedFilters[filter.key] !== undefined && savedFilters[filter.key] !== null) ||
      (focusedDropdown && filter.endpoint === focusedDropdown.endpoint)
    )
      ? acc[0].push(Object.assign(filter, { index }))
      : acc[1].push(Object.assign(filter, { index }));

    return acc;
  }, [[], []]);
}

function getSavedSelections({ savedFilters }) {
  const selectedKeys = Object.assign({}, savedFilters);

  Object.keys(selectedKeys).forEach((k) => {
    if (selectedKeys[k] === null) {
      delete selectedKeys[k];
    }
  });

  return selectedKeys;
}

function getCalendarIsRange(dates) {
  const isRange = dates && dates.length === 2;

  return isRange;
}

function stringify(selectedKeys) {
  const stringifySelectedKey = (acc, v) => (selectedKeys[v] ? acc.concat(`${v}${selectedKeys[v].sort().join()}`) : acc);

  return Object.keys(selectedKeys).sort()
    .reduce(stringifySelectedKey, [])
    .join();
}

function isValidDate(filter) {
  const commitedStartDate = (filter && filter[0] && filter[0] !== '') ? filter[0] : null;
  const commitedEndDate = (filter && filter[1] && filter[1] !== '') ? filter[1] : null;
  const isRange = getCalendarIsRange(filter);

  if (
    (isRange && commitedStartDate && commitedEndDate) ||
    (!isRange && commitedStartDate && !commitedEndDate)
  ) {
    return true;
  }

  return false;
}

export const FILTERS_ANALYTICS = {
  ADD: 'add',
  CLEAR_ALL: 'clear-all',
  NAME: 'Filters',
  REMOVE_ALL: 'remove-all',
  REMOVE_ONE: 'remove-one',
  SELECT_ALL: 'select-all'
};

const CALENDAR_IS_RANGE_DEFAULT = true;

export class Filters extends React.Component {
  static propTypes = {
    availableFilters: PropTypes.arrayOf(PropTypes.shape({
      endpoint: PropTypes.string,
      key: PropTypes.string,
      value: PropTypes.string
    })).isRequired,
    disabled: PropTypes.bool,
    filterCheckboxRowHeight: PropTypes.number,
    focusedDropdown: PropTypes.shape(),
    focusedDropdownFilters: PropTypes.arrayOf(PropTypes.shape()),
    I18n: PropTypes.shape().isRequired,
    isIncludeSublocations: PropTypes.bool,
    onAnalytics: PropTypes.func,
    onFetch: PropTypes.func.isRequired,
    onRemoveFilter: PropTypes.func,
    onSave: PropTypes.func.isRequired,
    savedFilters: PropTypes.shape().isRequired,
    scope: PropTypes.string,
    stylesheets: PropTypes.arrayOf(PropTypes.shape())
  }

  static defaultProps = {
    disabled: false,
    filterCheckboxRowHeight: 24,
    filterOptions: item => item,
    focusedDropdown: {},
    focusedDropdownFilters: [],
    isIncludeSublocations: false,
    onAnalytics: () => { },
    onRemoveFilter: () => { },
    scope: FILTERS_ANALYTICS.NAME,
    stylesheets: []
  }

  constructor(props) {
    super(props);

    this.styles = composeStyles(baseStyles, [...props.stylesheets]);

    const {
      availableFilters,
      focusedDropdown,
      savedFilters,
      options = {},
      isIncludeSublocations
    } = this.props;

    const [activeFilters, inactiveFilters] =
      getSavedActiveAndInactive({ availableFilters, focusedDropdown, savedFilters });
    const selectedKeys = getSavedSelections({ savedFilters });
    const previousCommittedDatesSelectedKeys = stringify(selectedKeys);

    this.state = {
      activeFilters,
      hiding: {},
      inactiveFilters,
      options: { ...options, isIncludeSublocations },
      selectedKeys,
      previousCommittedDatesSelectedKeys
    };
  }

  componentWillReceiveProps(nextProps) {
    const {
      availableFilters,
      focusedDropdown,
      savedFilters,
      isIncludeSublocations,
      options
    } = nextProps;

    const [activeFilters, inactiveFilters] =
      getSavedActiveAndInactive({ availableFilters, focusedDropdown, savedFilters });
    const selectedKeys = getSavedSelections({ savedFilters });
    const previousCommittedDatesSelectedKeys = stringify(selectedKeys);

    this.setState({
      activeFilters,
      inactiveFilters,
      selectedKeys,
      options: { ...options, isIncludeSublocations },
      previousCommittedDatesSelectedKeys
    });
  }

  onAnalytics = (event) => {
    this.props.onAnalytics({
      event,
      particle: FILTERS_ANALYTICS.NAME,
      scope: this.props.scope
    });
  }

  onSave = (filter, selected, newOptions = {}) => {
    const { selectedKeys, options: prevOptions } = this.state;
    const prev = stringify(selectedKeys);

    selectedKeys[filter.key] = selected;

    if (selected.length === 0) {
      this.removeFilter(filter.key);
      delete selectedKeys[filter.key];
    }

    const mergedOptions = {
      ...prevOptions,
      ...newOptions
    };

    this.setState({
      selectedKeys,
      options: mergedOptions
    });

    const curr = stringify(selectedKeys);

    if (prev !== curr || !equals(prevOptions, mergedOptions)) {
      this.props.onSave(selectedKeys, mergedOptions);
    }
  }

  onAvailableSelect = (key) => {
    const { activeFilters, inactiveFilters } = this.state;

    const index = inactiveFilters.findIndex(filter => filter.key === key);
    const item = inactiveFilters[index];

    inactiveFilters.splice(index, 1);
    activeFilters.push(item);

    this.setState({ activeFilters, inactiveFilters });
    this.onAnalytics(FILTERS_ANALYTICS.ADD);
  }

  onRemove = (evt) => {
    const key = evt.target.dataset.key;
    evt.stopPropagation();
    this.removeFilter(key);
    this.onAnalytics(FILTERS_ANALYTICS.REMOVE_ONE);
  }

  onRemoveAll = () => {
    const { activeFilters, inactiveFilters } = this.state;
    const inactive = inactiveFilters.concat(activeFilters)
      .sort((a, b) => (a.index - b.index));

    this.setState({
      inactiveFilters: inactive,
      activeFilters: [],
      selectedKeys: {},
      previousCommittedDatesSelectedKeys: '',
      options: {}
    });
    this.props.onSave({}, {});
    this.onAnalytics(FILTERS_ANALYTICS.REMOVE_ALL);
  }

  removeFilter(key) {
    const {
      activeFilters,
      inactiveFilters,
      selectedKeys,
      options
    } = this.state;

    const previousKeys = selectedKeys[key] || [];

    const index = activeFilters.findIndex(filter => filter.key === key);
    const item = activeFilters[index];

    activeFilters.splice(index, 1);
    inactiveFilters.push(item);
    inactiveFilters.sort((a, b) => (a.index - b.index));

    delete selectedKeys[key];

    const previousCommittedDatesSelectedKeys = stringify(selectedKeys);

    this.setState({
      inactiveFilters,
      activeFilters,
      selectedKeys,
      previousCommittedDatesSelectedKeys
    });

    if ((previousKeys && isValidDate(previousKeys)) || (previousKeys && previousKeys.length > 0)) {
      this.props.onSave(selectedKeys, options);
    }

    this.props.onRemoveFilter(key);
  }

  render() {
    const inactiveFilters = this.state.inactiveFilters.length
      ? (
        <FilterAvailable
          data={this.state.inactiveFilters}
          disabled={this.props.disabled}
          onChange={this.onAvailableSelect}
          styles={this.styles}
        />
      )
      : null;

    const activeFilters = this.state.activeFilters.map((item) => {
      const committedDates = this.state.selectedKeys[item.key] || [];
      const isRange = this.state.selectedKeys[item.key] ? getCalendarIsRange(committedDates) : CALENDAR_IS_RANGE_DEFAULT;

      const calendarInterface = {
        committedDates,
        filter: item,
        isRange,
        onSave: this.onSave,
        onRemove: this.onRemove,
        removeFilter: this.removeFilter.bind(this),
        stylesheets: [...this.props.stylesheets],
        disabled: this.props.disabled
      };

      const preMenuInterfaceSelected = this.state.selectedKeys[item.key] || [];
      let preMenuInterfaceIsRange = CALENDAR_IS_RANGE_DEFAULT;

      if (preMenuInterfaceSelected[0] && item.type && item.type === 'calendarWithPreMenu') {
        const preMenuInterfaceFirstDate = moment(preMenuInterfaceSelected[0], 'YYYY-MM-DD HH:mm:ss Z');
        const preMenuInterfaceHasValidDate = preMenuInterfaceFirstDate._isValid;

        if (preMenuInterfaceHasValidDate) {
          preMenuInterfaceIsRange = getCalendarIsRange(preMenuInterfaceSelected);
        }
      }

      const calendarWithPreMenuInterface = {
        selected: preMenuInterfaceSelected,
        filter: item,
        isRange: preMenuInterfaceIsRange,
        onSave: this.onSave,
        onRemove: this.onRemove,
        removeFilter: this.removeFilter.bind(this),
        stylesheets: [...this.props.stylesheets],
        disabled: this.props.disabled
      };

      const checkboxInterface = {
        filter: item,
        focusedDropdown: this.props.focusedDropdown,
        focusedDropdownFilters: this.props.focusedDropdownFilters,
        onAnalytics: this.onAnalytics,
        onFetch: this.props.onFetch,
        onRemove: this.onRemove,
        onSave: this.onSave,
        selected: this.state.selectedKeys[item.key],
        styles: this.styles,
        disabled: this.props.disabled,
      };

      const Component = item.Component || DEFAULT_COMPONENTS[item.type || 'checkbox'];

      const props = {
        categoryCheckbox: {
          ...checkboxInterface,
          filterCheckboxRowHeight: this.props.filterCheckboxRowHeight
        },
        checkbox: {
          ...checkboxInterface,
          filterCheckboxRowHeight: this.props.filterCheckboxRowHeight
        },
        location: {
          ...checkboxInterface,
          filterCheckboxRowHeight: this.props.filterCheckboxRowHeight,
          isIncludeSublocations: this.props.isIncludeSublocations
        },
        calendar: calendarInterface,
        calendarWithPreMenu: calendarWithPreMenuInterface,
        options: this.state.options
      };

      return (
        <Component key={`active-${item.key}`} {...props} />
      );
    });

    const clearAllBtnStyles = cx(this.styles.clearAllFilters,
      { [this.styles.disabled]: this.props.disabled });

    const clearAll = activeFilters.length ? (
      <button
        data-qa="clearAllFilters"
        disabled={this.props.disabled}
        className={clearAllBtnStyles}
        onClick={this.onRemoveAll}
      >
        {this.props.I18n.t('views.global.clear_all')}
      </button>
    ) : null;

    return (
      <div className={this.styles.container}>
        {inactiveFilters}
        {activeFilters}
        {clearAll}
      </div>
    );
  }
}

Filters.FilterCheckbox = FilterCheckbox;
Filters.FilterCalendar = FilterCalendar;
Filters.FilterCalendarWithPreMenu = FilterCalendarWithPreMenu;
Filters.FilterLocation = FilterLocation;

export default I18nDecorator.withConsumer(Filters);
