/**
 * backendUrl.ts
 * 动态解析后端服务地址（端口冲突时自动向后探测可用端口）
 */
const DEFAULT_HOST =
  typeof window !== "undefined" && window.location.hostname
    ? window.location.hostname
    : "127.0.0.1";
const DEFAULT_PORT = 18790;
const MAX_PORT = 18820;
const DEFAULT_URL = `http://${DEFAULT_HOST}:${DEFAULT_PORT}`;
const PROBE_TIMEOUT_MS = 1800;
const PROBE_BATCH_SIZE = 2;
const _cache: {
  url: string;
  resolved: boolean;
  inflight: Promise<string> | null;
} = { url: DEFAULT_URL, resolved: false, inflight: null };

async function probeCandidate(candidate: string): Promise<string | null> {
  for (const endpoint of ["/api/lightweight-status", "/api/ping"]) {
    try {
      const response = await fetch(`${candidate}${endpoint}`, {
        signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),
      });
      if (!response.ok) {
        continue;
      }
      const contentType = response.headers.get("content-type") || "";
      if (!contentType.toLowerCase().includes("application/json")) {
        continue;
      }
      const payload = (await response.json()) as { ok?: unknown };
      if (payload?.ok === true) {
        return candidate;
      }
    } catch {
      // try next lightweight endpoint
    }
  }
  return null;
}

export async function resolveBackendUrl(): Promise<string> {
  if (_cache.resolved) return _cache.url;
  if (_cache.inflight) return _cache.inflight;
  const explicitBase = import.meta.env.VITE_API_BASE?.trim();
  if (explicitBase) {
    _cache.url = explicitBase;
    _cache.resolved = true;
    return _cache.url;
  }

  _cache.inflight = (async () => {
    const sameOrigin =
      typeof window !== "undefined" &&
      /^https?:\/\//.test(window.location.origin || "")
        ? window.location.origin
        : "";
    const sameOriginPort =
      typeof window !== "undefined"
        ? Number.parseInt(window.location.port || "", 10)
        : Number.NaN;
    const shouldProbeSameOrigin =
      sameOrigin &&
      (Number.isNaN(sameOriginPort) || sameOriginPort === DEFAULT_PORT);
    const candidates = new Set<string>();
    // Skip dev-server origins like Vite on 5173, which cannot serve the backend API.
    if (shouldProbeSameOrigin) {
      candidates.add(sameOrigin);
    }
    for (let port = DEFAULT_PORT; port < MAX_PORT; port += 1) {
      candidates.add(`http://${DEFAULT_HOST}:${port}`);
    }

    const orderedCandidates = Array.from(candidates);
    const defaultCandidate = `http://${DEFAULT_HOST}:${DEFAULT_PORT}`;
    if (orderedCandidates.includes(defaultCandidate)) {
      const resolvedDefault = await probeCandidate(defaultCandidate);
      if (resolvedDefault) {
        _cache.url = resolvedDefault;
        _cache.resolved = true;
        return _cache.url;
      }
    }

    for (
      let index = 0;
      index < orderedCandidates.length;
      index += PROBE_BATCH_SIZE
    ) {
      const batch = orderedCandidates
        .slice(index, index + PROBE_BATCH_SIZE)
        .filter((candidate) => candidate !== defaultCandidate);
      if (batch.length === 0) {
        continue;
      }
      const winner = await Promise.any(
        batch.map(async (candidate) => {
          const resolved = await probeCandidate(candidate);
          if (!resolved) {
            throw new Error(`probe failed: ${candidate}`);
          }
          return resolved;
        }),
      ).catch(() => null);
      if (winner) {
        _cache.url = winner;
        _cache.resolved = true;
        return _cache.url;
      }
    }

    return _cache.url;
  })().finally(() => {
    _cache.inflight = null;
  });

  return _cache.inflight;
}
