/**
 * Utility functions for performing "smart" updates on arrays
 */

/**
 * Creates a stable content hash for comparison
 */
function hashContent(obj: any): string {
  if (obj === null || obj === undefined) return 'null';
  if (typeof obj !== 'object') return String(obj);
  
  // Handle arrays
  if (Array.isArray(obj)) {
    return `[${obj.map(hashContent).join(',')}]`;
  }
  
  // Sort keys for stable hashing (CRITICAL for nested objects!)
  const sortedKeys = Object.keys(obj).sort();
  const parts = sortedKeys.map(key => `${key}:${hashContent(obj[key])}`);
  return parts.join('|');
}

/**
 * Checks if content has meaningfully changed (not just object reference)
 */
function hasContentChanged(current: any, next: any): boolean {
  return hashContent(current) !== hashContent(next);
}

export interface SmartMergeOptions<T> {
  compareFields?: (keyof T)[];
  preserveOrder?: boolean;
  addToTop?: boolean;
}

export interface MergeChanges<T> {
  added: T[];
  removed: T[];
  updated: T[];
}

export interface SmartMergeResult<T> {
  items: T[];
  changes: MergeChanges<T>;
  hasChanges: boolean;
}

/**
 * Performs intelligent array diffing and merging
 * 
 * @param currentItems - The current items in state
 * @param newItems - The new items fetched from the server
 * @param options - Configuration options
 * @returns Merged items with change information
 */
export function smartMergeArray<T extends { id: string }>(
  currentItems: T[],
  newItems: T[],
  options?: SmartMergeOptions<T>
): SmartMergeResult<T> {
  const {
    compareFields,
    preserveOrder = false,
    addToTop = true,
  } = options || {};

  // Create ID maps for quick lookup
  const currentMap = new Map(currentItems.map(item => [item.id, item]));
  const newMap = new Map(newItems.map(item => [item.id, item]));

  // Detect changes
  const added: T[] = [];
  const removed: T[] = [];
  const updated: T[] = [];

  // Find added and updated items
  for (const newItem of newItems) {
    const currentItem = currentMap.get(newItem.id);
    
    if (!currentItem) {
      // New item
      added.push(newItem);
    } else if (hasItemChanged(currentItem, newItem, compareFields)) {
      // Item has changed
      updated.push(newItem);
    }
  }

  // Find removed items
  for (const currentItem of currentItems) {
    if (!newMap.has(currentItem.id)) {
      removed.push(currentItem);
    }
  }

  const hasChanges = added.length > 0 || removed.length > 0 || updated.length > 0;

  // If no changes, return current items (preserves object references)
  if (!hasChanges) {
    return {
      items: currentItems,
      changes: { added: [], removed: [], updated: [] },
      hasChanges: false,
    };
  }

  // Build merged array
  let mergedItems: T[];

  if (preserveOrder) {
    // Preserve the order of current items
    mergedItems = currentItems
      .filter(item => !removed.some(r => r.id === item.id))
      .map(item => {
        const updatedItem = updated.find(u => u.id === item.id);
        return updatedItem || item; // Use updated version if available, otherwise keep current
      });

    // Add new items at top or bottom
    if (addToTop) {
      mergedItems = [...added, ...mergedItems];
    } else {
      mergedItems = [...mergedItems, ...added];
    }
  } else {
    // Use the order from newItems
    mergedItems = newItems.map(newItem => {
      const currentItem = currentMap.get(newItem.id);
      
      // If item exists in current state and hasn't changed, preserve reference
      if (currentItem && !updated.some(u => u.id === newItem.id)) {
        return currentItem;
      }
      
      return newItem;
    });
  }

  return {
    items: mergedItems,
    changes: { added, removed, updated },
    hasChanges: true,
  };
}

/**
 * Checks if an item has changed by comparing specific fields
 */
function hasItemChanged<T>(
  currentItem: T,
  newItem: T,
  compareFields?: (keyof T)[]
): boolean {
  if (!compareFields || compareFields.length === 0) {
    // If no fields specified, use content hash comparison
    return hasContentChanged(currentItem, newItem);
  }

  // Compare only specified fields
  for (const field of compareFields) {
    const currentValue = currentItem[field];
    const newValue = newItem[field];
    
    // Handle nested objects/arrays with content hash
    if (typeof currentValue === 'object' && typeof newValue === 'object') {
      if (hasContentChanged(currentValue, newValue)) {
        return true;
      }
    } else if (currentValue !== newValue) {
      return true;
    }
  }

  return false;
}

/**
 * Creates a stable ID set for tracking interactions
 */
export function createStableIdSet(ids: string[]): Set<string> {
  return new Set(ids);
}

/**
 * Checks if an ID is in an interaction set
 */
export function isInteractingWith(id: string, interactionSet: Set<string>): boolean {
  return interactionSet.has(id);
}
