package channels

import (
	"reflect"

	"ragflow/internal/harness/graph/types"
)

// ReducerChannel wraps a channel with a reducer function.
type ReducerChannel struct {
	Channel
	reducer types.ReducerFunc
}

// NewReducerChannel creates a new ReducerChannel.
func NewReducerChannel(channel Channel, reducer types.ReducerFunc) *ReducerChannel {
	return &ReducerChannel{
		Channel: channel,
		reducer: reducer,
	}
}

// Update applies the reducer to combine new values with the current value.
func (rc *ReducerChannel) Update(values []interface{}) (bool, error) {
	if len(values) == 0 {
		return false, nil
	}

	// Read current value from the wrapped channel.
	current, err := rc.Channel.Get()

	// Combine all values with the current value using the reducer.
	// If the channel is empty, start from values[0] and combine the rest.
	combined := values[0]
	if err == nil {
		combined = rc.reducer(current, combined)
	}
	for i := 1; i < len(values); i++ {
		combined = rc.reducer(combined, values[i])
	}

	return rc.Channel.Update([]interface{}{combined})
}

// Copy returns a copy of the ReducerChannel.
func (rc *ReducerChannel) Copy() Channel {
	return &ReducerChannel{
		Channel: rc.Channel.Copy(),
		reducer: rc.reducer,
	}
}

// Checkpoint returns the checkpoint of the wrapped channel.
func (rc *ReducerChannel) Checkpoint() interface{} {
	return rc.Channel.Checkpoint()
}

// FromCheckpoint restores the wrapped channel from a checkpoint.
func (rc *ReducerChannel) FromCheckpoint(checkpoint interface{}) Channel {
	return &ReducerChannel{
		Channel: rc.Channel.FromCheckpoint(checkpoint),
		reducer: rc.reducer,
	}
}

// CreateReducerChannel creates a reducer channel based on type hints.
// It inspects the field type and annotation to determine appropriate channel.
func CreateReducerChannel(fieldName string, fieldType reflect.Type, reducer types.ReducerFunc) (Channel, error) {
	// Determine default channel based on type
	var channel Channel

	switch fieldType.Kind() {
	case reflect.Slice, reflect.Array:
		// For slices, use BinaryOperatorAggregate with append operator
		channel = NewBinaryOperatorAggregate(fieldType, ListAppend)
	case reflect.Map:
		// For maps, use BinaryOperatorAggregate with merge operator
		channel = NewBinaryOperatorAggregate(fieldType, func(a, b interface{}) interface{} {
			if aMap, ok := a.(map[string]interface{}); ok {
				if bMap, ok := b.(map[string]interface{}); ok {
					result := make(map[string]interface{}, len(aMap)+len(bMap))
					for k, v := range aMap {
						result[k] = v
					}
					for k, v := range bMap {
						result[k] = v
					}
					return result
				}
			}
			return b
		})
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
		reflect.Float32, reflect.Float64:
		// For numeric types, use BinaryOperatorAggregate with add operator
		channel = NewBinaryOperatorAggregate(fieldType, IntAdd)
	default:
		// Default to LastValue channel
		channel = NewLastValue(fieldType)
	}

	// Set the channel key
	channel.SetKey(fieldName)

	// If a reducer is provided, wrap the channel with it
	if reducer != nil {
		return NewReducerChannel(channel, reducer), nil
	}

	return channel, nil
}

// Built-in reducer functions for common types.
var (
	// AddReducer adds numeric values.
	AddReducer = func(current, update interface{}) interface{} {
		if current == nil {
			return update
		}
		// Try int addition
		if ci, ok := current.(int); ok {
			if ui, ok := update.(int); ok {
				return ci + ui
			}
		}
		// Try float64 addition
		if cf, ok := current.(float64); ok {
			if uf, ok := update.(float64); ok {
				return cf + uf
			}
		}
		// Fallback: return update
		return update
	}

	// AppendReducer appends to slices.
	AppendReducer = func(current, update interface{}) interface{} {
		if current == nil {
			return []interface{}{update}
		}
		if slice, ok := current.([]interface{}); ok {
			return append(slice, update)
		}
		// Convert to slice
		return []interface{}{current, update}
	}

	// MergeReducer merges maps.
	MergeReducer = func(current, update interface{}) interface{} {
		if current == nil {
			return update
		}
		if currentMap, ok := current.(map[string]interface{}); ok {
			if updateMap, ok := update.(map[string]interface{}); ok {
				result := make(map[string]interface{}, len(currentMap)+len(updateMap))
				for k, v := range currentMap {
					result[k] = v
				}
				for k, v := range updateMap {
					result[k] = v
				}
				return result
			}
		}
		return update
	}
)
