import auth from 'store/auth';

import {
  handleRequest,
  handleResponse,
  handleAddResponse,
  handleReset,
  handleDeleteResponse,
  handleUpdateResponse,
} from 'store/utils/reducers';
import produce from 'immer';
import { StoreAction } from 'store/utils/actions';
import { config } from 'config/general';
import _ from 'lodash';
import { clone } from 'store/utils/helper';
import apolloActions from 'store/genericApollo/actions';
import genericApollo from 'store/genericApollo';
import account from 'store/account';
import actions from './actions';

interface GenericState {
  list: Record<string, any>;
  detail: Record<string, any>;
  appHistory: any[] | null;
  loading: {
    add: boolean;
    update: boolean;
    login: boolean;
  };
  meta: {};
}

export const initialState: GenericState = {
  list: null,
  detail: null,
  search: null,
  appHistory: null,
  loading: {
    add: false,
    update: false,
    login: false,
  },
  meta: {},
};

const treatGeneric = (item: Record<string, any>): Record<string, any> => {
  const newItem = { ...item };

  return newItem;
};

const reducer = (state: GenericState = initialState, action: StoreAction): GenericState =>
  produce(
    state,
    (draft: GenericState): GenericState => {
      let item;
      let index;
      let found;
      switch (action.type) {
        // meta is used to store data
        case actions.setMeta.constant: {
          const payload = action.payload || {};

          const keys = Object.keys(payload);
          for (let i = 0; i < keys.length; i += 1) {
            const key = keys[i];
            const elem = payload[key];
            draft.meta[key] = elem;
          }
          break;
        }
        // List
        case actions.list.request.constant: {
          const { resourceType, filterHash } = action.payload;

          draft.list = draft.list || {};

          const currentResource = draft.list.resource;
          const currentFilterHash = draft.list.filterHash;
          const currentData = draft.list.data;

          if (
            currentFilterHash === filterHash &&
            currentResource === resourceType &&
            currentData &&
            currentData.length > 0
          ) {
            // do nothing
          } else {
            // reinit meta
            draft.meta = {};
            draft.list = {
              resource: resourceType,
              filterHash,
              loading: true,
              loaded: false,
            };
          }
          break;
        }
        case apolloActions.list.request.constant: {
          const { options, filterHash } = action.payload;

          // add title list to avoid having same resource different entities thanks to inputs
          const resourceName = (options.resource || options.name) + options.titleList;

          draft.list = draft.list || {};

          const currentResource = draft.list.resource;
          const currentFilterHash = draft.list.filterHash;
          const currentData = draft.list.data;

          if (
            currentFilterHash === filterHash &&
            currentResource === resourceName &&
            currentData &&
            currentData.length > 0
          ) {
            // do nothing
          } else {
            // reinit meta
            draft.meta = {};
            draft.list = {
              resource: resourceName,
              filterHash,
              loading: true,
              loaded: false,
            };
          }
          break;
        }
        case actions.list.reset.constant:
          draft.list = draft.list || {};
          draft.list.data = [];
          break;
        case actions.list.result.constant:
          handleResponse(draft, action, 'list', treatGeneric);
          break;
        case actions.listMore.request.constant:
          handleRequest(draft, action, 'list');
          break;
        case apolloActions.listMore.request.constant:
          handleRequest(draft, action, 'list');
          break;
        case actions.listMore.result.constant:
          handleAddResponse(draft, action, 'list', treatGeneric);
          break;
        // Search
        case actions.search.reset.constant:
          handleReset(draft, action, 'search');
          break;
        case actions.search.result.constant:
          handleResponse(draft, action, 'search', treatGeneric);
          break;
        // Detail
        case actions.detail.reset.constant:
          handleReset(draft, action, 'detail');
          break;
        // case genericApollo.actions.detail.request.constant:
        //   handleRequest(draft, action, 'detail', true);
        //   break;
        case actions.detail.request.constant:
          handleRequest(draft, action, 'detail');
          break;
        case actions.detail.result.constant:
          handleResponse(draft, action, 'detail', treatGeneric);
          break;
        case actions.update.request.constant:
          draft.loading.update = true;
          break;
        case actions.update.result.constant:
          draft.loading.update = false;
          handleResponse(draft, action, 'detail', treatGeneric);
          handleUpdateResponse(draft, action, 'list', treatGeneric);
          break;
        case actions.fastUpdate.request.constant:
          index = _.findIndex(
            state.list.data,
            e => e[config.idKey] === action.payload.data[config.idKey],
          );
          if (index !== -1) draft.list.data[index] = action.payload.data;
          break;
        case actions.fastUpdate.result.constant:
          index = _.findIndex(
            state.list.data,
            e => e[config.idKey] === action.payload[config.idKey],
          );
          if (index !== -1) draft.list.data[index] = action.payload;
          break;
        case actions.add.request.constant:
          draft.loading.add = true;
          break;
        case actions.add.result.constant:
          draft.loading.add = false;
          handleResponse(draft, action, 'detail', treatGeneric);
          break;
        // Delete
        // case actions.delete.result.constant:
        //   item = _.find(
        //     draft.list.data,
        //     (e): void => e[config.idKey] === action.payload[config.idKey],
        //   );
        //   index = draft.list.data.indexOf(item);
        //   draft.list.data.splice(index, 1);
        //   break;
        // History
        case actions.history.push.constant:
          item = action.payload;

          draft.appHistory = draft.appHistory || [];

          found = false;
          for (let i = 0; i < draft.appHistory.length; i++) {
            if (draft.appHistory[i].id === item.id) {
              draft.appHistory = draft.appHistory.slice(0, i);
              found = true;
              break;
            }
          }
          draft.appHistory.push(item);
          break;
        case actions.history.reset.constant:
          draft.appHistory = null;
          break;
        case actions.delete.result.constant:
          handleDeleteResponse(draft, action, 'list');
          break;
        // login
        case auth.actions.login.request.constant:
          draft.loading.login = true;
          break;
        case auth.actions.login.result.constant:
          draft.loading.login = false;
          break;
        case auth.actions.logout.result.constant:
          draft.list = null;
          draft.detail = null;
          draft.search = null;
          draft.appHistory = null;

          break;
        // account
        case account.actions.selectAccountResource.constant:
          if (action.payload.forceReload) {
            draft.list = null;
            draft.detail = null;
            draft.search = null;
          }
          break;
        // firebase login
        case auth.actions.firebase.login.request.constant:
          draft.loading.login = true;
          break;
        case auth.actions.firebase.login.result.constant:
          draft.loading.login = false;
          break;
        default:
      }
    },
  );

export default reducer;
