import { uppercase, firstLowerCase, capitalize, camelToUnderscore } from 'store/utils/helper';

/**
|--------------------------------------------------
| Types
|--------------------------------------------------
*/

export interface ActionRequest {
  type: string;
  payload: any;
}

export interface AsyncActionResult {
  type: string;
  status: string;
  payload: any;
}

export type StoreAction = ActionRequest & AsyncActionResult;

export interface SyncRequestStructure {
  action: (payload: any) => ActionRequest;
  constant: string;
}

export interface AsyncResponseStructure {
  action: (payload: any, status: string, requestData?: any) => AsyncActionResult;
  constant: string;
}

export interface AsyncActionStructure {
  request: SyncRequestStructure;
  result: AsyncResponseStructure;
  set: SyncRequestStructure;
  reset?: SyncRequestStructure;
}

export interface ResourceStucture {
  list: AsyncActionStructure;
  listMore: AsyncActionStructure;
  listPage?: AsyncActionStructure;
  search: AsyncActionStructure;
  add: AsyncActionStructure;
  detail: AsyncActionStructure;
  update: AsyncActionStructure;
  delete: AsyncActionStructure;
}

/**
|--------------------------------------------------
| Methods
|--------------------------------------------------
*/

/**
 * @deprecated
 */
export const makeAc = (type: string): Function => (payload: any): {} => ({
  type,
  payload,
});

/**
 * @deprecated
 */
export const makeResAc = (type: string): Function => (payload: any, status: string): {} => ({
  type,
  payload,
  status,
});

/**
 * @deprecated
 */
export const makeReqCons = (str: string, reset?: boolean): {} => {
  str = camelToUnderscore(str);
  str = uppercase(str);
  const returnObj = {
    request: `${str}_REQUEST`,
    result: `${str}_RESULT`,
    reset: false,
  };
  if (reset) {
    returnObj.reset = `${str}_RESET`;
  }
  return returnObj;
};

/**
 * @deprecated
 */
export const makeResourceConstants = (str: string): ResourceStucture => ({
  list: makeReqCons(`${str}_list`, true),
  listMore: makeReqCons(`${str}_list_more`),
  listPage: makeReqCons(`${str}_list_page`),
  detail: makeReqCons(`${str}_detail`, true),
  add: makeReqCons(`${str}_add`),
  update: makeReqCons(`${str}_update`),
  delete: makeReqCons(`${str}_delete`),
});

/**
 * @deprecated
 * IDEA OF AUTOGENERATED ACTIONS
 */
export const buildActions = (
  prefix: string,
  listConstants: {},
  returnObj: {},
  sync?: boolean,
): {} => {
  const keys = Object.keys(listConstants);
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i];
    const label = firstLowerCase(prefix + capitalize(keys[i]));
    if (
      typeof listConstants[key] === 'string' ||
      (listConstants[key] && listConstants[key].result)
    ) {
      if (typeof listConstants[key] === 'string') {
        returnObj[label] = makeAc(listConstants[key]);
      } else {
        returnObj[label] = makeAc(listConstants[key].request);
        if (listConstants[keys[i]].reset)
          returnObj[`${label}Reset`] = makeAc(listConstants[keys[i]].reset);
        returnObj[`${label}Result`] = makeResAc(listConstants[keys[i]].result);
      }
    } else if (typeof listConstants[key] === 'object') {
      buildActions(label, listConstants[key], returnObj, sync);
    }
  }
  return returnObj;
};

/*
 * Creation of an action structure
 */
const createActionStructure = (str: string): SyncRequestStructure => {
  str = uppercase(str);

  return {
    action: (payload: any): SyncRequestStructure => ({
      type: str,
      payload,
    }),
    constant: str,
  };
};

const createResponseStructure = (str: string): AsyncResponseStructure => {
  str = uppercase(str);

  return {
    action: (payload: any, status: string, requestData?: {}): AsyncActionResult => ({
      type: str,
      payload,
      requestData,
      status,
    }),
    constant: str,
  };
};

const createAsyncStructure = (str: string, reset?: boolean): AsyncActionStructure => {
  if (str.indexOf('_') < 0) str = camelToUnderscore(str);
  const returnObj = {
    request: createActionStructure(`${str}_request`),
    result: createResponseStructure(`${str}_result`),
    set: createActionStructure(`${str}_set`),
  };
  if (reset) {
    returnObj.reset = createActionStructure(`${str}_reset`);
  }
  return returnObj;
};

const createResourceStructure = (str: string): ResourceStucture => {
  if (str.indexOf('_') < 0) str = camelToUnderscore(str);
  return {
    list: createAsyncStructure(`${str}_list`, true),
    listMore: createAsyncStructure(`${str}_list_more`),
    listPage: createAsyncStructure(`${str}_list_page`),
    search: createAsyncStructure(`${str}_search`, true),
    detail: createAsyncStructure(`${str}_detail`, true),
    add: createAsyncStructure(`${str}_add`),
    update: createAsyncStructure(`${str}_update`),
    delete: createAsyncStructure(`${str}_delete`),
  };
};

const syncAction = (dispatch, action) => data => {
  if (typeof action === 'object') action = action?.request?.action;
  if (typeof action !== 'function') {
    console.error('No action provided', action);
    return;
  }
  dispatch(action(data));
};

const asyncAction = (dispatch, action) => data => {
  return new Promise((resolve, reject) => {
    data = data || {};
    data.resolve = resolve;
    data.reject = reject;
    syncAction(dispatch, action)(data);
  });
};

export {
  createActionStructure,
  createAsyncStructure,
  createResourceStructure,
  syncAction,
  asyncAction,
};
