"use client";

import createClient from "openapi-fetch";
import type { paths } from "../reflector-api";
import createFetchClient from "openapi-react-query";
import { assertExistsAndNonEmptyString } from "./utils";
import { isBuildPhase } from "./next";

export const API_URL = !isBuildPhase
  ? assertExistsAndNonEmptyString(
      process.env.NEXT_PUBLIC_API_URL,
      "NEXT_PUBLIC_API_URL required",
    )
  : "http://localhost";

// TODO decide strict validation or not
export const WEBSOCKET_URL =
  process.env.NEXT_PUBLIC_WEBSOCKET_URL || "ws://127.0.0.1:1250";

export const client = createClient<paths>({
  baseUrl: API_URL,
});

const waitForAuthTokenDefinitivePresenceOrAbscence = async () => {
  let tries = 0;
  let time = 0;
  const STEP = 100;
  while (currentAuthToken === undefined) {
    await new Promise((resolve) => setTimeout(resolve, STEP));
    time += STEP;
    tries++;
    // most likely first try is more than enough, if it's more there's already something weird happens
    if (tries > 10) {
      // even when there's no auth assumed at all, we probably should explicitly call configureApiAuth(null)
      throw new Error(
        `Could not get auth token definitive presence/absence in ${time}ms. not calling configureApiAuth?`,
      );
    }
  }
};

client.use({
  async onRequest({ request }) {
    await waitForAuthTokenDefinitivePresenceOrAbscence();
    if (currentAuthToken) {
      request.headers.set("Authorization", `Bearer ${currentAuthToken}`);
    }
    // XXX Only set Content-Type if not already set (FormData will set its own boundary)
    // This is a work around for uploading file, we're passing a formdata
    // but the content type was still application/json
    if (
      !request.headers.has("Content-Type") &&
      !(request.body instanceof FormData)
    ) {
      request.headers.set("Content-Type", "application/json");
    }
    return request;
  },
});

export const $api = createFetchClient<paths>(client);

let currentAuthToken: string | null | undefined = undefined;

// the function contract: lightweight, idempotent
export const configureApiAuth = (token: string | null | undefined) => {
  // watch only for the initial loading; "reloading" state assumes token presence/absence
  if (token === undefined && currentAuthToken !== undefined) return;
  currentAuthToken = token;
};
