import { getCorrelationId, getSessionId } from "@komi-app/correlation";

import { ApiResponse, ApisauceInstance, create } from "apisauce";

import {
  FEATURE_LIST_HEADER,
  FEATURE_LIST_LOCAL_STORAGE,
} from "constants/feature-flags";

import Cookies from "js-cookie";

import { Response } from "redux/Common/types";
import { KOMI_TALENT_CURRENCY } from "services/UserService";

import { ApiConfig } from "./api.config";

import config from "config";
import { CancelToken } from "axios";
import { getGeneralApiProblem } from "./apiProblem";
import { KOMI_CSRF } from "services/AuthService";
import { TALENT_PROFILE_ID } from "constants/profile";
// abstracted from legacy API class

export default class Api {
  /**
   * The underlying apisauce instance which performs the requests.
   */
  apisauceSecure: ApisauceInstance;

  /**
   * Configurable options.
   */
  config: ApiConfig;

  /**
   * Creates the api.
   *
   * @param config The configuration to use.
   */
  constructor(config: ApiConfig) {
    this.config = config;
    this.apisauceSecure = create({
      baseURL: this.config.url,
      timeout: this.config.timeout,
      headers: {
        Accept: "application/json",
      },
      withCredentials: true,
    });
  }

  /**
   * Get instance of apisauceSecure.
   */
  get instanceSecure(): ApisauceInstance {
    return this.apisauceSecure;
  }

  /**
   * Sets up the API.  This will be called during the bootup
   * sequence and will happen before the first React component
   * is mounted.
   *
   * Be as quick as possible in here.
   */
  setup() {
    this.apisauceSecure.axiosInstance.interceptors.request.use(
      async (options) => {
        const headers: Record<string, string> = {};

        const currencyCode = Cookies.get(KOMI_TALENT_CURRENCY);
        const correlationId = getCorrelationId();
        const sessionId = getSessionId();
        const serviceName = config.service.name || "";
        const serviceVersion = config.service.version || "";
        const featureList = localStorage.getItem(FEATURE_LIST_LOCAL_STORAGE);
        const csrfToken = Cookies.get(KOMI_CSRF);
        const talentProfileId = Cookies.get(TALENT_PROFILE_ID);

        if (currencyCode) {
          headers["local-currency-code"] = currencyCode;
        }

        if (correlationId) {
          headers["x-correlation-id"] = correlationId;
        }
        if (sessionId) {
          headers["x-session-id"] = sessionId;
        }
        if (serviceName) {
          headers["x-service-name"] = serviceName;
        }
        if (serviceVersion) {
          headers["x-service-version"] = serviceVersion;
        }
        if (csrfToken) {
          headers["x-komi-csrf"] = csrfToken;
        }
        if (featureList) {
          headers[FEATURE_LIST_HEADER] = featureList;
        }
        if (talentProfileId) {
          headers["x-profile-id"] = talentProfileId;
        }

        options.headers = {
          ...options.headers,
          ...headers,
        };

        return options;
      },
      (error) => {
        // Do something with request error
        return Promise.reject(error);
      }
    );
  }

  private requestAPI<P, R>(
    url: string,
    method: "get" | "delete" | "delete-payload" | "put" | "post" | "patch",
    cancelToken?: CancelToken
  ) {
    return async (payload?: P): Promise<Response<R>> => {
      let response: ApiResponse<any>;
      switch (method) {
        case "get":
          response = await this.apisauceSecure.get(url, payload, {
            cancelToken,
          });
          break;
        case "post":
          response = await this.apisauceSecure.post(url, payload);
          break;
        case "put":
          response = await this.apisauceSecure.put(url, payload);
          break;
        case "delete":
          response = await this.apisauceSecure.delete(url, payload);
          break;
        case "delete-payload":
          response = await this.apisauceSecure.delete(
            url,
            {},
            { data: payload }
          );
          break;
        case "patch":
          response = await this.apisauceSecure.patch(url, payload);
          break;
        default:
          response = await this.apisauceSecure.get(url, payload);
      }

      if (!response.ok) {
        return getGeneralApiProblem(response);
      }

      return { ok: response.ok, response: response.data };
    };
  }
  async postRequest<P = any | undefined, R = any>(
    url: string,
    payload?: P
  ): Promise<Response<R>> {
    return this.requestAPI<P, R>(url, "post")(payload);
  }

  getRequest = async <P, R = any>(
    url: string,
    payload?: P,
    cancelToken?: CancelToken
  ): Promise<Response<R>> => {
    return this.requestAPI<P, R>(url, "get", cancelToken)(payload);
  };

  async putRequest<P, R = any>(url: string, payload?: P): Promise<Response<R>> {
    return this.requestAPI<P, R>(url, "put")(payload);
  }

  async patchRequest<P, R = any>(
    url: string,
    payload?: P
  ): Promise<Response<R>> {
    return this.requestAPI<P, R>(url, "patch")(payload);
  }

  async deleteRequest<P, R = any>(
    url: string,
    payload?: P
  ): Promise<Response<R>> {
    return this.requestAPI<P, R>(url, "delete")(payload);
  }

  deleteRequestWithPayload<P, R = any>(
    url: string,
    payload?: P
  ): Promise<Response<R>> {
    return this.requestAPI<P, R>(url, "delete-payload")(payload);
  }

  removeAuthToken(): void {
    this.apisauceSecure.deleteHeader("Authorization");
  }
}
