import { State } from "../types";
import { sortMultiple } from "../helpers";

const defaultState: State = {
  objectKey: "",
  recordId: "",
  wait: 0,
  attempts: 3,
  page: 1,
  rowsPerPage: 1000,
  additions: [],
  failed: false,
  records: [],
  combined: {},
};

const requestFactory = () => {
  let state: { [key: string]: State } = {};

  const initialize = (requestId: string) => {
    state[requestId] = { ...defaultState };
    return state[requestId];
  };
  // objectKey
  const setObjectKey = (requestId: string, objectKey: string) =>
    (state[requestId].objectKey = objectKey);

  // recordId
  const setRecordId = (requestId: string, recordId: string) =>
    (state[requestId].recordId = `/${recordId}`);

  // wait
  const setWaitTime = (requestId: string, wait: number) =>
    (state[requestId].wait = wait);
  const getWaitTime = (requestId: string) => state[requestId].wait;
  const incrementWaitTime = (requestId: string) => {
    state[requestId].wait = state[requestId].wait + 2000;
    return state[requestId].wait;
  };

  // attempts
  const getAttempts = (requestId: string) => state[requestId].attempts;
  const decrementAttempts = (requestId: string) =>
    (state[requestId].attempts = state[requestId].attempts - 1);
  const setAttempts = (requestId: string, attempts: number) =>
    (state[requestId].attempts = attempts);
  // page
  const incrementPage = (requestId: string) => {
    state[requestId].page = state[requestId].page + 1;
    return state[requestId].page;
  };
  // rowsPerPage
  const setRowsPerPage = (requestId: string, rows: number) => {
    state[requestId].rowsPerPage = rows;
    return state[requestId].rowsPerPage;
  };
  // additions
  const resetAdditions = (requestId: string) =>
    (state[requestId].additions = []);
  const addAddition = (requestId: string, addition: string) => {
    if (state[requestId].additions.indexOf(addition) === -1)
      state[requestId].additions = [...state[requestId].additions, addition];
  };

  // failed
  const setFailed = (requestId: string, failed: boolean) =>
    (state[requestId].failed = failed);

  // utils
  const getUrl = (requestId: string, host?: string) => {
    const { objectKey, recordId, additions } = state[requestId];

    const knackHost: string = host || "knack";

    const rootUrl = `https://api.${knackHost}.com/v1/objects/${objectKey}/records${recordId}`;

    if (additions.length) {
      const requestUrl = `${rootUrl}?${additions.join("&")}`;
      return requestUrl;
    }

    return rootUrl;
  };

  const combineRecords = (requestId: string, settings: any, response: any) => {
    let records = [...state[requestId].records];
    let combined = { ...state[requestId].combined };

    if (!response.records) {
      if (response.id) {
        records = [...records, response];
        combined = {
          ...combined,
          records,
        };
        const recordResponse = combined;
        remove(requestId);
        return recordResponse;
      }
      combined = { ...response };
      const recordResponse = combined;
      remove(requestId);
      return recordResponse;
    }
    // initially set combined obj to response so it will have current_page, total_pages, etc.
    if (!Object.keys(combined).length) combined = response;
    // combine request records with existing records from previous calls
    records = [...records, ...response.records];
    // set combined response obj records to all records
    combined = {
      ...combined,
      records,
    };

    // check if we have a limit on the settings and if the records is over that limit
    const hasLimit = !!settings.recordLimit;
    const overLimit = !hasLimit
      ? false
      : combined.records.length >= settings.recordLimit
      ? true
      : false;

    // call again if we havent reached total pages
    if (response.current_page < response.total_pages && !overLimit) {
      settings.page = incrementPage(requestId);
      resetAdditions(requestId);
      state[requestId].combined = combined;
      state[requestId].records = records;
      return {
        retry: true,
        settings,
      };
    }
    if (settings.sort && Array.isArray(settings.sort)) {
      combined = {
        ...combined,
        records: sortMultiple(settings.sort, combined.records),
      };
    }
    const recordResponse = combined;
    remove(requestId);
    return recordResponse;
  };

  const getState = (requestId: string) => state[requestId];

  const remove = (requestId: string) => {
    const { [requestId]: removeState, ...remainingState } = state;

    state = remainingState;
  };

  return {
    initialize,
    setObjectKey,
    setRecordId,
    setWaitTime,
    decrementAttempts,
    setAttempts,
    incrementPage,
    setRowsPerPage,
    addAddition,
    resetAdditions,
    setFailed,
    incrementWaitTime,
    combineRecords,
    getWaitTime,
    getAttempts,
    getUrl,
    getState,
    remove,
  };
};

const factory = requestFactory();

export default factory;
