/**
 * Turn an unknown thrown value into something an operator can act on.
 *
 * Node reports a refused TCP connection (Postgres or Redis down, wrong port)
 * as an AggregateError whose own `message` is an empty string - the result of
 * the happy-eyeballs IPv4/IPv6 race. Logging `err.message` there produces the
 * useless `{"err":""}`, so fall back to the error code and the nested causes.
 */
export function describeError(err: unknown): string {
  const e = err as { message?: string; code?: string; errors?: unknown[]; cause?: unknown };

  if (e?.message) return e.message;

  if (Array.isArray(e?.errors) && e.errors.length) {
    const inner = e.errors
      .map((x) => (x as { message?: string; code?: string })?.message || (x as { code?: string })?.code)
      .filter(Boolean);
    if (inner.length) return [...new Set(inner)].join('; ');
  }

  if (e?.cause) return describeError(e.cause);
  if (e?.code) return e.code;
  return String(err);
}

/** Common connection failures, translated into the thing to actually check. */
export function connectionHint(err: unknown, target: string): string {
  const text = describeError(err);
  if (/ECONNREFUSED/i.test(text)) return `Nothing is listening at ${target}. Is it running?`;
  if (/ENOTFOUND|EAI_AGAIN/i.test(text)) return `Cannot resolve the host in ${target}. Check the URL for typos.`;
  if (/ETIMEDOUT/i.test(text)) return `Timed out reaching ${target}. Check network access or an IP allow-list.`;
  if (/password authentication failed|SASL|auth/i.test(text)) return `Credentials in ${target} were rejected.`;
  if (/self.signed|certificate|SSL|TLS/i.test(text)) return `TLS failed for ${target}. Managed providers need sslmode=require (Postgres) or rediss:// (Redis).`;
  return text;
}
