package aoiprxkit

import (
	"math"
	"sync"
	"time"
)

type ChannelMeter struct {
	RMS    float64 `json:"rms"`
	Peak   float64 `json:"peak"`
	Latest float64 `json:"latest"`
}

type MeterSnapshot struct {
	UpdatedAt    string         `json:"updatedAt"`
	Source       string         `json:"source"`
	SampleRateHz int            `json:"sampleRateHz"`
	Channels     int            `json:"channels"`
	Meters       []ChannelMeter `json:"meters"`
}

// LiveMeter consumes PCM frames and publishes simple per-channel level data.
type LiveMeter struct {
	mu     sync.RWMutex
	latest MeterSnapshot
	subs   map[chan MeterSnapshot]struct{}
}

func NewLiveMeter() *LiveMeter {
	return &LiveMeter{subs: make(map[chan MeterSnapshot]struct{})}
}

func (m *LiveMeter) Consume(frame PCMFrame) {
	if frame.Channels <= 0 || len(frame.Samples) == 0 {
		return
	}
	meters := make([]ChannelMeter, frame.Channels)
	fullScale := detectFullScale(frame.Samples)
	sums := make([]float64, frame.Channels)
	counts := make([]int, frame.Channels)

	for i, sample := range frame.Samples {
		ch := i % frame.Channels
		norm := float64(sample) / fullScale
		abs := math.Abs(norm)
		if abs > meters[ch].Peak {
			meters[ch].Peak = abs
		}
		meters[ch].Latest = norm
		sums[ch] += norm * norm
		counts[ch]++
	}
	for ch := range meters {
		if counts[ch] > 0 {
			meters[ch].RMS = math.Sqrt(sums[ch] / float64(counts[ch]))
		}
	}

	snap := MeterSnapshot{
		UpdatedAt:    time.Now().UTC().Format(time.RFC3339Nano),
		Source:       frame.Source,
		SampleRateHz: frame.SampleRateHz,
		Channels:     frame.Channels,
		Meters:       meters,
	}

	m.mu.Lock()
	m.latest = snap
	subs := make([]chan MeterSnapshot, 0, len(m.subs))
	for ch := range m.subs {
		subs = append(subs, ch)
	}
	m.mu.Unlock()

	for _, ch := range subs {
		select {
		case ch <- snap:
		default:
		}
	}
}

func detectFullScale(samples []int32) float64 {
	var maxAbs int64
	for _, s := range samples {
		v := int64(s)
		if v < 0 {
			v = -v
		}
		if v > maxAbs {
			maxAbs = v
		}
	}
	if maxAbs <= 8388608 {
		return 8388608.0
	}
	return 2147483648.0
}

func (m *LiveMeter) Snapshot() MeterSnapshot {
	m.mu.RLock()
	defer m.mu.RUnlock()
	return m.latest
}

func (m *LiveMeter) Subscribe() (<-chan MeterSnapshot, func()) {
	ch := make(chan MeterSnapshot, 8)
	m.mu.Lock()
	m.subs[ch] = struct{}{}
	latest := m.latest
	m.mu.Unlock()

	if latest.UpdatedAt != "" {
		ch <- latest
	}

	unsubscribe := func() {
		m.mu.Lock()
		if _, ok := m.subs[ch]; ok {
			delete(m.subs, ch)
			close(ch)
		}
		m.mu.Unlock()
	}
	return ch, unsubscribe
}
