import { toast } from "vue-sonner";
import { crudFormat, crudLabel } from "../crud/style";
import { configureClient, fetchPackagedEpsJson, getClientBaseUrl } from "../../client/configure";
export function apiUrl(path) {
  if (/^https?:\/\//.test(path))
    return path;
  const base = getClientBaseUrl().replace(/\/$/, "");
  const p = path.startsWith("/") ? path : `/${path}`;
  return `${base}${p}`;
}
const TOKEN_KEY = "vome_admin_access";
const REFRESH_KEY = "vome_admin_refresh";
function browserStorage() {
  try {
    if (typeof localStorage === "undefined")
      return null;
    return localStorage;
  } catch {
    return null;
  }
}
export function getAccessToken() {
  return browserStorage()?.getItem(TOKEN_KEY) ?? null;
}
export function setTokens(accessToken, refreshToken) {
  const s = browserStorage();
  if (!s)
    return;
  s.setItem(TOKEN_KEY, accessToken);
  s.setItem(REFRESH_KEY, refreshToken);
}
export function clearTokens() {
  const s = browserStorage();
  if (!s)
    return;
  s.removeItem(TOKEN_KEY);
  s.removeItem(REFRESH_KEY);
}
export function getRefreshToken() {
  return browserStorage()?.getItem(REFRESH_KEY) ?? null;
}
let refreshing = null;
async function tryRefresh() {
  const refreshToken = getRefreshToken();
  if (!refreshToken)
    return false;
  try {
    const res = await fetch(apiUrl("/admin/base/auth/refresh"), {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ refreshToken })
    });
    const json = await res.json();
    if (!res.ok || json.code !== 1000) {
      clearTokens();
      return false;
    }
    setTokens(json.data.accessToken, json.data.refreshToken);
    return true;
  } catch {
    return false;
  }
}
export function formatApiMessage(message) {
  const t = message.trim();
  if (t.startsWith("{") || t.startsWith("[")) {
    try {
      const parsed = JSON.parse(t);
      if (Array.isArray(parsed)) {
        return parsed.map((i) => {
          const path = (i.path || []).join(".");
          const tip = i.message || crudLabel("invalid");
          return path ? `${path}: ${tip}` : tip;
        }).join("；");
      }
      if (parsed && typeof parsed === "object") {
        const o = parsed;
        const parts = [];
        const walk = (err, prefix = "") => {
          if (!err || typeof err !== "object")
            return;
          const e = err;
          if (Array.isArray(e.errors)) {
            for (const nested of e.errors) {
              if (Array.isArray(nested)) {
                for (const item of nested)
                  walk(item, prefix);
              } else {
                walk(nested, prefix);
              }
            }
            return;
          }
          const path = [...prefix ? [prefix] : [], ...e.path || []].join(".");
          if (e.message && e.message !== "Invalid input") {
            parts.push(path ? `${path}: ${e.message}` : e.message);
          }
        };
        if (Array.isArray(o.errors)) {
          for (const err of o.errors)
            walk(err);
        }
        if (parts.length)
          return [...new Set(parts)].join("；");
        if (o.message && o.message !== "Invalid input")
          return o.message;
      }
    } catch {}
  }
  return message;
}
function redirectLogin() {
  if (typeof window === "undefined")
    return;
  if (location.pathname.startsWith("/login"))
    return;
  location.href = `/login?redirect=${encodeURIComponent(location.pathname + location.search)}`;
}
function toFetchBody(body) {
  if (body == null)
    return;
  if (typeof body === "string" || body instanceof Blob || body instanceof FormData || body instanceof URLSearchParams || body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
    return body;
  }
  return JSON.stringify(body);
}
export async function request(path, init) {
  const {
    toast: showToast = true,
    skipRefresh,
    timeoutMs = 5000,
    signal: userSignal,
    ...rest
  } = init || {};
  const { body: rawBody, ...fetchInit } = rest;
  const body = toFetchBody(rawBody);
  const headers = new Headers(fetchInit.headers);
  if (!headers.has("content-type") && rawBody != null && !(rawBody instanceof FormData)) {
    headers.set("content-type", "application/json");
  }
  const token = getAccessToken();
  if (token)
    headers.set("authorization", `Bearer ${token}`);
  try {
    const lang = localStorage.getItem("vome_admin_locale")?.trim();
    if (lang)
      headers.set("X-Lang", lang);
  } catch {}
  const ac = new AbortController;
  let timer;
  const abortTimedOut = () => {
    ac.abort(typeof DOMException !== "undefined" ? new DOMException("signal timed out", "TimeoutError") : new Error("signal timed out"));
  };
  const armTimer = () => {
    if (timeoutMs <= 0)
      return;
    timer = setTimeout(abortTimedOut, timeoutMs);
  };
  const clearTimer = () => {
    if (timer == null)
      return;
    clearTimeout(timer);
    timer = undefined;
  };
  armTimer();
  const signal = userSignal && typeof AbortSignal.any === "function" ? AbortSignal.any([userSignal, ac.signal]) : userSignal || ac.signal;
  let res;
  try {
    res = await fetch(apiUrl(path), { ...fetchInit, body, headers, signal });
  } catch (e) {
    clearTimer();
    throw e;
  }
  clearTimer();
  const ct = res.headers.get("content-type") || "";
  if (ct.includes("text/event-stream")) {
    if (res.status === 401 && !skipRefresh) {
      if (getRefreshToken()) {
        refreshing ??= tryRefresh().finally(() => {
          refreshing = null;
        });
        const ok = await refreshing;
        if (ok) {
          return request(path, { ...init, skipRefresh: true });
        }
      }
      if (getRefreshToken()) {
        const message = crudLabel("networkError");
        if (showToast)
          toast.error(message);
        throw new Error(message);
      }
      clearTokens();
      redirectLogin();
      const err = new Error(crudLabel("loginExpired"));
      err.toasted = true;
      throw err;
    }
    if (!res.ok) {
      const message = crudFormat(crudLabel("streamFail"), res.status);
      if (showToast)
        toast.error(message);
      throw new Error(message);
    }
    return {
      kind: "stream",
      mode: "stream",
      stream: readSseStream(res)
    };
  }
  armTimer();
  let raw;
  try {
    raw = await res.text();
  } finally {
    clearTimer();
  }
  let json = null;
  try {
    json = raw ? JSON.parse(raw) : null;
  } catch {}
  if (json?.code === 1000)
    return json.data;
  const rawMessage = json?.message || raw || crudFormat(crudLabel("requestFail"), res.status);
  const needAuth = !skipRefresh && (res.status === 401 || /token|登录|未授权|鉴权|Unauthorized|unauthorized/i.test(rawMessage));
  if (needAuth) {
    if (getRefreshToken()) {
      refreshing ??= tryRefresh().finally(() => {
        refreshing = null;
      });
      const ok = await refreshing;
      if (ok) {
        return request(path, { ...init, skipRefresh: true });
      }
      if (getRefreshToken()) {
        const err = new Error(crudLabel("networkError"));
        if (showToast)
          toast.error(err.message);
        err.toasted = Boolean(showToast);
        throw err;
      }
    }
    clearTokens();
    redirectLogin();
    const err = new Error(crudLabel("loginExpired"));
    err.toasted = true;
    throw err;
  }
  const message = formatApiMessage(rawMessage);
  if (showToast)
    toast.error(message.slice(0, 200));
  const err = new Error(message);
  err.toasted = Boolean(showToast);
  throw err;
}
export function isAiStreamResult(v) {
  return !!v && typeof v === "object" && v.kind === "stream" && typeof v.stream?.[Symbol.asyncIterator] === "function";
}
async function* readSseStream(res) {
  const reader = res.body?.getReader();
  if (!reader) {
    yield {
      type: "error",
      error: { code: "stream", message: crudLabel("noStream") }
    };
    return;
  }
  const decoder = new TextDecoder;
  let buf = "";
  while (true) {
    const { done, value } = await reader.read();
    if (done)
      break;
    buf += decoder.decode(value, { stream: true });
    const parts = buf.split(`
`);
    buf = parts.pop() ?? "";
    for (const line of parts) {
      const s = line.trim();
      if (!s.startsWith("data:"))
        continue;
      const payload = s.slice(5).trim();
      if (!payload || payload === "[DONE]")
        continue;
      try {
        yield JSON.parse(payload);
      } catch {}
    }
  }
}
export const api = {
  login(body) {
    return request("/admin/base/auth/login", {
      method: "POST",
      body: JSON.stringify(body),
      toast: true
    });
  },
  captcha(params) {
    const q = new URLSearchParams;
    if (params?.width != null)
      q.set("width", String(params.width));
    if (params?.height != null)
      q.set("height", String(params.height));
    if (params?.color)
      q.set("color", params.color);
    const qs = q.toString();
    return request(`/admin/base/open/captcha${qs ? `?${qs}` : ""}`, { toast: false });
  },
  me(init) {
    return request("/admin/base/auth/me", init);
  },
  perms(init) {
    return request("/admin/base/auth/perms", init);
  },
  logout() {
    const refreshToken = getRefreshToken();
    return request("/admin/base/auth/logout", {
      method: "POST",
      body: JSON.stringify({ refreshToken: refreshToken || "" }),
      toast: false
    }).catch(() => null);
  }
};
configureClient({ request, loadStaticEps: fetchPackagedEpsJson });
