import findIndex from 'lodash/findIndex';
import remove from 'lodash/remove';
import get from 'lodash/get';
import set from 'lodash/set';

import { cloneDeep } from 'lodash';
import { config } from 'config/general';
import { compareItems, compareSearch, customMerge } from './helper';

// resturn true if same

export const treatList = (list: any[], transformFunc: (...args: any[]) => any): any[] => {
  if (!list) return null;

  const newData = [];

  for (let i = 0; i < list.length; i++) {
    newData.push(transformFunc(list[i]));
  }
  return newData;
};

export const treatmentData = (data: any[] | any, transformFunc?: (...args: any[]) => any): any[] | any => {
  if (!transformFunc || typeof transformFunc !== 'function') return data;

  if (Array.isArray(data)) {
    return treatList(data, transformFunc);
  }
  return transformFunc(data);
};

export const getStateData = (state: any, path: string): any => {
  const current = get(state, path);
  if (!current) return false;
  const list = current.data;
  if (!list) return false;
  // list = list.slice();
  return list;
};

const handleDefaultEndRequest = (state: any, path: string): any => {
  set(state, path, {
    ...get(state, path),
    loaded: true,
    loading: false,
  });
  return state;
};

/**
 * For any request
 */
export function handleRequest(state: any, action: any, path: string, reset?: boolean): any {
  const payload = action.payload;
  const limit = (payload && payload.limit) || config.defaultLimit;
  let noReset = payload && payload.noReset;
  if (!noReset) {
    noReset = !reset;
  }
  if (payload && payload.reset) {
    noReset = false;
  }
  let newElem;
  if (!noReset) {
    newElem = {
      limit,
      loaded: false,
      loading: true,
    };
  } else {
    newElem = {
      ...get(state, path),
      limit,
      loaded: false,
      loading: true,
    };
  }
  set(state, path, newElem);

  return state;
}

export const handlePageRequest = (state: any, action: any, path: string): any => {
  const payload = action.payload;

  const current = get(state, path);

  const currentSearch = current && current.search;
  const reset = !compareSearch(currentSearch, payload);

  const tmpState = handleRequest(state, action, path, reset);

  set(tmpState, path, {
    ...get(tmpState, path),
    search: payload,
    currentPage: (payload && payload.page) || 1,
  });

  return tmpState;
};
/**
 * Setter
 */
export function handleResponse(state: any, action: any, path: string, transformFunc?: (...args: any[]) => any, nullAllowed?: boolean): any {
  const { payload, status, requestData } = action;

  if ((payload || (payload === null && nullAllowed)) && status === 'fulfilled') {
    const current = get(state, path);

    set(state, path, {
      ...current,
      requestData,
      data: treatmentData(payload, transformFunc),
      loaded: true,
      loading: false,
      noMore: (current && current.limit && payload?.length < current.limit) || payload?.length === 0,
    });
    return state;
  }
  return handleDefaultEndRequest(state, path);
}

export function handleMergeResponse(state, action, path: string, transformFunc?: (...args: any[]) => any): any {
  const { payload, status, requestData } = action;

  if (payload && status === 'fulfilled') {
    const current = get(state, path);

    const currentData = cloneDeep(current?.data);

    const merged = customMerge(currentData, payload);

    set(state, path, {
      ...current,
      requestData,
      data: treatmentData(merged, transformFunc),
      loaded: true,
      loading: false,
      noMore: (current?.limit && payload.length < current.limit) || payload.length === 0,
    });
    return state;
  }
  return handleDefaultEndRequest(state, path);
}

export function handleCountResponse(
  state: any,
  action: any,
  path: string,
  transformFunc?: (...args: any[]) => any,
  dataLabel?: string,
  countLabel?: string,
): any {
  const { payload, status, requestData } = action;

  const nameCount = countLabel || `${path}sGetCount`;
  const nameData = dataLabel || `${path}sGetMany`;

  if (payload && status === 'fulfilled') {
    const current = get(state, path);
    set(state, path, {
      ...current,
      requestData,
      data: treatmentData(payload[nameData], transformFunc),
      count: payload[nameCount],
      loaded: true,
      loading: false,
      noMore: (current && current.limit && payload.length < current.limit) || payload.length === 0,
    });
    return state;
  }
  return handleDefaultEndRequest(state, path);
}

export function handleReset(state: any, action: any, path: string): any {
  set(state, path, null);
  return state;
}

export function handlePageResponse(state: any, action: any, path: string, transformFunc?: (...args: any[]) => any): any {
  const { payload, status, requestData } = action;

  if (!payload) return handleDefaultEndRequest(state, path);

  const data = payload.data;
  const metadata = payload.metadata;

  const pagination = metadata && metadata.pagination;

  if (data && status === 'fulfilled') {
    const current = get(state, path);
    set(state, path, {
      ...current,
      requestData,
      metadata,
      pages: {
        ...current.pages,
        [(pagination && pagination.current_page) || 1]: treatmentData(data, transformFunc),
      },
      loaded: true,
      loading: false,
    });
    return state;
  }
  return handleDefaultEndRequest(state, path);
}
/**
 * Any add response (list or unit)
 */
