import useSWR, { type SWRConfiguration } from "swr";
import { Failure, fromPromise, Success } from "./async-op.ts";
import { swrResponseToResult } from "./result/swr.ts";
import type { FailurePayload } from "./types.ts";

export type DocContentTypeMismatch = {
  type: "DOC_CONTENT_TYPE_MISMATCH";
};

type FetchJsonResult<T> =
  | Success<T>
  | Failure<FailurePayload | DocContentTypeMismatch>;

export async function fetchJson<T>(url: string): Promise<FetchJsonResult<T>> {
  const result = await fromPromise(() => fetch(url));

  if (result.isFailure) {
    return new Failure({ type: "NETWORK_ERROR" });
  }

  const { value: response } = result;
  if (!response.ok) {
    return new Failure({ type: "API_ERROR", code: response.status });
  }

  // This can happen due to Netlify rewrites serving index.html in case the
  // requested doc file is not found.
  if (response.headers.get("Content-Type") !== "application/json") {
    return new Failure({ type: "DOC_CONTENT_TYPE_MISMATCH" });
  }

  const parsed = await fromPromise(() => response.json());
  if (parsed.isFailure) {
    return new Failure({ type: "UNKNOWN_ERROR" });
  }

  return parsed;
}

/**
 * Fetches a JSON document at provided URL. Note that the return type can be
 * explicitly provided, but it is not validated at runtime — beware!
 */
export function useFetchJson<T = unknown>(
  url: string,
  config?: SWRConfiguration,
) {
  return useSWR<FetchJsonResult<T>>(url, fetchJson, config);
}

/**
 * Fetches a JSON document at provided URL. Response will be wrapped in a
 * Result. Note that the return type can be explicitly provided, but it is
 * not validated at runtime — beware!
 */
export function useFetchJsonResult<T = unknown>(
  url: string,
  config?: SWRConfiguration,
) {
  return swrResponseToResult(useFetchJson<T>(url, config));
}
