import { throwException } from "@/lib/error";
import qs from "qs";

/**
 * Get full Strapi URL from path
 * @param path - Path of the URL
 * @returns Full Strapi URL
 */
export function getStrapiURL(path = ""): string {
  const apiUrl = process.env.STRAPI_API ?? "http://localhost:1337";
  return `${apiUrl}${path}`;
}

/**
 * Helper to make GET requests to Strapi API endpoints
 * @param path - Path of the API route
 * @param params - URL params object, will be stringified
 * @param options - Options passed to fetch

 * @returns Parsed API call response
 */

async function StrapiFetcher<TResult>(
  path: string,
  params = {},
  options: { "x-account"?: string; "x-contact"?: string } & Record<
    string,
    unknown
  >,
): Promise<TResult> {
  try {
    const token = process.env.STRAPI_API_KEY;
    // Merge default and user options
    const {
      "x-account": accountID,
      "x-contact": contactID,
      ...restOptions
    } = options;
    const mergedOptions = {
      next: { revalidate: 60 },

      headers: {
        Authorization: `Bearer ${token}`,
        "Content-Type": "application/json",
        "x-account": accountID ?? "",
        "x-contact": contactID ?? "",
      },
      ...restOptions,
    };
    if (options.body instanceof FormData) {
      const { "Content-Type": _, ...restHeaders } = mergedOptions.headers;
      // @ts-expect-error NOTE: quick workaround
      mergedOptions.headers = { ...restHeaders };
    }

    // Build request URL
    const queryString = qs.stringify({ ...params });
    const requestUrl = `${getStrapiURL(
      `/api${path}${queryString ? `?${queryString}` : ""}`,
    )}`;

    // Trigger API call
    const response = await fetch(requestUrl, mergedOptions);
    if (options.method === "DELETE") {
      return {} as TResult;
    }
    if (!response.ok) {
      const error = (await response.json()) as {
        status: number;
        message: string;
      };
      throwException(error);
    }

    return (await response.json()) as TResult;
  } catch (error) {
    throwException(error);
    return {} as TResult;
  }
}

export async function StrapiGet<TResponse, TParams>(
  path: string,
  params?: TParams,
  options = {},
): Promise<TResponse> {
  return StrapiFetcher(
    path,
    { ...params, status: "published" },
    {
      method: "GET",
      ...options,
    },
  );
}

export async function StrapiPost<TResponse, TParams>(
  path: string,
  params?: TParams,
  body = {},
  options = {},
): Promise<TResponse> {
  return StrapiFetcher(path, params ?? {}, {
    method: "POST",
    body: JSON.stringify(body),
    ...options,
  });
}

export async function StrapiPut<TResponse, TParams>(
  path: string,
  params?: TParams,
  body = {},
  options = {},
): Promise<TResponse> {
  return StrapiFetcher(path, params ?? {}, {
    method: "PUT",
    body: JSON.stringify(body),
    ...options,
  });
}

export async function StrapiDelete<TResponse, TParams>(
  path: string,
  params?: TParams,
  options = {},
): Promise<TResponse> {
  return StrapiFetcher(path, params ?? {}, {
    method: "DELETE",
    ...options,
  });
}

export async function StrapiFileUpload<TResponse>(
  path: string,
  file: File,
  fileName: string,
  options = {},
): Promise<TResponse> {
  const formData = new FormData();
  formData.append("files", file, fileName);
  return StrapiFetcher(
    path,
    {},
    {
      method: "POST",
      body: formData,
      ...options,
    },
  );
}

export async function StrapiMultipleFileUpload<TResponse>(
  path: string,
  files: File[],
  options = {},
): Promise<TResponse> {
  const formData = new FormData();
  files.forEach((file) => {
    formData.append("files", file, file.name);
  });
  return StrapiFetcher(
    path,
    {},
    {
      method: "POST",
      body: formData,
      ...options,
    },
  );
}
