/**
 * Global registry for deduplicating warp markers objects.
 * Ensures clips with identical downbeat analysis share the same object reference.
 */
import md5 from 'md5';

export type WarpMarkers = Readonly<Record<number, number>>;

// Global registry mapping hash -> canonical markers object
const warpMarkersRegistry = new Map<string, WarpMarkers>();

// Cache for markers -> hash mapping to avoid recomputing hashes
const markersToHashCache = new WeakMap<WarpMarkers, string>();

export function hashWarpMarkers(markers: WarpMarkers): string {
  const cached = markersToHashCache.get(markers);
  if (cached) return cached;

  // Sort entries by key for deterministic hash
  const sortedEntries = Object.entries(markers)
    .sort(([a], [b]) => Number(a) - Number(b))
    .map(([key, value]) => `${key}:${value}`)
    .join(',');

  const hash = `warp_${md5(sortedEntries)}`;
  markersToHashCache.set(markers, hash);
  return hash;
}

/**
 * Get or create a deduplicated warp markers object.
 * Returns the canonical instance for markers with identical content.
 */
export function deduplicateWarpMarkers(markers: WarpMarkers): WarpMarkers {
  const hash = hashWarpMarkers(markers); // hash even if empty, to ensure markersToHashCache is populated

  const keys = Object.keys(markers);
  if (keys.length === 0) {
    return emptyMarkers;
  }

  const existing = warpMarkersRegistry.get(hash);
  if (existing) {
    return existing;
  }

  const canonicalMarkers = Object.freeze({ ...markers });
  warpMarkersRegistry.set(hash, canonicalMarkers);

  return canonicalMarkers;
}

const emptyMarkers: WarpMarkers = Object.freeze({});

/**
 * Check if two markers objects are referentially equal (deduplicated)
 */
export function areMarkersEqual(
  a: WarpMarkers | null,
  b: WarpMarkers | null
): boolean {
  return a === b;
}

export function getRegistryStats() {
  return {
    uniqueMarkers: warpMarkersRegistry.size,
    contents: warpMarkersRegistry.entries(),
  };
}

/**
 * Deduplicate all markers in an array and return mapping of old -> new
 */
export function deduplicateMarkersArray(markersArray: WarpMarkers[]): {
  deduplicated: WarpMarkers[];
  mapping: Map<WarpMarkers, WarpMarkers>;
  hashMapping: Map<WarpMarkers, string>;
} {
  const mapping = new Map<WarpMarkers, WarpMarkers>();
  const hashMapping = new Map<WarpMarkers, string>();
  const deduplicatedSet: Set<WarpMarkers> = new Set();

  for (const markers of markersArray) {
    const canonical = deduplicateWarpMarkers(markers);
    const hash = markersToHashCache.get(markers)!;
    hashMapping.set(markers, hash);
    hashMapping.set(canonical, hash);
    mapping.set(markers, canonical);
    deduplicatedSet.add(canonical);
  }

  return { deduplicated: [...deduplicatedSet], mapping, hashMapping };
}
