import axios from "axios";
import dotenv from "dotenv";
import factory from "../factory";
import { getSettings } from "../types";
import { generateRequestId, handleError, wait } from "../helpers";

dotenv.config();

declare let process: any;

let axiosInstance = axios.create({
  headers: {
    "X-Knack-Application-Id": process.env.KNACK_APP_ID,
    "X-Knack-REST-API-Key": process.env.KNACK_API_KEY,
  },
});

const get = async (settings: getSettings): Promise<any> => {
  const {
    objectKey,
    recordId,
    filters,
    sort,
    debug,
    attempts,
    rowsPerPage,
    format,
    host,
  } = settings;

  if (!settings.requestId) settings.requestId = generateRequestId(8);

  const requestId = settings.requestId;

  let state = factory.getState(requestId);

  if (!state) state = factory.initialize(requestId);

  // if a number of attempts was sent in, this will set the number of retries to the attempts. If not, it will set the number of retries to the default 3
  // if it has failed before, it won't update the number of attempts again so that it will decrease properly
  const hasFailed = state.failed;
  hasFailed && attempts && factory.setAttempts(requestId, attempts);
  // initial page to 1
  if (!settings.page) settings.page = state.page;

  settings.method = "get";

  const rows_per_page = rowsPerPage
    ? factory.setRowsPerPage(requestId, rowsPerPage)
    : state.rowsPerPage;

  factory.setObjectKey(requestId, objectKey);

  recordId && factory.setRecordId(requestId, recordId);

  filters &&
    factory.addAddition(
      requestId,
      `filters=${encodeURIComponent(JSON.stringify(filters))}`
    );

  factory.addAddition(
    requestId,
    `page=${settings.page}&rows_per_page=${rows_per_page}`
  );

  if (format) {
    factory.addAddition(requestId, `format=${format}`);
  }

  if (sort) {
    // sort can be sent in as a single object or array of objects for multiple sorting
    // if it is an array, it will be done after records are retrieved in combine records
    if (!Array.isArray(sort)) {
      factory.addAddition(
        requestId,
        `sort_field=${sort.field}&sort_order=${sort.order}`
      );
    }
  }

  // if client sends in settings.debug as true, this will tell them the request url
  if (debug)
    console.warn(
      "Request ID:",
      requestId,
      "Request URL:",
      factory.getUrl(requestId, host)
    );

  try {
    axiosInstance.defaults.headers.get["Content-Type"] = "application/json";
    const response = await axiosInstance.get(factory.getUrl(requestId, host));

    const returnResponse: any = await factory.combineRecords(
      requestId,
      settings,
      response.data
    );

    if (returnResponse?.retry) {
      return await get(returnResponse.settings);
    }
    return returnResponse;
  } catch (error) {
    const errorRsp: any = handleError(factory, requestId, error);
    if (errorRsp?.retry) {
      const waitTime = factory.incrementWaitTime(requestId);
      settings.debug &&
        console.warn(
          "Request ID:",
          requestId,
          `Waiting ${waitTime} milliseconds`
        );
      await wait(waitTime);
      return await get(settings);
    }
    factory.remove(requestId);
    return errorRsp;
  }
};

export default get;
