/* eslint no-param-reassign: "off" */

import _ from 'lodash';
import { ActionTypes } from 'lore-utils';

/*
 * find Reducer Blueprint
 */

function mergeDataAndIntersectWithDictionaryKeys(oldData, newData, dictionary) {
  const ids = _.map(oldData, 'id');
  const newIds = _.map(newData, 'id');
  const combinedIds = _.union(ids, newIds);
  const dictionaryKeys = _.keys(dictionary);

  // If any ids are Numbers, convert them to Strings so we can compare equality
  // with the dictionary keys
  const combinedIdsAsStrings = combinedIds.map(function(id) {
    return id.toString();
  });

  const idsInDictionary = _.intersection(combinedIdsAsStrings, dictionaryKeys);
  return idsInDictionary.map(function(id) {
    return dictionary[id];
  });
}

function mergeMissingDataIntoDictionary(data, query, byCid) {
  const where = query.where;
  const pagination = query.pagination;

  // Do NOT add data to paginated data sets. This function only exists
  // to facilitate use cases where the user needs to display ALL results
  // or results for simple queries (like authorId=xyz)
  if (pagination && Object.keys(pagination).length > 0) {
    return;
  }

  _.keys(byCid).forEach(function(cid) {
    const datum = byCid[cid];
    const existingDatum = _.find(data, { cid: cid });

    // if the datum is not in the data set
    if (!existingDatum) {
      // if this is the empty query add it (it stores everything)
      if (Object.keys(where).length === 0) {
        data.push(datum);

      // else if the datum matches the 'where' query, add it
      } else if (_.find([datum.data], where)) {
        data.push(datum);
      }
    }
  });
}

const SETTINGS = {
  REMOVE_DESTROYED_DATA: false,
  ADD_NEW_DATA_TO_QUERIES: false
};

export default function(modelName) {
  const initialState = {};
  // const EMPTY_QUERY_KEY = JSON.stringify({});

  return function find(state, action, options) {
    state = state || initialState;
    let nextState = _.assign({}, state);
    const byId = options.nextState.byId;
    const byCid = options.nextState.byCid;

    // Remove any data that no longer exists in byId
    // This usually means it was deleted
    if (SETTINGS.REMOVE_DESTROYED_DATA) {
      nextState = _.mapValues(nextState, function(collection, key) {
        const ids = collection.data.map(function(data) {
          // id will not always exist for the empty query case: {}
          return data.id ? data.id.toString() : data.id;
        });

        // get the list of ids that still exist
        const idsThatExist = _.keys(byId);

        // get the list of ids that should remain in the collection
        const validIds = _.intersection(ids, idsThatExist);

        // convert the array of ids in the collection back to real objects
        collection.data = validIds.map(function(id) {
          return byId[id];
        });

        return collection;
      });
    }

    // This adds new data to queries it should be in...
    // Unsure whether this is good or bad...what are the limits?
    if (SETTINGS.ADD_NEW_DATA_TO_QUERIES) {
      nextState = _.mapValues(nextState, function(collection, key) {
        const query = JSON.parse(key);

        mergeMissingDataIntoDictionary(collection.data, query, byCid);

        // sort the data by cid, so it has some kind of default ordering
        _.sortBy(collection.data, 'cid');

        return collection;
      });
    }

    switch (action.type) {
      case ActionTypes.fetchPlural(modelName): {
        const query = JSON.stringify(action.query);
        nextState[query] = nextState[query] || {};
        nextState[query] = _.assign({}, action.payload, {
          data: mergeDataAndIntersectWithDictionaryKeys(
            nextState[query].data,
            action.payload.data,
            byId
          )
        });
        return nextState;
      }

      case ActionTypes.update(modelName): {
        nextState = _.mapValues(nextState, function (collection, key) {
          collection.data = collection.data.map(function (datum) {
            if (datum.id === action.payload.id) {
              return action.payload;
            }
            return datum;
          });

          return collection;
        });
        return nextState;
      }

      case ActionTypes.remove(modelName): {
        nextState = _.mapValues(nextState, function (collection, key) {
          collection.data = collection.data.map(function (datum) {
            if (datum.id === action.payload.id) {
              return action.payload;
            }
            return datum;
          });

          return collection;
        });
        return nextState;
      }

      default:
        return nextState;
    }
  };
}

