export type NetworkFailureCategory =
  | "timeout"
  | "dns"
  | "tls"
  | "connection"
  | "aborted"
  | "network";

/** Classify transport failures without retaining messages that may contain a URL. */
export function classifyNetworkFailure(error: unknown): NetworkFailureCategory {
  const chain = errorChain(error);
  const names = chain.map((candidate) => candidate.name);
  const codes = chain.flatMap((candidate) =>
    typeof candidate.code === "string" ? [candidate.code.toUpperCase()] : []
  );
  if (names.includes("TimeoutError") || codes.some((code) => code === "ETIMEDOUT")) return "timeout";
  if (names.includes("AbortError") || codes.some((code) => code === "ABORT_ERR")) return "aborted";
  if (codes.some((code) => code === "ENOTFOUND" || code === "EAI_AGAIN")) return "dns";
  if (codes.some((code) => code.includes("TLS") || code.includes("CERT") || code.includes("SSL"))) return "tls";
  if (codes.some((code) => ["ECONNREFUSED", "ECONNRESET", "EHOSTUNREACH", "ENETUNREACH", "EPIPE"].includes(code))) {
    return "connection";
  }
  return "network";
}

function errorChain(error: unknown): Array<{ readonly name: string; readonly code?: unknown }> {
  const chain: Array<{ readonly name: string; readonly code?: unknown }> = [];
  let candidate = error;
  for (let depth = 0; depth < 4; depth += 1) {
    if (typeof candidate !== "object" || candidate === null) break;
    const record = candidate as { readonly name?: unknown; readonly code?: unknown; readonly cause?: unknown };
    chain.push({
      name: typeof record.name === "string" ? record.name : "Error",
      ...(record.code !== undefined ? { code: record.code } : {}),
    });
    candidate = record.cause;
  }
  return chain;
}
