package core

// JoinByKey une dos slices de map[string]any por una clave comun.
// Soporta los cuatro tipos de join: inner, left, right, outer.
// Campos duplicados del lado right (distintos a la clave) se sufijan con _right.
// Algoritmo O(n+m): indexa right por key, luego itera left.
func JoinByKey(left, right []map[string]any, key, how string) []map[string]any {
	// Determinar campos conflictivos entre left y right
	leftFields := map[string]bool{}
	for _, row := range left {
		for k := range row {
			leftFields[k] = true
		}
	}
	rightFields := map[string]bool{}
	for _, row := range right {
		for k := range row {
			if k != key {
				rightFields[k] = true
			}
		}
	}
	conflicting := map[string]bool{}
	for k := range rightFields {
		if leftFields[k] {
			conflicting[k] = true
		}
	}

	// Indexar right por key (un key puede tener multiples rows)
	rightIndex := map[any][]map[string]any{}
	for _, row := range right {
		k := row[key]
		rightIndex[k] = append(rightIndex[k], row)
	}

	// Plantilla vacia del right (todos los campos de right a nil)
	emptyRight := func() map[string]any {
		m := map[string]any{}
		for k := range rightFields {
			if conflicting[k] {
				m[k+"_right"] = nil
			} else {
				m[k] = nil
			}
		}
		return m
	}

	merge := func(l, r map[string]any) map[string]any {
		out := map[string]any{}
		if l != nil {
			for k, v := range l {
				out[k] = v
			}
		}
		if r != nil {
			for k, v := range r {
				if k == key {
					continue
				}
				if conflicting[k] {
					out[k+"_right"] = v
				} else {
					out[k] = v
				}
			}
		}
		return out
	}

	matchedRightKeys := map[any]bool{}
	var result []map[string]any

	for _, l := range left {
		k := l[key]
		rRows, ok := rightIndex[k]
		if ok {
			matchedRightKeys[k] = true
			for _, r := range rRows {
				result = append(result, merge(l, r))
			}
		} else {
			if how == "left" || how == "outer" {
				row := merge(l, nil)
				for rk, rv := range emptyRight() {
					row[rk] = rv
				}
				result = append(result, row)
			}
		}
	}

	if how == "right" || how == "outer" {
		for _, r := range right {
			k := r[key]
			if !matchedRightKeys[k] {
				row := emptyRight()
				row[key] = k
				for rk, rv := range r {
					if rk == key {
						continue
					}
					if conflicting[rk] {
						row[rk+"_right"] = rv
					} else {
						row[rk] = rv
					}
				}
				result = append(result, row)
			}
		}
	}

	return result
}
