import { ofetch } from "ofetch";
import { trimEnd, trimStart } from "lodash-es";
import ls from "localstorage-slim";
import { stableStringify } from "../utils/object.js";
import { sha256 } from "../utils/hash.js";
import { useRuntimeConfig } from "#imports";
import { useAuthentication } from "../bridges/authentication.js";
const DEFAULT_TTL_SECONDS_FOR_TRUE = 5 * 60;
const CACHE_PREFIX = "api-cache-";
const IDEMPOTENCY_HEADER_DEFAULT = "Idempotency-Key";
let _legacyHeuristicWarned = false;
function warnLegacyHeuristic(value, resolvedSeconds) {
  if (_legacyHeuristicWarned) return;
  _legacyHeuristicWarned = true;
  console.warn(
    `[useApi] cache=${value} resolved to ${resolvedSeconds}s via the legacy heuristic (values <=60 are minutes, >60 are seconds). This is ambiguous \u2014 pass { ttlSeconds: N } or { ttlMs: N } instead. See useApi() docblock for details.`
  );
}
export function _resetLegacyHeuristicWarning() {
  _legacyHeuristicWarned = false;
}
export function resolveCacheTtlSeconds(cache) {
  if (!cache) return 0;
  if (typeof cache === "boolean") return DEFAULT_TTL_SECONDS_FOR_TRUE;
  if (typeof cache === "number") {
    const n = Math.max(0, Number(cache));
    if (!Number.isFinite(n) || n === 0) return 0;
    const resolved = n > 60 ? n : n * 60;
    warnLegacyHeuristic(n, resolved);
    return resolved;
  }
  if (typeof cache === "object") {
    if ("ttlSeconds" in cache) return Math.max(0, Math.floor(Number(cache.ttlSeconds) || 0));
    if ("ttlMs" in cache) return Math.max(0, Math.ceil((Number(cache.ttlMs) || 0) / 1e3));
  }
  return 0;
}
function invalidateCache(prefix) {
  if (typeof window === "undefined" || !window.localStorage) return 0;
  const keys = [];
  for (let i = 0; i < window.localStorage.length; i++) {
    const k = window.localStorage.key(i);
    if (k && k.startsWith(CACHE_PREFIX)) keys.push(k);
  }
  let removed = 0;
  for (const k of keys) {
    if (!prefix) {
      try {
        ls.remove(k);
        removed++;
      } catch {
      }
      continue;
    }
    try {
      const raw = window.localStorage.getItem(k);
      if (raw && raw.includes(prefix)) {
        ls.remove(k);
        removed++;
      }
    } catch {
    }
  }
  return removed;
}
export function useApi() {
  const config = useRuntimeConfig();
  function urlBuilder(url) {
    let returnUrl;
    if (Array.isArray(url)) {
      if (url[0].toLowerCase() === "agent") {
        url[0] = config?.public.WS_AGENT || config?.public.WS_DEVICE;
      }
      returnUrl = url.join("/");
    } else {
      returnUrl = url;
    }
    if (returnUrl.startsWith("http://") || returnUrl.startsWith("https://")) return returnUrl;
    return trimEnd(config?.public.WS_API, "/") + "/" + trimStart(returnUrl, "/");
  }
  async function optionBuilder(method, body, params, options = {}) {
    const headers = {
      "Content-Type": "application/json",
      Accept: "application/json"
    };
    const auth = useAuthentication();
    const token = auth.keycloak?.token || auth.token;
    if (token) {
      headers["Authorization"] = `Bearer ${token}`;
    }
    const callerHeaders = {
      ...options.headers || {}
    };
    const enabledConfig = config?.public?.IDEMPOTENCY_ENABLED;
    const idempotencyEnabled = enabledConfig !== false;
    const headerName = config?.public?.IDEMPOTENCY_HEADER || IDEMPOTENCY_HEADER_DEFAULT;
    const callerProvidedHeader = Object.keys(callerHeaders).some(
      (k) => k.toLowerCase() === headerName.toLowerCase()
    );
    if (method === "POST" && idempotencyEnabled && options.idempotent !== false && !callerProvidedHeader) {
      const username = (typeof auth.getUsername === "function" ? auth.getUsername() : auth.userProfile?.username) ?? "";
      const second = Math.floor(Date.now() / 1e3).toString();
      const bodyJson = stableStringify(body ?? {});
      headers[headerName] = await sha256(`${second}|${username}|${bodyJson}`);
    }
    const { idempotent: _omitIdempotent, ...passThroughOptions } = options;
    const baseOptions = {
      method,
      body,
      query: params,
      headers
    };
    const finalHeaders = {
      ...headers,
      ...callerHeaders
    };
    Object.assign(baseOptions, passThroughOptions);
    return {
      ...baseOptions,
      headers: finalHeaders
    };
  }
  function get(url, body, params, options, cache = false) {
    return getPromise(url, body, params, options, cache);
  }
  function getPromise(url, body, params, options, cache = false) {
    return ofetchWithCache(url, "GET", body, params, options, cache);
  }
  function post(url, body, params, options, cache = false) {
    return postPromise(url, body, params, options, cache);
  }
  function postPromise(url, body, params, options, cache = false) {
    return ofetchWithCache(url, "POST", body, params, options, cache);
  }
  async function hashKey(data) {
    const jsonString = stableStringify(data);
    return sha256(jsonString);
  }
  function safeGetCache(key) {
    try {
      const v = ls.get(key);
      return v ?? null;
    } catch {
      return null;
    }
  }
  async function ofetchWithCache(url, method, body, params, options, cache) {
    const builtUrl = urlBuilder(url);
    const ttl = resolveCacheTtlSeconds(cache);
    if (ttl === 0) {
      return ofetch(builtUrl, await optionBuilder(method, body, params, options));
    }
    const keyData = { url: builtUrl, method, body, params, headers: options?.headers };
    const key = CACHE_PREFIX + await hashKey(keyData);
    const cached = safeGetCache(key);
    if (cached !== null) {
      return cached;
    }
    const result = await ofetch(builtUrl, await optionBuilder(method, body, params, options));
    ls.set(key, result, { ttl });
    return result;
  }
  return { urlBuilder, get, getPromise, post, postPromise, hashKey, invalidate: invalidateCache };
}