export function handleAddResponse(state: any, action: any, path: string, transformFunc?: (...args: any[]) => any, reverse?: boolean): any {
  const { payload, status } = action;

  const list = getStateData(state, path) || [];

  const current = get(state, path);

  let treatedData = treatmentData(payload, transformFunc);
  if (!Array.isArray(treatedData)) treatedData = [treatedData];

  const newData = reverse ? [...treatedData.reverse().concat(list)] : [...list.concat(treatedData)];

  if (payload && status === 'fulfilled') {
    set(state, path, {
      ...current,
      data: newData,
      loaded: true,
      noMore: (current?.limit && payload.length < current.limit) || payload.length === 0,
      loading: false,
    });
    return state;
  }

  // PAGES MODE
  return handleDefaultEndRequest(state, path);
}

export function handleUpdateResponse(
  state: any,
  action: any,
  path: string,
  transformFunc?: (...args: any[]) => any,
  idKey: string = config.idKey,
): any {
  const { payload, status } = action;

  if (payload && status === 'fulfilled') {
    const list = getStateData(state, path);
    if (!list) return handleDefaultEndRequest(state, path);

    const index = findIndex(list, (item): boolean => compareItems(item, payload, idKey));
    list.splice(index, 1, treatmentData(payload, transformFunc));

    set(state, path, {
      ...get(state, path),
      data: list,
      loaded: true,
      loading: false,
    });
    return state;
  }
  return handleDefaultEndRequest(state, path);
}

export function handleUpdateMergeResponse(state, action, path: string, transformFunc?: (...args: any[]) => any, idKey: string = config.idKey): any {
  const { payload, status } = action;

  if (payload && status === 'fulfilled') {
    const list = getStateData(state, path);
    if (!list) return handleDefaultEndRequest(state, path);

    const index = findIndex(list, (item): boolean => compareItems(item, payload, idKey));
    const current = cloneDeep(list[index]);
    const merged = customMerge(current, payload);

    list.splice(index, 1, merged);

    set(state, path, {
      ...get(state, path),
      data: list,
      loaded: true,
      loading: false,
    });
    return state;
  }
  return handleDefaultEndRequest(state, path);
}

export function handleUpdatePaginationResponse(
  state: any,
  action: any,
  path: string,
  transformFunc?: (...args: any[]) => any,
  idKey: string = config.idKey,
): any {
  const { payload, status } = action;

  if (payload && status === 'fulfilled') {
    const current = get(state, path);
    const pages = current.pages;
    if (!pages) return handleDefaultEndRequest(state, path);

    const keys = Object.keys(pages);

    keys.forEach((key): void => {
      const page = pages[key];
      const index = findIndex(page, (item): boolean => compareItems(item, payload, idKey));
      page.splice(index, 1, treatmentData(payload, transformFunc));
    });

    set(state, path, {
      ...current,
      pages: { ...pages },
      loaded: true,
      loading: false,
    });
    return state;
  }
  return handleDefaultEndRequest(state, path);
}

export function handleDeleteResponse(state: any, action: any, path: string, idKey: string = config.idKey): any {
  const { payload, status, requestData } = action;

  const deleteIds = [];
  // multiple deleted
  if (Array.isArray(payload)) {
    payload?.forEach(it => deleteIds.push(it[idKey] || it));
  }
  deleteIds.push(requestData?.id || payload[idKey] || payload);
  if (payload && status === 'fulfilled') {
    const list = getStateData(state, path);
    if (!list) return handleDefaultEndRequest(state, path);

    // const removed =
    remove(list, (item): boolean => deleteIds?.indexOf(item[idKey]) >= 0);

    const currentState = get(state, path);
    // if (currentState?.count && removed) {
    //   currentState.count -= removed.length;
    // }

    set(state, path, {
      ...currentState,
      data: list,
      loaded: true,
      loading: false,
    });
    return state;
  }
  return handleDefaultEndRequest(state, path);
}

export function handleDeletePaginationResponse(state: any, action: any, path: string, idKey: string = config.idKey): any {
  const { payload, status } = action;

  if (payload && status === 'fulfilled') {
    const current = get(state, path);

    const pages = current.pages;
    if (!pages) return handleDefaultEndRequest(state, path);

    const keys = Object.keys(pages);

    keys.forEach((key): void => {
      const page = pages[key];
      remove(page, (item): boolean => item[idKey] === payload);
    });

    set(state, path, {
      ...current,
      pages: { ...pages },
      loaded: true,
      loading: false,
    });
    return state;
  }
  return handleDefaultEndRequest(state, path);
}

export function defaultTreatItem(item: any, excludedKeys?: string[], idKey: string = config.idKey): any {
  const newItem = {
    [idKey]: item[idKey],
    type: item.type,
  };

  const keys = Object.keys(item);
  keys.forEach((key): void => {
    if (excludedKeys && excludedKeys.indexOf(key) >= 0) return;

    const attr = item[key];

    // undefined, null, '', false
    if (!attr) {
      newItem[key] = attr;
      return;
    }
    if (typeof attr === 'object') {
      // TODO
    }
    if (Array.isArray(attr)) {
      // TODO
    }
    newItem[key] = attr;
  });

  return newItem;
}
