/**
 * A basic api request service
 *
 * It will send basic http requests and returns raw results
 */
import type { FetchOptions } from 'ofetch'
import type { $Fetch } from 'nitropack'
import type { AnyJson, AssociativeArray } from '~/types/data'
import { HTTP_METHOD } from '~/types/enum/data'

class ApiRequestService {
  private readonly fetch: $Fetch

  public constructor(fetch: $Fetch) {
    this.fetch = fetch
  }

  /**
   * Send a GET request
   *
   * @param url
   * @param query
   * @param headers
   */
  public async get(
    url: string,
    query: AssociativeArray | null = null,
    headers: AssociativeArray | null = null,
  ) {
    return await this.request(HTTP_METHOD.GET, url, null, query, headers)
  }

  /**
   * Send a POST request
   *
   * @param url
   * @param body
   * @param query
   * @param headers
   */
  public async post(
    url: string,
    body: string | AnyJson | null = null,
    query: AssociativeArray | null = null,
    headers: AssociativeArray | null = null,
  ) {
    return await this.request(HTTP_METHOD.POST, url, body, query, headers)
  }

  /**
   * Send a PUT request
   *
   * @param url
   * @param body
   * @param query
   * @param headers
   */
  public async put(
    url: string,
    body: string | AnyJson | null = null,
    query: AssociativeArray | null = null,
    headers: AssociativeArray | null = null,
  ) {
    return await this.request(HTTP_METHOD.PUT, url, body, query, headers)
  }

  /**
   * Send a DELETE request
   *
   * @param url
   * @param query
   * @param headers
   */
  public async delete(
    url: string,
    query: AssociativeArray | null = null,
    headers: AssociativeArray | null = null,
  ) {
    return await this.request(HTTP_METHOD.DELETE, url, null, query, headers)
  }

  /**
   * Send an http request
   *
   * @param method
   * @param url
   * @param body
   * @param query
   * @param headers
   * @protected
   */
  protected async request(
    method: HTTP_METHOD,
    url: string,
    body: string | AnyJson | null = null,
    query: AssociativeArray | null = null,
    headers: AssociativeArray | null = null,
  ): Promise<Response> {
    const config: FetchOptions = { method }
    if (query) {
      config.query = query
    }
    if (method === HTTP_METHOD.POST || method === HTTP_METHOD.PUT) {
      config.body = body
    }
    if (headers) {
      config.headers = headers
    }

    // @ts-expect-error TODO: solve the type mismatch
    return await this.fetch(url, config)
  }
}

export default ApiRequestService
