export function toSafeString(v: unknown, fallback = ''): string {
  if (v == null) return fallback;
  if (typeof v === 'string') return v;
  if (
    typeof v === 'number' ||
    typeof v === 'boolean' ||
    typeof v === 'bigint'
  ) {
    return String(v);
  }
  if (v instanceof Date) return v.toISOString();
  return fallback;
}

export function toSafeStringOrNull(v: unknown): string | null {
  const normalized = toSafeString(v, '').trim();
  return normalized.length ? normalized : null;
}

export function toSafeIsoDateOrNull(v: unknown): string | null {
  if (v == null) return null;
  if (v instanceof Date) return v.toISOString();
  if (typeof v === 'string') return v;
  return null;
}

export function toSafeNumberOrNull(v: unknown): number | null {
  if (v == null) return null;
  if (typeof v === 'number' && Number.isFinite(v)) return v;
  if (typeof v === 'string') {
    const parsed = Number(v);
    return Number.isFinite(parsed) ? parsed : null;
  }
  return null;
}

export function toSafeIntOrNull(v: unknown): number | null {
  const parsed = toSafeNumberOrNull(v);
  if (parsed == null) return null;
  return Math.trunc(parsed);
}

export function toSafeBooleanOrNull(v: unknown): boolean | null {
  if (v == null) return null;
  if (typeof v === 'boolean') return v;
  if (typeof v === 'number') return v !== 0;
  if (typeof v === 'string') {
    const normalized = v.trim().toLowerCase();
    if (normalized === 'true' || normalized === '1') return true;
    if (normalized === 'false' || normalized === '0') return false;
  }
  return null;
}
