package utils

import (
	"bytes"
	"sync"

	"github.com/ebitengine/oto/v3"
)

// AudioPlayer wraps oto for simple audio playback.
// The oto context is created once and reused across plays.
type AudioPlayer struct {
	ctx    *oto.Context
	mu     sync.Mutex
	player *oto.Player
}

// NewAudioPlayer creates a new audio player with the given sample rate.
// Only one AudioPlayer should exist per process (oto allows one context).
func NewAudioPlayer(sampleRate int) (*AudioPlayer, error) {
	op := &oto.NewContextOptions{
		SampleRate:   sampleRate,
		ChannelCount: 1,
		Format:       oto.FormatSignedInt16LE,
	}
	ctx, readyChan, err := oto.NewContext(op)
	if err != nil {
		return nil, err
	}
	<-readyChan

	return &AudioPlayer{ctx: ctx}, nil
}

// Play stops any current playback and starts playing the given samples.
// Samples are float64 in the range -1.0 to 1.0.
// Playback is non-blocking — audio plays in the background.
func (ap *AudioPlayer) Play(samples []float64, sampleRate int) {
	ap.PlayAtSpeed(samples, sampleRate, 1.0)
}

// PlayAtSpeed plays samples at the given speed (1.0 = normal, 0.5 = half speed).
// Speed change is achieved by resampling the audio.
// Playback is non-blocking — audio plays in the background.
func (ap *AudioPlayer) PlayAtSpeed(samples []float64, sampleRate int, speed float64) {
	ap.mu.Lock()
	defer ap.mu.Unlock()

	// Stop previous playback
	if ap.player != nil {
		ap.player.Pause()
		ap.player = nil
	}

	// Resample if speed is not normal
	if speed != 1.0 {
		samples = Resample(samples, speed)
	}

	// Convert float64 samples to signed int16 LE bytes
	buf := Float64ToPCM16(samples)

	ap.player = ap.ctx.NewPlayer(bytes.NewReader(buf))
	ap.player.Play()
}

// IsPlaying returns true if audio is currently playing.
func (ap *AudioPlayer) IsPlaying() bool {
	ap.mu.Lock()
	defer ap.mu.Unlock()
	return ap.player != nil && ap.player.IsPlaying()
}

// Stop stops any current playback.
func (ap *AudioPlayer) Stop() {
	ap.mu.Lock()
	defer ap.mu.Unlock()
	if ap.player != nil {
		ap.player.Pause()
		ap.player = nil
	}
}

// Close stops playback. The oto context is released by the garbage collector
// (oto v3 does not expose a context close method).