import isNode from 'detect-node';
import axios, { Method, Canceler } from 'axios';
import { toast } from "react-toastify";
import qs from 'query-string';

// eslint-disable-next-line @typescript-eslint/naming-convention
const CancelToken = axios.CancelToken;

interface RequestOptions<Req> {
  method?: Method;
  href?: string;
  host?: string;
  pathname?: string;
  search?: string | Req;
  data?: Req;
  cancelCb?: (cancel: Canceler) => void;
}

export default async function requestJSON<Res, Req extends Record<string, any> = Record<string, any>>(options: RequestOptions<Req> = {}): Promise<Res> {
  if(isNode) return new Promise(() => {});
  let { method, href, host, pathname, search, cancelCb, data } = options;
  
  host = host || location.host;
  pathname = pathname || location.pathname;
  if(search && typeof search !== "string") {
    search = qs.stringify(search);
  }
  search = search ? "?" + search : location.search;
  href = href || `//${host}${pathname}${search}`;
  method = method || "GET";
  
  let response;
  try {
    response = await axios({
      method,
      url: href,
      data,
      cancelToken: cancelCb ? new CancelToken(cancelCb) : undefined,
    });
  } catch(err) {
    if(!(err instanceof axios.Cancel)) toast.error(err.response?.data?._error?.message || err.message);
    throw err;
  }
  
  return response.data;
}