package comparer

import (
	"github.com/ahatornn/enumerable/hashcode"
)

// ByField creates an EqualityComparer that compares two values of type T by comparing
// a specific field selected by the provided field selector function.
//
// This comparer is particularly useful when:
//   - T is a struct type containing non-comparable fields (slices, maps, etc.)
//   - You want to compare instances based on a single comparable field
//   - You need custom equality logic for complex types
//   - You want to leverage efficient hash-based collections
//
// The returned EqualityComparer will:
//   - Extract the field value from both input values using fieldSelector
//   - Compare the extracted field values using == operator
//   - Compute hash codes based on the field values for efficient lookup
//
// Type Parameters:
//
//	T - the type of values to compare (can contain non-comparable fields)
//	F - the type of the field to compare (must be comparable)
//
// Parameters:
//
//	fieldSelector - a function that extracts the comparable field from type T
//
// Returns:
//
//	An EqualityComparer[T] that compares values by the selected field
//
// ⚠️ Important: The field type F must be comparable (no slices, maps, functions)
// If F is non-comparable, the == operation will cause a compile-time error
//
// ⚠️ Performance note: The field selector function is called twice per equality comparison
// and once per hash code computation. Consider the cost of field extraction when using
// expensive selectors. Average time complexity: O(1) for both Equals and GetHashCode.
//
// Notes:
//   - The field selector should be deterministic and side-effect free
//   - For nested field access, you can use chained selectors
//   - Works with pointer receivers and value receivers
//   - Thread safety depends on the field selector implementation
//   - Common use cases include ID-based comparison, name-based comparison
//   - Hash codes are computed using simple hashing of field values
//   - Can be composed with other comparers for complex logic
//
// ⚠️ Limitations:
//   - Cannot compare fields of non-comparable types (slices, maps, funcs)
//   - Field selector must not panic or have side effects
//   - No automatic handling of nil pointers in field selection
//   - Hash distribution depends on field value distribution
func ByField[T any, F comparable](fieldSelector func(T) F) EqualityComparer[T] {
	return New(
		func(a, b T) bool {
			return fieldSelector(a) == fieldSelector(b)
		},
		func(a T) uint64 {
			return hashcode.Compute(fieldSelector(a))
		},
	)
}

// Composite creates an EqualityComparer that combines multiple EqualityComparer instances
// using logical AND operation for equality and combined hash codes.
//
// This comparer is useful when:
//   - You need to compare complex objects by multiple fields
//   - You want to combine different comparison strategies
//   - You need to create hierarchical equality logic
//   - You want to reuse existing comparers in combinations
//
// The returned EqualityComparer will:
//   - Call each provided comparer's Equals method in order until one returns false
//   - Return false immediately if any comparer returns false (short-circuit)
//   - Return true only if all comparers return true
//   - Combine hash codes using prime multiplication for good distribution
//   - Handle empty comparer slice (returns true for equality, consistent hash)
//
// Type Parameters:
//
//	T - the type of values to compare
//
// Parameters:
//
//	comparers - variadic list of EqualityComparer[T] to combine
//
// Returns:
//
//	An EqualityComparer[T] that combines all provided comparers with AND logic
//
// ⚠️ Performance note: Equality comparison time complexity is O(n*m) where n is the
// number of comparers and m is the average time per comparer. Hash code computation
// is O(n*k) where k is the average hash computation time. No memory allocations
// during comparison or hashing operations.
//
// Notes:
//   - If no comparers are provided, equality always returns true and hash is consistent
//   - If one comparer is provided, it behaves identically to that comparer
//   - Each comparer should be deterministic and side-effect free
//   - Thread safety depends on the individual comparer implementations
//   - Common use cases include multi-field comparison, composite key comparison
//   - Can be nested to create complex comparison hierarchies
//   - Hash codes use prime-based combination (17 as seed, 31 as multiplier)
//
// ⚠️ Important considerations:
//   - Order of comparers affects performance but not correctness
//   - Place the most selective or fastest comparers first for better performance
//   - Each comparer is called with the same input parameters
//   - No duplicate comparer detection is performed
//   - Be careful with expensive comparers in the chain
func Composite[T any](comparers ...EqualityComparer[T]) EqualityComparer[T] {
	return New(
		func(a, b T) bool {
			for _, comparer := range comparers {
				if !comparer.Equals(a, b) {
					return false
				}
			}
			return true
		},
		func(a T) uint64 {
			hashes := make([]uint64, len(comparers))
			for i, comparer := range comparers {
				hashes[i] = comparer.GetHashCode(a)
			}
			return hashcode.CombineHashes(hashes...)
		},
	)
}

// Custom creates an EqualityComparer from custom equals and hash code functions.
// This is useful when you need complete control over both equality comparison
// and hash code generation logic.
//
// This comparer is particularly useful when:
//   - Built-in comparers (ByField, Composite) don't meet your needs
//   - You need complex comparison logic that spans multiple fields
//   - You want to implement domain-specific equality rules
//   - You need to compare types with special equality semantics
//   - You want to wrap existing comparison functions
//   - You need custom hash code generation for optimal performance
//
// The returned EqualityComparer will:
//   - Delegate equality checks to the provided equals function
//   - Delegate hash code generation to the provided getHashCode function
//   - Pass both input parameters directly to equals function
//   - Pass single parameter to getHashCode function
//   - Preserve the behavior and performance characteristics of provided functions
//
// Type Parameters:
//
//	T - the type of values to compare
//
// Parameters:
//
//	equals - a function that determines equality between two values of type T
//	getHashCode - a function that computes hash code for a value of type T
//
// Returns:
//
//	An EqualityComparer[T] that uses the provided functions for equality and hashing
//
// ⚠️ Important: The provided functions must follow these contracts:
//   - equals must be reflexive (equals(x, x) == true)
//   - equals must be symmetric (equals(x, y) == equals(y, x))
//   - equals must be transitive (if equals(x, y) && equals(y, z) then equals(x, z))
//   - getHashCode must return the same value for equal objects (if equals(x, y) then getHashCode(x) == getHashCode(y))
//   - getHashCode should distribute well to minimize hash collisions
//
// ⚠️ Performance note: The performance characteristics of the returned comparer
// are identical to those of the provided functions. No additional overhead is added.
// Average time complexity: O(1) for both Equals and GetHashCode.
//
// Notes:
//   - The equals and getHashCode functions should be side-effect free
//   - Thread safety depends on the provided function implementations
//   - Can be used to wrap existing comparison logic
//   - Useful for implementing comparison with tolerance (floating point, time, etc.)
//   - Can handle nil values gracefully if functions support them
//   - Common use cases include custom business logic, approximate equality, complex struct comparison
//   - Hash code function should be fast and provide good distribution
//
// ⚠️ Important considerations:
//   - No validation is performed on the provided functions - incorrect implementation may cause unexpected behavior
//   - The functions are called directly - ensure they handle all possible input values
//   - Consider using ByField or Composite for simpler cases before resorting to Custom
//   - Be careful with recursive types to avoid infinite loops
//   - Hash code quality directly affects performance of hash-based collections
func Custom[T any](equals func(T, T) bool, getHashCode func(T) uint64) EqualityComparer[T] {
	return New(equals, getHashCode)
}
