/**
 * 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
   */
  public async get(url: string, query: AssociativeArray | null = null) {
    return await this.request(HTTP_METHOD.GET, url, null, query)
  }

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

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

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

  /**
   * 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
    }

    config.headers = headers ? { ...headers } : {}

    config.headers.Accept = 'application/ld+json'
    config.headers['Content-Type'] = 'application/ld+json'

    if (
      method === HTTP_METHOD.POST ||
      method === HTTP_METHOD.PUT ||
      method === HTTP_METHOD.PATCH
    ) {
      config.body = body
    }

    if (method === HTTP_METHOD.PATCH) {
      // config.headers['Accept'] = 'application/merge-patch+json'
      config.headers['Content-Type'] = 'application/merge-patch+json'
    }

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

export default ApiRequestService
