package audio

import (
	"git.atri.dad/atridad/muse/schema"
)

// Defines the interface for audio effects
type EffectProcessor interface {
	Process(samples []float64) []float64
}

// Holds a sequence of effects to be applied
type EffectChain struct {
	effects []EffectProcessor
}

// Adds an effect to the chain
func (ec *EffectChain) AddEffect(effect EffectProcessor) {
	ec.effects = append(ec.effects, effect)
}

// Applies all effects in the chain sequentially
func (ec *EffectChain) Process(samples []float64) []float64 {
	for _, effect := range ec.effects {
		samples = effect.Process(samples)
	}
	return samples
}

// Implements a simple reverb effect
type ReverbEffect struct {
	decay       float64
	wet         float64
	delayBuffer []float64
	delayIndex  int
	sampleRate  float64
}

// Creates a new reverb effect with specified parameters
func NewReverbEffect(decay, wet, sampleRate float64) *ReverbEffect {
	delaySize := int(0.1 * sampleRate)
	return &ReverbEffect{
		decay:       decay,
		wet:         wet,
		delayBuffer: make([]float64, delaySize),
		delayIndex:  0,
		sampleRate:  sampleRate,
	}
}

// Applies reverb effect to audio samples
func (r *ReverbEffect) Process(samples []float64) []float64 {
	output := make([]float64, len(samples))

	for i, sample := range samples {
		delayed := r.delayBuffer[r.delayIndex]
		r.delayBuffer[r.delayIndex] = sample + delayed*r.decay
		output[i] = sample*(1.0-r.wet) + delayed*r.wet
		r.delayIndex = (r.delayIndex + 1) % len(r.delayBuffer)
	}

	return output
}

// Implements a simple delay effect
type DelayEffect struct {
	delayTime   float64
	feedback    float64
	wet         float64
	delayBuffer []float64
	delayIndex  int
}

// Creates a new delay effect with specified parameters
func NewDelayEffect(delayTime, feedback, wet, sampleRate float64) *DelayEffect {
	delaySize := int(delayTime * sampleRate)
	return &DelayEffect{
		delayTime:   delayTime,
		feedback:    feedback,
		wet:         wet,
		delayBuffer: make([]float64, delaySize),
		delayIndex:  0,
	}
}

// Applies delay effect to audio samples
func (d *DelayEffect) Process(samples []float64) []float64 {
	output := make([]float64, len(samples))

	for i, sample := range samples {
		delayed := d.delayBuffer[d.delayIndex]
		d.delayBuffer[d.delayIndex] = sample + delayed*d.feedback
		output[i] = sample*(1.0-d.wet) + delayed*d.wet
		d.delayIndex = (d.delayIndex + 1) % len(d.delayBuffer)
	}

	return output
}

// Creates an effect chain from schema effect definitions
func CreateEffectChain(effects []schema.Effect, sampleRate float64) *EffectChain {
	chain := &EffectChain{}

	for _, effect := range effects {
		switch effect.Type {
		case "reverb":
			decay := getFloatParam(effect.Params, "decay", 0.5)
			wet := getFloatParam(effect.Params, "wet", 0.3)
			chain.AddEffect(NewReverbEffect(decay, wet, sampleRate))

		case "delay":
			delayTime := getFloatParam(effect.Params, "time", 0.25)
			feedback := getFloatParam(effect.Params, "feedback", 0.4)
			wet := getFloatParam(effect.Params, "wet", 0.3)
			chain.AddEffect(NewDelayEffect(delayTime, feedback, wet, sampleRate))
		}
	}

	return chain
}

// Safely extracts a float parameter with default fallback
func getFloatParam(params map[string]interface{}, key string, defaultValue float64) float64 {
	if val, exists := params[key]; exists {
		switch v := val.(type) {
		case float64:
			return v
		case int:
			return float64(v)
		}
	}
	return defaultValue
}
