import _ from 'lodash';
import { handleActions } from 'redux-actions';
import getSectionState from 'Utilities/State/getSectionState';
import updateSectionState from 'Utilities/State/updateSectionState';
import {
  SET,
  UPDATE,
  UPDATE_ITEM,
  UPDATE_SERVER_SIDE_COLLECTION,
  CLEAR_PENDING_CHANGES,
  REMOVE_ITEM
} from 'Store/Actions/baseActions';

const blacklistedProperties = [
  'section',
  'id'
];

export default function createHandleActions(handlers, defaultState, section) {
  return handleActions({

    [SET]: function(state, { payload }) {
      const payloadSection = payload.section;
      const [baseSection] = payloadSection.split('.');

      if (section === baseSection) {
        const newState = Object.assign(getSectionState(state, payloadSection),
          _.omit(payload, blacklistedProperties));

        return updateSectionState(state, payloadSection, newState);
      }

      return state;
    },

    [UPDATE]: function(state, { payload }) {
      const payloadSection = payload.section;
      const [baseSection] = payloadSection.split('.');

      if (section === baseSection) {
        const newState = getSectionState(state, payloadSection);

        if (_.isArray(payload.data)) {
          newState.items = payload.data;
          newState.itemMap = _.zipObject(_.map(payload.data, 'id'), _.range(payload.data.length));
        } else {
          newState.item = payload.data;
        }

        return updateSectionState(state, payloadSection, newState);
      }

      return state;
    },

    [UPDATE_ITEM]: function(state, { payload }) {
      const {
        section: payloadSection,
        updateOnly = false,
        ...otherProps
      } = payload;

      const [baseSection] = payloadSection.split('.');

      if (section === baseSection) {
        const newState = getSectionState(state, payloadSection);
        const items = newState.items;
        const index = _.findIndex(items, { id: payload.id });

        newState.items = [...items];

        // TODO: Move adding to it's own reducer
        if (index >= 0) {
          const item = items[index];

          newState.items.splice(index, 1, { ...item, ...otherProps });
        } else if (!updateOnly) {
          newState.items.push({ ...otherProps });
          newState.itemMap = _.zipObject(_.map(newState.items, 'id'), _.range(newState.items.length));
        }

        return updateSectionState(state, payloadSection, newState);
      }

      return state;
    },

    [CLEAR_PENDING_CHANGES]: function(state, { payload }) {
      const payloadSection = payload.section;
      const [baseSection] = payloadSection.split('.');

      if (section === baseSection) {
        const newState = getSectionState(state, payloadSection);
        newState.pendingChanges = {};

        if (newState.hasOwnProperty('saveError')) {
          newState.saveError = null;
        }

        return updateSectionState(state, payloadSection, newState);
      }

      return state;
    },

    [REMOVE_ITEM]: function(state, { payload }) {
      const payloadSection = payload.section;
      const [baseSection] = payloadSection.split('.');

      if (section === baseSection) {
        const newState = getSectionState(state, payloadSection);

        newState.items = [...newState.items];
        _.remove(newState.items, { id: payload.id });

        newState.itemMap = _.zipObject(_.map(newState.items, 'id'), _.range(newState.items.length));

        return updateSectionState(state, payloadSection, newState);
      }

      return state;
    },

    [UPDATE_SERVER_SIDE_COLLECTION]: function(state, { payload }) {
      const payloadSection = payload.section;
      const [baseSection] = payloadSection.split('.');

      if (section === baseSection) {
        const data = payload.data;
        const newState = getSectionState(state, payloadSection);

        const serverState = _.omit(data, ['records']);
        const calculatedState = {
          totalPages: Math.max(Math.ceil(data.totalRecords / data.pageSize), 1),
          items: data.records
        };

        return updateSectionState(state, payloadSection, Object.assign(newState, serverState, calculatedState));
      }

      return state;
    },

    ...handlers

  }, defaultState);
}
