import { joinURL } from 'ufo';
import { createAxios } from '../utils';

async function renewRefreshToken(refreshToken, { authServicePrefix, serviceHost }) {
  if (!refreshToken) {
    throw new Error('Refresh token not found');
  }
  const refreshApi = createAxios({
    baseURL: serviceHost,
    timeout: 10 * 1000,
    secure: true,
    headers: {
      authorization: `Bearer ${encodeURIComponent(refreshToken)}`,
    },
  });
  const { data } = await refreshApi.post(joinURL(authServicePrefix, '/refreshSession'));
  return data;
}

export default function createService(
  {
    refreshTokenStorage,
    sessionTokenStorage,
    serviceHost,
    authServicePrefix,
    onRefreshTokenError,
    onRefreshTokenSuccess,
  },
  options = {}
) {
  let refreshingTokenRequest = null;
  const service = createAxios({
    baseURL: serviceHost,
    timeout: 30 * 1000,
    ...options,
  });
  const { getToken: getSessionToken, setToken: setSessionToken, removeToken: removeSessionToken } = sessionTokenStorage;
  const { getToken: getRefreshToken, setToken: setRefreshToken, removeToken: removeRefreshToken } = refreshTokenStorage;

  service.interceptors.request.use(
    async (config) => {
      if (sessionTokenStorage.engine === 'ls') {
        const token = getSessionToken();
        if (token) {
          config.headers.authorization = `Bearer ${encodeURIComponent(token)}`;
        }
      }
      if (refreshingTokenRequest) {
        await refreshingTokenRequest;
      }

      return config;
    },
    (error) => Promise.reject(error)
  );

  service.interceptors.response.use(
    (response) => response,
    async (error) => {
      const originalRequest = error.config;
      if (!originalRequest) {
        return Promise.reject(error);
      }

      originalRequest.headers = { ...originalRequest?.headers };
      // FIXME: @zhanghan 在某次 createTokenFn 的时候，出现了 401 导致 did-connect 页面展示了 `Refresh token not found` 错误
      if (error?.response?.status === 401 && !originalRequest._retry) {
        originalRequest._retry = true;
        if (!refreshingTokenRequest) {
          refreshingTokenRequest = renewRefreshToken(getRefreshToken(), { serviceHost, authServicePrefix });
        }
        try {
          const tokenData = await refreshingTokenRequest;
          setSessionToken(tokenData.nextToken);
          setRefreshToken(tokenData.nextRefreshToken);
          if (typeof onRefreshTokenSuccess === 'function') {
            onRefreshTokenSuccess(tokenData);
          }
          return service(originalRequest);
        } catch (error2) {
          removeSessionToken();
          removeRefreshToken();
          if (typeof onRefreshTokenError === 'function') {
            onRefreshTokenError();
          }
          return Promise.reject(error2);
        } finally {
          refreshingTokenRequest = null;
        }
      }
      return Promise.reject(error);
    }
  );

  return service;
}
