import axios from 'axios';
import { getFailureActionType, getRequestActionType, getSuccessActionType } from '../utils';
import { fetchNewToken, logoutUser } from '../actions/auth';
import { addError } from '../actions/errors';
import { ACCESS_TOKEN_KEY, REFRESH_TOKEN_KEY } from '../constants';
import { FETCH_NEW_TOKEN, FETCH_TOKEN } from '../constants/auth';

const apiMiddleware = store => next => async action => {
  if (!action.api) return next(action);
  const {
    url,
    params,
    data: requestData,
    method = 'GET',
    baseURL = process.env.REACT_APP_API_BASE_URL
  } = action.payload;

  const requestType = getRequestActionType(action.type);
  const successType = getSuccessActionType(action.type);
  const failureType = getFailureActionType(action.type);

  next({ ...action, type: requestType });

  function handleSuccessRequest(data) {
    return next({ ...action, type: successType, payload: data });
  }

  function handleFailureRequest(error, showToast, forceLogout) {
    const errorData = error.response || error.toString();
    next({ ...action, type: failureType, payload: errorData, error: true });
    if (showToast) store.dispatch(addError(errorData));
    if (forceLogout) store.dispatch(logoutUser());
    return Promise.reject(errorData);
  }

  const headers = {};
  if (action.auth) {
    const accessToken = localStorage.getItem(ACCESS_TOKEN_KEY);
    if (!accessToken) return handleFailureRequest('token not found!', false, true);
    headers.Authorization = `Bearer ${accessToken}`;
  }

  delete action.api;
  delete action.auth;
  const request = { baseURL, url, method, data: requestData, params, headers };
  try {
    const { data } = await axios(request);
    return handleSuccessRequest(data);
  } catch (error) {
    if (action.type === FETCH_TOKEN) return handleFailureRequest(error, false, false);
    if (action.type === FETCH_NEW_TOKEN)
      return handleFailureRequest('refresh token expired!', false, true);
    if (!error.response || error.response.status !== 401)
      return handleFailureRequest(error, true, false);

    const refreshToken = localStorage.getItem(REFRESH_TOKEN_KEY);
    if (!refreshToken) return handleFailureRequest('refresh token not found!', false, true);
    const {
      payload: { access: accessToken }
    } = await store.dispatch(fetchNewToken(refreshToken));
    if (!accessToken) return handleFailureRequest('token expired!', false, true);
    request.headers.Authorization = `Bearer ${accessToken}`;
    try {
      const { data } = await axios(request);
      return handleSuccessRequest(data);
    } catch (error) {
      return handleFailureRequest(error, true, false);
    }
  }
};

export default apiMiddleware;
