import "core-js/modules/es.array.iterator";
import "core-js/modules/web.dom-collections.iterator";
import _findInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/find";
import _keysInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/keys";
import _Map from "@babel/runtime-corejs3/core-js-stable/map";
import _valuesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/values";
import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/includes";
import _everyInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/every";
import _Array$isArray from "@babel/runtime-corejs3/core-js-stable/array/is-array";
import _sliceInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/slice";
import _Object$keys from "@babel/runtime-corejs3/core-js-stable/object/keys";
import _filterInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/filter";
import _forEachInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/for-each";
import _sortInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/sort";
import _JSON$stringify from "@babel/runtime-corejs3/core-js-stable/json/stringify";
import _Object$assign from "@babel/runtime-corejs3/core-js-stable/object/assign";
import _mapInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/map";
// @flow
import React, { useMemo, useEffect } from 'react';
import { makeStyles } from '@material-ui/core/styles';
import List from '@material-ui/core/List';
import { defineMessages, useIntl } from 'react-intl';
import isEqual from 'lodash/isEqual';
import omit from 'lodash/omit';
import DateFnsUtils from '@date-io/date-fns'; // choose your lib

import { MuiPickersUtilsProvider } from '@material-ui/pickers';
import { createMemoizedStats } from '../lib/data_analysis/index';
import DiscreteFilter from './DiscreteFilter';
import DateFilter from './DateFilter';
import getStats from '../stats';
import FormattedFieldname from '../internal/FormattedFieldname';
/*:: import type {
  Statistics,
  Filter,
  Field,
  SelectOptions,
  FieldStatistic
} from '../types'*/

/*:: import type { Observation, Preset } from 'mapeo-schema'*/

/*:: type Props = {
  filter: Filter | null,
  onChangeFilter: (filter: Filter | null) => void,
  observations?: Observation[],
  fields?: Field[],
  presets?: Preset[]
}*/

const m = defineMessages({
  // Button text to change which fields are shown and filterable in the filter pane
  editFilters: {
    "id": "eO0bIg==",
    "defaultMessage": 'Edit Filters…'
  },
  // Label for filter by date observation was created
  created: {
    "id": "pxFasw==",
    "defaultMessage": 'Date of observation'
  },
  // Label for filter by date observation was modified (e.g. edited by a user)
  modified: {
    "id": "eykPgw==",
    "defaultMessage": 'Modified'
  },
  // Label for filter by category (e.g. the preset)
  preset: {
    "id": "KdbzkA==",
    "defaultMessage": 'Category'
  }
});
const memoizedStats = createMemoizedStats(); // Stats just for the created and modified fields. The other stat instance we
// use is for memoized stats of observation tags which does not include these
// top-level props

const getTimestampStats = (observations
/*: Observation[]*/
) =>
/*: Statistics*/
{
  return memoizedStats(_mapInstanceProperty(observations).call(observations, o => ({
    $created: o.created_at,
    $modified: o.timestamp
  })));
};

