import moment from 'moment';
import { split } from '../utils/filter';
import { is } from '../utils/helper';

const parseExpr = (valueExpr) => {
  let valueExpr0 = valueExpr.replace(/\s+/, '');
  let operator;
  if (valueExpr0.indexOf('=') === 0) {
    return {
      operator: 'exact',
      exact: valueExpr0.slice(1),
    };
  } else if (valueExpr0.indexOf('~') === 0) {
    operator = 'range';
    const ret = {
      operator,
    };
    valueExpr0 = valueExpr0.slice(1).replace(/\s+/, '');
    ret.downClose = valueExpr0[0] === '[';
    valueExpr0 = valueExpr0.slice(1).replace(/\s+/, '');
    let pos = valueExpr0.indexOf(',');
    if (pos === -1) {
      throw new Error('Invalid input.');
    }
    ret.down = Number.parseFloat(valueExpr0.slice(0, pos));
    valueExpr0 = valueExpr0.slice(pos + 1);
    pos = valueExpr0.indexOf(']');
    if (pos !== -1) {
      ret.upClose = true;
      ret.up = Number.parseFloat(valueExpr0.slice(0, pos));
      return ret;
    }
    pos = valueExpr0.indexOf(')');
    if (pos !== -1) {
      ret.upClose = false;
      ret.up = Number.parseFloat(valueExpr0.slice(0, pos));
      return ret;
    }
    throw new Error('Invalid input.');
  } else if (valueExpr0.indexOf('@') === 0) {
    operator = 'like';
    return {
      operator,
      like: valueExpr0.slice(1),
    };
  } else if (valueExpr0.indexOf('^') === 0) {
    operator = 'list';
    return {
      operator,
      list: valueExpr0.slice(1).split(','),
    };
  } else {
    throw new Error('Invalid input.');
  }
};

const toFunction = (keysExpr, valueExpr) => {
  const parsedExpr = parseExpr(valueExpr);
  return (obj) => {
    const value = obj[keysExpr.slice(2)]; // 略过'f-'
    if (value === undefined) {
      return true;
    }
    if (value === null) {
      return parsedExpr.operator === 'exact' && parsedExpr.exact === '' || parsedExpr.exact === null;
    }
    if (parsedExpr.operator === 'exact') {
      if (Number.isFinite(value)) {
        return value === Number.parseFloat(parsedExpr.exact);
      }
      if (moment.isMoment(value)) {
        return value.isSame(Number.parseFloat(parsedExpr.exact));
      }
      if (moment.isDate(value)) {
        return value.isSame(Number.parseFloat(parsedExpr.exact));
      }
      if (is(value, 'String')) {
        return value === parsedExpr.exact;
      }
    } else if (parsedExpr.operator === 'range') {
      if (Number.isFinite(value)) {
        let ret = true;
        if (!(/^\s*$/).test(parsedExpr.down)) {
          if (parsedExpr.downClose) {
            ret = ret && value >= Number.parseFloat(parsedExpr.down);
          } else {
            ret = ret && value > Number.parseFloat(parsedExpr.down);
          }
        }
        if (!(/^\s*$/).test(parsedExpr.up)) {
          if (parsedExpr.upClose) {
            ret = ret && value <= Number.parseFloat(parsedExpr.up);
          } else {
            ret = ret && value < Number.parseFloat(parsedExpr.up);
          }
        }
        return ret;
      } else if (moment.isDate(value) || moment.isMoment(value) || is(value, 'String')) {
        let ret = true;
        if (!(/^\s*$/).test(parsedExpr.down)) {
          const a = moment(value);
          const b = moment(Number.parseFloat(parsedExpr.down));
          if (parsedExpr.downClose) {
            ret = ret && (a.isAfter(b) || a.isSame(b));
          } else {
            ret = ret && a.isAfter(b);
          }
        }
        if (!(/^\s*$/).test(parsedExpr.up)) {
          const a = moment(value);
          const b = moment(Number.parseFloat(parsedExpr.up));
          if (parsedExpr.upClose) {
            ret = ret && (a.isBefore(b) || a.isSame(b));
          } else {
            ret = ret && a.isBefore(b);
          }
        }
        return ret;
      }
    } else if (parsedExpr.operator === 'like') {
      if (is(value, 'String')) {
        const reg = new RegExp(`^${parsedExpr.like
          .replace(/%/g, '[\\s\\S]*')
          .replace(/_/g, '[\\s\\S]')
          .replace(/\[!([\s\S]+)]/, '[^$1]')}$`);
        return reg.test(value);
      }
    } else if (parsedExpr.operator === 'list') {
      for (const test of parsedExpr.list) {
        // noinspection EqualityComparisonWithCoercionJS
        if (test == value) { // eslint-disable-line eqeqeq
          return true;
        }
      }
      return false;
    }
    throw new Error(`Invalid input.${keysExpr}|${valueExpr}.`);
  };
};

const toFilters = (keysExpr, valueExpr) => {
  const keys = split(keysExpr);
  const values = split(valueExpr);
  if (keys.length !== values.length) {
    throw new Error('Invalid input.');
  }
  return (obj) => {
    const len = keys.length;
    const fucs = [];
    for (let i = 0; i < len; ++i) {
      const key = keys[i];
      const value = values[i];
      fucs.push(toFunction(key, value));
    }
    for (let i = 0; i < len; ++i) {
      if (fucs[i](obj)) {
        return true;
      }
    }
    return false;
  };
};

export default toFilters;