import { ofetch } from 'ofetch'

export interface RequestOptions<T> extends RequestInit {
  onErrorOccured?: (error: Error, statusCode: number) => void
  onSuccess?: (data: T, statusCode: number) => void
  onStart?: () => void
}

/**
 * @function useMyFetch
 *
 * @description
 * Makes a request to a given url, handles cookies and errors.
 *
 * @param {string} url - The url to make a request to.
 * @param {RequestOptions} [options] - The options to use for the request.
 *
 * @returns {Promise<T | undefined>} - A promise resolving to the response data
 *  or undefined if an error occurred.
 *
 * @example
 * const { data } = useMyFetch<{ name: string }>('/api/user')
 */
export const useMyFetch = async <T>(
  url: string,
  options?: RequestOptions<T>,
): Promise<T> => {
  if (options?.onStart) options.onStart()
  let response = null
  try {
    const event = useRequestEvent()

    if (options == null) options = {}

    options.credentials = 'include'

    if (import.meta.server) {
      const api_uri
        = event?.node.req.headers['api-uri'] || 'http://localhost:4000'
      url = api_uri + url
      options.headers = event?.headers
    }

    response = await ofetch.raw(url, options)
    if (import.meta.server && !event?.handled) {
      for (const cookie of response.headers.getSetCookie()) {
        event?.headers.set('Cookie', cookie)
        event?.node.res.setHeader('set-cookie', cookie)
      }
    }

    // handle errors if any
    if (!response.ok && typeof options.onErrorOccured == 'function') {
      options.onErrorOccured(new Error(response.statusText), response.status)
    }

    // handle success to be able clearly marked that request has finished
    if (response.ok && typeof options.onSuccess == 'function') {
      options.onSuccess(response._data, response.status)
    }

    return response._data as T
  }
  catch (e) {
    // handle errors if any
    if (typeof options?.onErrorOccured == 'function') {
      options.onErrorOccured(e as Error, response?.status || 500)
    }
    else {
      console.error(e)
    }
    return {} as T
  }
}
