import axios, {
  AxiosError,
  AxiosInstance,
  AxiosResponse,
  InternalAxiosRequestConfig,
} from 'axios';
import { TokenResponse } from '../interfaces/types';

const AUTH_API_URL = process.env.NEXT_PUBLIC_AUTH_API_URL;

interface ExtendedAxiosRequestConfig extends InternalAxiosRequestConfig {
  _retry?: boolean;
}

let isRefreshing = false;
let refreshSubscribers: ((token: string) => void)[] = [];
let temporaryToken: string | null = null;

function subscribeTokenRefresh(cb: (token: string) => void) {
  refreshSubscribers.push(cb);
}

function onRefreshed(token: string) {
  refreshSubscribers.forEach((cb) => cb(token));
  refreshSubscribers = [];
}

export function isTokenExpired(): boolean {
  return !temporaryToken;
}

export function getAccessToken(): string | null {
  return temporaryToken;
}

export function setTemporaryToken(token: string): void {
  temporaryToken = token;
}

export function clearTemporaryToken(): void {
  temporaryToken = null;
}

export function clearAuthData(): void {
  clearTemporaryToken();
}

export async function refreshToken(): Promise<string> {
  const response = await axios.post<TokenResponse>(
    `${AUTH_API_URL}/auth/refresh`,
    {},
    { withCredentials: true }
  );

  const { token } = response.data;
  return token;
}

export function handleTokenRefresh<T = unknown>(
  error: AxiosError,
  originalRequest: InternalAxiosRequestConfig,
  axiosInstance?: AxiosInstance
): Promise<AxiosResponse<T>> {
  const request = originalRequest as ExtendedAxiosRequestConfig;

  if (error.response?.status !== 401 || request._retry) {
    throw error;
  }

  if (
    request.url?.includes('/auth/login') ||
    request.url?.includes('/auth/refresh')
  ) {
    throw error;
  }

  request._retry = true;

  if (isRefreshing) {
    return new Promise<AxiosResponse<T>>((resolve, reject) => {
      subscribeTokenRefresh((token: string) => {
        request.headers.Authorization = `Bearer ${token}`;
        const requestFn = axiosInstance || axios;
        requestFn<T>(request).then(resolve).catch(reject);
      });
    });
  }

  isRefreshing = true;

  return refreshToken()
    .then((token) => {
      if (token) {
        setTemporaryToken(token);
      }
      request.headers.Authorization = `Bearer ${token}`;
      onRefreshed(token);
      isRefreshing = false;
      const requestFn = axiosInstance || axios;
      return requestFn<T>(request);
    })
    .catch((refreshError) => {
      isRefreshing = false;
      refreshSubscribers = [];

      if (globalThis.window !== undefined) {
        const { useAuthStore } = require('../store/useAuthStore');
        useAuthStore.getState().logout();

        globalThis.window.location.href = '/login';
      }

      throw refreshError;
    });
}
