import { AuthCredentials } from "auth/domain/authEntity";
import axios, { AxiosError, AxiosRequestConfig } from "axios";
import { AuthDTO } from "./dto";

const BASE_URL = process.env.REACT_APP_INTEGRATOR_URL;
export const GRAPHQL_URL = process.env.REACT_APP_GRAPHQL_URL ?? "";

const instance = axios.create({
  baseURL: BASE_URL + (process.env.REACT_APP_INTEGRATOR_API_VERSION ?? ""),
});

export const graphQlConfig: AxiosRequestConfig = {
  baseURL: BASE_URL + (GRAPHQL_URL ?? ""),
  headers: {
    "X-Activity-Trace-Method": "GRAPHQL",
  },
};

const integratorApiClient = axios.create({
  baseURL: BASE_URL + (process.env.REACT_APP_INTEGRATOR_API_VERSION ?? ""),
});

integratorApiClient.interceptors.request.use(
  async (config) => {
    const [access] = getTokens();
    config.headers = Object.assign({}, config.headers, {
      Authorization: `Bearer ${access}`,
      Accept: "application/json",
      "Content-Type": "application/json",
    });
    return config;
  },
  (error) => {
    Promise.reject(error);
  }
);

integratorApiClient.interceptors.response.use(
  (response) => {
    return response;
  },
  async function (error: AxiosError) {
    const originalRequest: AxiosRequestConfig & { _retry?: boolean } =
      error.config;
    if (error.response?.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;
      try {
        await refresh();
      } catch (e) {
        const replacement: AxiosError = {
          ...error,
          response: {
            ...error.response,
            status: 401,
          },
        };
        return Promise.reject(replacement);
      }
      return integratorApiClient(originalRequest);
    }
    return Promise.reject(error);
  }
);
/**
 * Returns user authoriztion tokens
 * @returns {[string, string]}
 */
function getTokens() {
  return [
    localStorage.getItem("accessToken"),
    localStorage.getItem("refreshToken"),
  ];
}

/**
 * Stores user authentication tokens
 * @param accessToken User authentication access token
 * @param refreshToken User authentication refresh token
 */
function setTokens(accessToken: string, refreshToken: string) {
  localStorage.setItem("accessToken", accessToken);
  localStorage.setItem("refreshToken", refreshToken);
}

/**
 * Drops authentication tokens from storage
 */
function dropTokens() {
  localStorage.removeItem("accessToken");
  localStorage.removeItem("refreshToken");
}

/**
 * User authorisation service call
 * @returns Promised authentication credentials information
 */
function auth(code: string): Promise<AuthCredentials> {
  return new Promise((resolve, reject) => {
    instance
      .post<AuthDTO>("/account/login", {
        code: code,
      })
      .then((response) => {
        const content = response.data;
        setTokens(content.data.access_token, content.data.refresh_token);
        resolve({
          accessToken: content.data.access_token,
          refreshToken: content.data.refresh_token,
        });
      })
      .catch((reason) => {
        reject(reason);
      });
  });
}
/**
 * Terminates user session
 * @returns
 */
async function signout(): Promise<void> {
  const refresh = getTokens()[1];
  if (!refresh) {
    dropTokens();
  }
  await instance.post("/account/logout", {
    refresh_token: refresh,
  });
}
/**
 * User authorisation refresh service call
 * @returns Promised authentication credentials information
 */
function refresh(): Promise<AuthCredentials> {
  const [access, refresh] = getTokens();
  return new Promise((resolve, reject) => {
    instance
      .post<AuthDTO>(
        "/account/refresh-token",
        {
          client_id: "techpal",
          refresh_token: refresh,
          grant_type: "refresh_token",
        },
        {
          headers: {
            Authorization: `Bearer: ${access}`,
          },
        }
      )
      .then((response) => {
        const content = response.data;
        setTokens(content.data.access_token, content.data.refresh_token);
        resolve({
          accessToken: content.data.access_token,
          refreshToken: content.data.refresh_token,
        });
      })
      .catch((reason) => {
        reject(reason);
      });
  });
}

export {
  getTokens,
  setTokens,
  dropTokens,
  auth,
  refresh,
  signout,
  integratorApiClient,
};