@@ -748,2652 +748,330 @@
 %0A%7D%0A%0A
-function mergeMissingDataIntoDictionary(data, query, byCid) %7B%0A  const where = query.where;%0A  const pagination = query.pagination;%0A%0A  // Do NOT add data to paginated data sets. This function only exists%0A  // to facilitate use cases where the user needs to display ALL results%0A  // or results for simple queries (like authorId=xyz)%0A  if (pagination && Object.keys(pagination).length %3E 0) %7B%0A    return;%0A  %7D%0A%0A  _.keys(byCid).forEach(function(cid) %7B%0A    const datum = byCid%5Bcid%5D;%0A    const existingDatum = _.find(data, %7B cid: cid %7D);%0A%0A    // if the datum is not in the data set%0A    if (!existingDatum) %7B%0A      // if this is the empty query add it (it stores everything)%0A      if (Object.keys(where).length === 0) %7B%0A        data.push(datum);%0A%0A      // else if the datum matches the 'where' query, add it%0A      %7D else if (_.find(%5Bdatum.data%5D, where)) %7B%0A        data.push(datum);%0A      %7D%0A    %7D%0A  %7D);%0A%7D%0A%0Aconst SETTINGS = %7B%0A  REMOVE_DESTROYED_DATA: false,%0A  ADD_NEW_DATA_TO_QUERIES: false%0A%7D;%0A%0Aexport default function(modelName) %7B%0A  const initialState = %7B%7D;%0A  // const EMPTY_QUERY_KEY = JSON.stringify(%7B%7D);%0A%0A  return function find(state, action, options) %7B%0A    state = state %7C%7C initialState;%0A    let nextState = _.assign(%7B%7D, state);%0A    const byId = options.nextState.byId;%0A    const byCid = options.nextState.byCid;%0A%0A    // Remove any data that no longer exists in byId%0A    // This usually means it was deleted%0A    if (SETTINGS.REMOVE_DESTROYED_DATA) %7B%0A      nextState = _.mapValues(nextState, function(collection, key) %7B%0A        const ids = collection.data.map(function(data) %7B%0A          // id will not always exist for the empty query case: %7B%7D%0A          return data.id ? data.id.toString() : data.id;%0A        %7D);%0A%0A        // get the list of ids that still exist%0A        const idsThatExist = _.keys(byId);%0A%0A        // get the list of ids that should remain in the collection%0A        const validIds = _.intersection(ids, idsThatExist);%0A%0A        // convert the array of ids in the collection back to real objects%0A        collection.data = validIds.map(function(id) %7B%0A          return byId%5Bid%5D;%0A        %7D);%0A%0A        return collection;%0A      %7D);%0A    %7D%0A%0A    // This adds new data to queries it should be in...%0A    // Unsure whether this is good or bad...what are the limits?%0A    if (SETTINGS.ADD_NEW_DATA_TO_QUERIES) %7B%0A      nextState = _.mapValues(nextState, function(collection, key) %7B%0A        const query = JSON.parse(key);%0A%0A        mergeMissingDataIntoDictionary(collection.data, query, byCid);%0A%0A        // sort the data by cid, so it has some kind of default ordering%0A        _.sortBy(collection.data, 'cid');%0A%0A        return collection;%0A      %7D);%0A    %7D
+export default function(modelName) %7B%0A  const initialState = %7B%7D;%0A  // const EMPTY_QUERY_KEY = JSON.stringify(%7B%7D);%0A%0A  return function find(state, action, options) %7B%0A    state = state %7C%7C initialState;%0A    let nextState = _.assign(%7B%7D, state);%0A    const byId = options.nextState.byId;%0A    const byCid = options.nextState.byCid;
 %0A%0A  
@@ -1593,33 +1593,32 @@
 tState, function
- 
 (collection, key
@@ -1669,33 +1669,32 @@
 ata.map(function
- 
 (datum) %7B%0A      
@@ -2000,17 +2000,16 @@
 function
- 
 (collect
@@ -2076,17 +2076,16 @@
 function
- 
 (datum) 

remove disabled special cases