const FilterPanel = ({
  filter,
  onChangeFilter,
  observations = [],
  fields = [],
  presets = []
}
/*: Props*/
) => {
  var _context2, _context3;

  const cx = useStyles();
  const {
    formatMessage: t
  } = useIntl();
  let filterByField
  /*: { [fieldId: string]: Filter | null }*/
  = {};
  let filterError;

  try {
    filterByField = parseFilter(filter);
  } catch (e) {
    console.log(filter);
    console.warn(e);
    filterError = false;
  }

  const timestampStats = useMemo(() => getTimestampStats(observations), [observations]);
  const stats = useMemo(() => getStats(observations), [observations]);

  const handleChangeFilter = fieldId => filter => {
    const newFilterByField = filter == null ? omit(filterByField, fieldId) : _Object$assign({}, filterByField, {
      [fieldId]: filter
    });
    const newFilter = compileFilter(newFilterByField);
    onChangeFilter(newFilter);
  }; // Filters are shown for: date the observation was created, date modified
  // (edited), the category (preset), and any fields defined in the preset which
  // are select_one, date or date_time. Currently we don't show presets for
  // free-form text fields (which could have too many values, or too many ways
  // of spelling things to makes sense).


  const filterFields = useMemo(() => {
    var _context;

    const $createdId = _JSON$stringify(['$created']);

    const $presetId = _JSON$stringify(['$preset']);

    const filterFields
    /*: { [fieldId: string]: Field }*/
    = {
      [$createdId]: {
        id: $createdId,
        key: ['$created'],
        label: t(m.created),
        type: 'datetime',
        min_value: timestampStats[$createdId] && timestampStats[$createdId].datetime.min,
        max_value: timestampStats[$createdId] && timestampStats[$createdId].datetime.max
      }
    };

    const presetOptions = _mapInstanceProperty(_context = _sortInstanceProperty(presets // .filter(
    //   preset =>
    //     stats['categoryId'] &&
    //     stats['categoryId'].string.values.has(preset.id)
    // )
    ).call(presets, presetCompare)).call(_context, preset => ({
      value: preset.id,
      label: preset.name
    }));

    filterFields[$presetId] = {
      id: $presetId,
      key: ['$preset'],
      label: t(m.preset),
      type: 'select_one',
      options: presetOptions
    }; // Enable filtering by any select_one, date or date_time field that is
    // defined in the preset, but add in options (for select_one) and min/max
    // (for dates) from the actual data, since the data could include values
    // outside the range defined in the preset

    _forEachInstanceProperty(fields).call(fields, field => {
      const fieldId = _JSON$stringify(field.key);

      const fieldStats = stats[fieldId];

      if (field.type === 'select_one') {
        // $FlowFixMe
        filterFields[fieldId] = _Object$assign({}, field, {
          options: combineOptionsWithStats(field.options, fieldStats)
        });
      } else if (field.type === 'date' || field.type === 'datetime') {
        // $FlowFixMe
        filterFields[fieldId] = _Object$assign({}, field, {
          min_value: fieldStats && fieldStats[field.type].min,
          max_value: fieldStats && fieldStats[field.type].max
        });
      }
    });

    return filterFields;
  }, [t, timestampStats, presets, fields, stats]);
  useEffect(() => {
    if (!filterError) return;
    onChangeFilter(null);
  }, [filterError, onChangeFilter]);
  if (filterError) return null;
  return /*#__PURE__*/React.createElement(MuiPickersUtilsProvider, {
    utils: DateFnsUtils
  }, /*#__PURE__*/React.createElement(List, {
    className: cx.list
  }, _filterInstanceProperty(_context2 = _mapInstanceProperty(_context3 = _Object$keys(filterFields)).call(_context3, id => {
    const field = filterFields[id];
    if (!field) return;

    const fieldId = _JSON$stringify(field.key);

    switch (field.type) {
      case 'select_one':
        return /*#__PURE__*/React.createElement(DiscreteFilter, {
          key: field.id,
          fieldKey: field.key,
          label: /*#__PURE__*/React.createElement(FormattedFieldname, {
            field: field
          }),
          filter: filterByField[fieldId],
          options: field.options,
          onChangeFilter: handleChangeFilter(fieldId)
        });

      case 'date':
      case 'datetime':
        return /*#__PURE__*/React.createElement(DateFilter, {
          key: field.id,
          fieldKey: field.key,
          label: /*#__PURE__*/React.createElement(FormattedFieldname, {
            field: field
          }),
          filter: filterByField[fieldId],
          min: field.min_value || '2001-01-01',
          max: field.max_value || new Date().toISOString(),
          onChangeFilter: handleChangeFilter(fieldId)
        });
    }
  })).call(_context2, Boolean)));
};

export default FilterPanel;
const comparisonOps = ['<=', '>='];
const membershipOps = ['in', '!in']; // Parse a filter and return filter expressions by field id

function parseFilter(filter
/*: Filter | null*/
)
/*: { [fieldId: string]: Filter | null }*/
{
  var _context4;

  const filterByField = {};
  if (filter == null) return filterByField;
  if (!isValidFilter(filter)) throw new Error('Unsupported filter expression');

  _forEachInstanceProperty(_context4 = _sliceInstanceProperty(filter).call(filter, 1)).call(_context4, subFilter => {
    if (!_Array$isArray(subFilter)) return;

    if (subFilter[0] === 'all') {
      const fieldId = _JSON$stringify(subFilter[1][1]);

      filterByField[fieldId] = subFilter;
    } else {
      const fieldId = _JSON$stringify(subFilter[1]);

      filterByField[fieldId] = subFilter;
    }
  });

  return filterByField;
}

