import { useGlobal } from './useGlobal';
let theToken: string | null = null;

export const createClient = (baseUrl: string = '/api') => {
  const lsToken =
    window.officerBearerToken ||
    document.body.dataset['officerBearerToken'] ||
    document.body.dataset['bearerToken'] ||
    new URL(window.location.href).searchParams.get('officerToken') ||
    localStorage.getItem('PERTENTO_EDITOR_AUTH_TOKEN') ||
    localStorage.getItem('BEARER_TOKEN') ||
    sessionStorage.getItem('BEARER_TOKEN');

  theToken = lsToken;

  return {
    baseUrl,
    token: theToken,
    get: <T>(url: string) => get<T>(url, baseUrl),
    getText: (url: string) => getText(url, baseUrl),
    getBlob: (url: string) => getBlob(url, baseUrl),
    // getStream: (url) => getStream(url, baseUrl),
    post: <T>(url: string, payload?: any) => post<T>(url, payload, baseUrl),
    put: <T>(url: string, payload?: any) => put<T>(url, payload, baseUrl),
    patch: <T>(url: string, payload?: any) => patch<T>(url, payload, baseUrl),
    delete: <T>(url: string, payload?: any) => DELETE<T>(url, payload, baseUrl),
  };
};

export const useClient = (baseUrl: string = '/api') => {
  const [apiError, setApiError] = useGlobal<ErrorDetails | null>('API_ERROR', null);
  useClient.config.onError = (error) => {
    setApiError(error);
  };

  const client = createClient(baseUrl);
  return { ...client, apiError, setApiError };
};

export const getHeaders = (isText: boolean = false) => {
  const headers: Record<string, string> = {};

  if (theToken) {
    headers['Authorization'] = `Bearer ${theToken}`;
  }

  headers['Content-Type'] = isText ? 'text/plain' : 'application/json';

  if (isText) {
    headers['Accept'] = 'text/plain';
  }

  return headers;
};

export const getText = async (uri: string, baseUrl = '') => {
  const headers = getHeaders(true);
  const theUrl = baseUrl ? `${baseUrl}/${uri.replace(/^\//, '')}` : uri;
  const res = await fetch(theUrl, { headers });
  await validateResponse(res);
  const text = await res.text();
  return text;
};

export const get = async <T>(uri: string, baseUrl = '') => {
  const headers = getHeaders();
  const theUrl = baseUrl ? `${baseUrl}/${uri.replace(/^\//, '')}` : uri;
  const res = await fetch(theUrl, { headers });
  await validateResponse(res);
  const data = await res.json();
  return data as T;
};

export const getBlob = async (uri: string, baseUrl = '') => {
  const headers = { Authorization: `Bearer ${theToken!}` };
  const theUrl = baseUrl ? `${baseUrl}/${uri.replace(/^\//, '')}` : uri;
  const res = await fetch(theUrl, { headers });
  await validateResponse(res);
  const blob = await res.blob();
  return blob;
};

// export const getStream = (uri: string, baseUrl = '') => {
//   const headers = getHeaders() as EventSourceInit;
//   const theUrl = baseUrl ? `${baseUrl}/${uri.replace(/^\//, '')}` : uri;
//   const stream = new EventSource(theUrl, { headers });
//   return stream;
// };

export const post = async <T>(uri: string, payload?: any, baseUrl = '') => {
  const headers: Record<string, string> = getHeaders();
  const theUrl = baseUrl ? `${baseUrl}/${uri.replace(/^\//, '')}` : uri;
  const body = payload instanceof FormData ? payload : JSON.stringify(payload);
  if (payload instanceof FormData) {
    delete headers['Content-Type'];
  }
  const res = await fetch(theUrl, {
    method: 'post',
    body,
    headers,
  });
  await validateResponse(res);
  const data = await res.json();
  return data as T;
};

export const put = async <T>(uri: string, payload?: any, baseUrl = '') => {
  const headers: any = getHeaders();
  const theUrl = baseUrl ? `${baseUrl}/${uri.replace(/^\//, '')}` : uri;
  const body = payload instanceof FormData ? payload : JSON.stringify(payload);

  if (payload instanceof FormData) {
    delete headers['Content-Type'];
  }

  const res = await fetch(theUrl, {
    method: 'put',
    body: body,
    headers,
  });
  await validateResponse(res);
  const data = await res.json();
  return data as T;
};

export const patch = async <T>(uri: string, payload?: any, baseUrl = '') => {
  const headers = getHeaders();
  const theUrl = baseUrl ? `${baseUrl}/${uri.replace(/^\//, '')}` : uri;
  const res = await fetch(theUrl, {
    method: 'PATCH',
    body: JSON.stringify(payload),
    headers,
  });
  await validateResponse(res);
  const data = await res.json();
  return data as T;
};

export const DELETE = async <T>(uri: string, payload?: any, baseUrl = '') => {
  const headers = getHeaders();
  const theUrl = baseUrl ? `${baseUrl}/${uri.replace(/^\//, '')}` : uri;
  const res = await fetch(theUrl, {
    method: 'delete',
    body: JSON.stringify(payload),
    headers,
  });
  await validateResponse(res);
  const data = await res.json();
  return data as T;
};

const validateResponse = async (res: Response) => {
  if (res.status >= 400) {
    const { onError } = useClient.config;
    const message = await res.text();
    if (onError) {
      onError({ status: res.status, message });
    }
    throw { status: res.status, message };
  }
};

export interface ErrorDetails {
  status: number;
  message: string;
}

interface UseClientConfig {
  onError: (error: ErrorDetails) => void;
}

// Assign the interface to the config property of useClient
useClient.config = {
  onError: () => null,
} as UseClientConfig;
