import axios from 'axios';
import type { AxiosError, AxiosRequestConfig } from 'axios';
import { RequestOptions } from '@/services/http/base';

import { redirectToSSO } from '@/lib/auth-helper';

const BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL ?? '';
const axiosClient = axios.create({ baseURL: BASE_URL, timeout: 300_000 });

axiosClient.interceptors.response.use(
  (response) => response,
  (error: AxiosError) => {
    if (
      error.response?.status === 401 &&
      error.config?.url !== '/sso/refresh'
    ) {
      redirectToSSO();
    }

    return Promise.reject(error);
  }
);

export async function httpClient<T, B = unknown>(
  path: string,
  opts: RequestOptions<B> = {}
): Promise<T> {
  const isCookieAuth =
    opts.auth === 'cookie' ||
    (!opts.auth && opts.auth !== 'none' && opts.auth !== 'bearer');
  const isBearerAuth = opts.auth === 'bearer' && !!opts.token;

  const isFormData =
    typeof FormData !== 'undefined' && opts.body instanceof FormData;

  const config: AxiosRequestConfig = {
    url: path,
    method: opts.method ?? 'GET',
    params: opts.query,
    data: opts.body,
    timeout: opts.timeoutMs ?? 300_000,
    withCredentials: isCookieAuth && !isBearerAuth,
    responseType: opts.responseType,
    headers: {
      ...(isFormData ? {} : { 'Content-Type': 'application/json' }),
      ...(opts.headers ?? {}),
      ...(isBearerAuth && !isCookieAuth
        ? { Authorization: `Bearer ${opts.token}` }
        : {}),
    },
  };

  try {
    const res = await axiosClient.request<T>(config);
    return res.data;
  } catch (e: unknown) {
    throw e;
  }
}

export type SWRHttpKey<B = unknown> =
  | string
  | [path: string, opts?: RequestOptions<B>]
  | { path: string; opts?: RequestOptions<B> };

export async function httpClientFetcher<T = unknown, B = unknown>(
  key: SWRHttpKey<B>
): Promise<T> {
  if (!key) throw new Error('Invalid SWR key');

  let path: string;
  let opts: RequestOptions<B> | undefined;

  if (typeof key === 'string') {
    path = key;
  } else if (Array.isArray(key)) {
    [path, opts] = key;
  } else {
    ({ path, opts } = key);
  }

  return httpClient<T, B>(path, opts);
}
