import moment from 'moment';
import get from 'lodash.get';

import { isDate } from '../../utils/validations.js';


import { DATE_FORMAT_SIMPLE, DATE_FORMAT_WITH_HOUR } from '../../constants';

function getDateFormat(option, showTime) {
  switch (option) {
    case 'Eq':
      return showTime ? DATE_FORMAT_WITH_HOUR : DATE_FORMAT_SIMPLE;
    case 'Gt':
      return showTime ? DATE_FORMAT_WITH_HOUR : DATE_FORMAT_SIMPLE;
    case 'Lt':
      return showTime ? DATE_FORMAT_WITH_HOUR : DATE_FORMAT_SIMPLE;
    case 'Gte':
      return showTime ? DATE_FORMAT_WITH_HOUR : DATE_FORMAT_SIMPLE;
    case 'Lte':
      return showTime ? DATE_FORMAT_WITH_HOUR : DATE_FORMAT_SIMPLE;
    case 'Between':
      return showTime ? DATE_FORMAT_WITH_HOUR : DATE_FORMAT_SIMPLE;
    default:
      return DATE_FORMAT_SIMPLE;
  }
}

export function formatLabel(value, showtime) {
  if (showtime) {
    return value.format(DATE_FORMAT_WITH_HOUR);
  } else {
    return value.format(DATE_FORMAT_SIMPLE);
  }
}

const baseProto = {
  buildItem(data, showTime) {
    const values = (data && data.target) ? data.target.values : data;
    return values ? {
      values,
      label: values.map(v => v.format(getDateFormat('Eq', showTime))),
      option: this.id,
    } : { option: this.id };
  },

  set selected(value) {
    this.selectedObject = this.buildItem(value, this.showTime);
    if (this.selectedObject) {
      this.selectedObject.chickletLabel = this.chickletLabel(this.selectedObject.label);
    }

    this.selectedObject.prefix = this.prefix;
  },

  get selected() {
    return this.selectedObject;
  },

  get selectedLabel() {
    return get(this.selectedObject, 'label', '');
  },

  get selectedValue() {
    return get(this.selectedObject, 'values', []);
  },

  loadDefaultValue() {
    return null;
  },

  chickletLabel(value) {
    const prefix = this.prefix || '';
    return `${prefix} ${String(value)}`;
  },

  clearValues() {
    if (this.selectedObject) {
      this.selectedObject.values = [];
    }
  },
};

const OptionFactory = {
  create(type, showTime, customLabel, selected) {
    const displayName = customLabel || this.optionsMap[type].displayName;

    this.optionsMap[type].displayName = displayName;
    return Object.assign(
      Object.create(baseProto),
      this.optionsMap[type],
      { key: type, showTime, default: true, isSelected: selected },
    );
  },

  createCustom(option) {
    return Object.assign(
      Object.create(baseProto),
      {
        id: `custom${option.operator}${option.values.join('').replace(/\s/g, '-')}`,
        key: `custom${option.values}`,
        displayName: option.label,
        custom: true,
        values: option.values,
        operator: option.operator,
        isSelected: option.selected,
        prefix: this.optionsMap[option.operator].prefix,
        buildItem() {
          return {
            values: this.values,
            prefix: this.prefix,
            label: this.displayName,
            option: this.operator,
            id: this.id,
            custom: true,
          };
        },
        loadDefaultValue() {
          this.selected = option;
          return true;
        },
      }
    );
  },

  optionsMap: {
    Any: {
      id: 'any',
      displayName: 'Any',
      values: [],
    },
    Eq: {
      id: 'Eq',
      displayName: 'On',
      prefix: '= ',
      values: [],
      buildItem: (value, showTime) => {
        const option = 'Eq';
        return (value ? { values: [value], label: formatLabel(value, showTime), option } : { option });
      },
    },
    NotEq: {
      id: 'NotEq',
      displayName: 'On',
      prefix: '!= ',
      values: [],
      buildItem: (value, showTime) => {
        const option = 'NotEq';
        return (value ? { values: [value], label: formatLabel(value, showTime), option } : { option });
      },
    },
    Gt: {
      id: 'Gt',
      displayName: '>',
      prefix: '> ',
      values: [],
      buildItem: (value, showTime) => {
        const option = 'Gt';
        return (value ? { values: [value], label: formatLabel(value, showTime), option } : { option });
      },
    },
    Lt: {
      id: 'Lt',
      displayName: '<',
      prefix: '< ',
      values: [],
      buildItem: (value, showTime) => {
        const option = 'Lt';
        return (value ? { values: [value], label: formatLabel(value, showTime), option } : { option });
      },
    },
    Gte: {
      id: 'Gte',
      displayName: '>=',
      prefix: '>= ',
      values: [],
      buildItem: (value, showTime) => {
        const option = 'Gte';
        return (value ? { values: [value], label: formatLabel(value, showTime), option } : { option });
      },
    },
    Lte: {
      id: 'Lte',
      displayName: '<=',
      prefix: '<= ',
      values: [],
      buildItem: (value, showTime) => {
        const option = 'Lte';
        return (value ? { values: [value], label: formatLabel(value, showTime), option } : { option });
      },
    },
    Between: {
      id: 'Between',
      displayName: 'Between',
      values: [],
      buildItem: (value, showTime) => {
        if (isDate(value)) {
          return {
            values: value,
            label: `${formatLabel(value[0], showTime)}
            - ${formatLabel(value[1], showTime)}`,
            option: 'Between',
          };
        }

        return { option: 'Between', values: [] };
      },
    },
  },
};

function buildChicklet(item, showTime) {
  const option = OptionFactory.optionsMap[item.operator];

  if (option === OptionFactory.optionsMap.Between) {
    const values = [
      moment(item.values[0], getDateFormat('between', showTime)),
      moment(item.values[1], getDateFormat('between', showTime)),
    ];

    return {
      option: option.id,
      operator: item.operator,
      prefix: option.prefix,
      values,
    };
  } else {
    return ({
      option: option.id,
      prefix: option.prefix,
      operator: item.operator,
      values: item.values,
    });
  }
}

export function buildChicklets(items, showTime) {
  const chicklets = items
    .map(item => buildChicklet(item, showTime))
    .filter(chicklet => chicklet !== null)
    .reduce((object, chicklet) => (
      Object.assign({}, object, { [chicklet.option]: chicklet })
    ), {});

  return Object.keys(chicklets).map(key => chicklets[key]);
}

export default OptionFactory;

export const isValidOption = option => (
  Object.keys(OptionFactory.optionsMap).indexOf(option) >= 0
);

export const defaultOptions = [];