function compileFilter(filterByField
/*: {
  [fieldId: string]: Filter
}*/
)
/*: Filter | null*/
{
  var _context5;

  const filter = ['all'];

  _forEachInstanceProperty(_context5 = _Object$keys(filterByField)).call(_context5, fieldId => filter.push(filterByField[fieldId]));

  if (filter.length === 1) return null; // $FlowFixMe

  return filter;
} // Currently we only support a very specific filter structure


function isValidFilter(filter)
/*: boolean*/
{
  var _context6;

  if (!_Array$isArray(filter)) return false;
  if (filter[0] !== 'all') return false;
  return _everyInstanceProperty(_context6 = _sliceInstanceProperty(filter).call(filter, 1)).call(_context6, subFilter => {
    if (!_Array$isArray(subFilter)) return false;

    if (subFilter[0] === 'all') {
      var _context7;

      if (subFilter.length < 2) return false;
      let key;
      return _everyInstanceProperty(_context7 = _sliceInstanceProperty(subFilter).call(subFilter, 1)).call(_context7, subFilter => {
        if (!_Array$isArray(subFilter)) return false;
        if (!_includesInstanceProperty(comparisonOps).call(comparisonOps, subFilter[0])) return false;
        if (subFilter.length !== 3) return false;
        if (key && !isEqual(key, subFilter[1])) return false;
        key = subFilter[1];
        return true;
      });
    }

    if (!_includesInstanceProperty(membershipOps).call(membershipOps, subFilter[0])) return false;
    if (subFilter.length < 3) return false;
    return true;
  });
} // Takes a list of options for a select_one field defined in a preset, and adds
// in any values that exist in the data that are not already listed in the field
// definition. Mapeo Desktop (currently) allows you to add new option to a
// select_one field which aren't defined in the preset, and this allows you to
// filter by these new values


function combineOptionsWithStats(fieldOptions
/*: SelectOptions*/
, fieldStats
/*: FieldStatistic*/
)
/*: SelectOptions*/
{
  var _context8;

  if (!fieldStats) return fieldOptions;
  const optionsWithStats = [...fieldOptions];

  _forEachInstanceProperty(_context8 = _Object$keys(fieldStats)).call(_context8, valueType => {
    if (!fieldStats[valueType] || !_valuesInstanceProperty(fieldStats[valueType])) return;

    const values = _valuesInstanceProperty(fieldStats[valueType]);

    if (!(values instanceof _Map)) return;

    for (const value of _keysInstanceProperty(values).call(values)) {
      if (_findInstanceProperty(optionsWithStats).call(optionsWithStats, o => o === value || typeof o === 'object' && o !== null && o.value === value)) continue;
      optionsWithStats.push(value);
    }
  });

  return optionsWithStats;
}

const useStyles = makeStyles(theme => ({
  list: {
    paddingTop: 0,
    paddingBottom: 0,
    overflowY: 'scroll'
  },
  settingsItem: {
    paddingTop: 8,
    paddingBottom: 8
  },
  listIcon: {
    minWidth: 40
  }
})); // Sort presets by sort property and then by name, then filter only point presets

function presetCompare(a, b) {
  if (typeof _sortInstanceProperty(a) !== 'undefined' && typeof _sortInstanceProperty(b) !== 'undefined') {
    // If sort value is the same, then sort by name
    if (_sortInstanceProperty(a) === _sortInstanceProperty(b)) return compareStrings(a.name, b.name); // Lower sort numbers come before higher numbers
    else return _sortInstanceProperty(a) - _sortInstanceProperty(b);
  } else if (typeof _sortInstanceProperty(a) !== 'undefined') {
    // If a has a sort field but b doesn't, a comes first
    return -1;
  } else if (typeof _sortInstanceProperty(b) !== 'undefined') {
    // if b has a sort field but a doesn't, b comes first
    return 1;
  } else {
    // if neither have sort defined, compare by name
    return compareStrings(a.name, b.name);
  }
}

function compareStrings(a = '', b = '') {
  return a.toLowerCase().localeCompare(b.toLowerCase());
}
//# sourceMappingURL=FilterPanel.js.map