import { createHash } from 'crypto';

function sortValue(value: unknown): unknown {
  if (Array.isArray(value)) {
    return value.map((item) => sortValue(item));
  }
  if (value && typeof value === 'object') {
    const out: Record<string, unknown> = {};
    for (const key of Object.keys(value as Record<string, unknown>).sort()) {
      const normalized = normalizeOptional((value as Record<string, unknown>)[key]);
      if (normalized !== undefined) {
        out[key] = sortValue(normalized);
      }
    }
    return out;
  }
  if (typeof value === 'number') {
    if (Number.isNaN(value) || !Number.isFinite(value)) {
      return 0;
    }
    // Keep integer-like values stable.
    return Number.isInteger(value) ? value : Number(value.toFixed(6));
  }
  return value;
}

export function normalizeOptional<T>(value: T): T | undefined {
  if (value === undefined || value === null) {
    return undefined;
  }
  if (Array.isArray(value) && value.length === 0) {
    return undefined;
  }
  if (typeof value === 'string' && value.trim() === '') {
    return undefined;
  }
  return value;
}

export function canonicalStringify(value: unknown): string {
  return JSON.stringify(sortValue(value));
}

export function hashCanonical(value: unknown): string {
  return createHash('sha256').update(canonicalStringify(value)).digest('hex');
}
