package datascience

import "fmt"

// DiffEntities compares two snapshots of entities and returns field-level differences.
// Detects added, removed, modified, and unchanged entities.
// ignoreFields specifies fields to exclude from comparison (defaults to ["created_at", "updated_at"] when nil).
func DiffEntities(before, after []map[string]any, key string, ignoreFields []string) map[string]any {
	if ignoreFields == nil {
		ignoreFields = []string{"created_at", "updated_at"}
	}

	ignoreSet := make(map[string]bool, len(ignoreFields))
	for _, f := range ignoreFields {
		ignoreSet[f] = true
	}

	beforeMap := make(map[string]map[string]any, len(before))
	for _, e := range before {
		if k, ok := e[key]; ok {
			beforeMap[fmt.Sprintf("%v", k)] = e
		}
	}

	afterMap := make(map[string]map[string]any, len(after))
	for _, e := range after {
		if k, ok := e[key]; ok {
			afterMap[fmt.Sprintf("%v", k)] = e
		}
	}

	added := []map[string]any{}
	for k, e := range afterMap {
		if _, exists := beforeMap[k]; !exists {
			added = append(added, e)
		}
	}

	removed := []map[string]any{}
	for k, e := range beforeMap {
		if _, exists := afterMap[k]; !exists {
			removed = append(removed, e)
		}
	}

	modified := []map[string]any{}
	unchanged := 0

	for k, b := range beforeMap {
		a, exists := afterMap[k]
		if !exists {
			continue
		}

		// Collect all fields from both entities
		allFields := make(map[string]bool)
		for f := range b {
			allFields[f] = true
		}
		for f := range a {
			allFields[f] = true
		}

		changes := map[string]any{}
		for field := range allFields {
			if ignoreSet[field] || field == key {
				continue
			}
			oldVal := b[field]
			newVal := a[field]
			if fmt.Sprintf("%v", oldVal) != fmt.Sprintf("%v", newVal) {
				changes[field] = map[string]any{"old": oldVal, "new": newVal}
			}
		}

		if len(changes) > 0 {
			modified = append(modified, map[string]any{"key": k, "changes": changes})
		} else {
			unchanged++
		}
	}

	nAdded := len(added)
	nRemoved := len(removed)
	nModified := len(modified)
	summary := fmt.Sprintf("%d added, %d removed, %d modified, %d unchanged",
		nAdded, nRemoved, nModified, unchanged)

	return map[string]any{
		"added":     added,
		"removed":   removed,
		"modified":  modified,
		"unchanged": unchanged,
		"summary":   summary,
	}
}
