package mp3

import (
	"context"
	"fmt"
	"io"

	gomp3 "github.com/hajimehoshi/go-mp3"
	"github.com/jan/fm-rds-tx/internal/ingest"
	"github.com/jan/fm-rds-tx/internal/ingest/decoder"
)

type Decoder struct{}

func New() *Decoder { return &Decoder{} }

func (d *Decoder) Name() string { return "mp3-native" }

func (d *Decoder) DecodeStream(ctx context.Context, r io.Reader, meta decoder.StreamMeta, emit func(ingest.PCMChunk) error) error {
	if r == nil {
		return fmt.Errorf("%w: mp3 decoder stream reader is nil", decoder.ErrUnsupported)
	}
	if emit == nil {
		return fmt.Errorf("%w: mp3 decoder emit callback is nil", decoder.ErrUnsupported)
	}

	dec, err := gomp3.NewDecoder(r)
	if err != nil {
		return fmt.Errorf("%w: mp3 decoder init: %v", decoder.ErrUnsupported, err)
	}

	const channels = 2 // go-mp3 always decodes to stereo s16le
	sampleRate := decoder.ResolveSampleRate(dec.SampleRate(), meta)

	const chunkFrames = 1024
	const frameBytes = channels * 2
	buf := make([]byte, chunkFrames*frameBytes)
	seq := uint64(0)

	for {
		select {
		case <-ctx.Done():
			return nil
		default:
		}

		n, readErr := io.ReadAtLeast(dec, buf, frameBytes)
		if readErr != nil {
			if readErr == io.EOF || readErr == io.ErrUnexpectedEOF {
				if n > 0 {
					if err := emitChunk(buf[:n], seq, sampleRate, meta.SourceID, emit); err != nil {
						return err
					}
				}
				return nil
			}
			return fmt.Errorf("mp3 decoder read pcm: %w", readErr)
		}

		if err := emitChunk(buf[:n], seq, sampleRate, meta.SourceID, emit); err != nil {
			return err
		}
		seq++
	}
}

func emitChunk(data []byte, seq uint64, sampleRate int, sourceID string, emit func(ingest.PCMChunk) error) error {
	return emit(decoder.BuildChunk(
		decoder.PCM16LEToPCM32(data),
		2,
		sampleRate,
		seq,
		sourceID,
	))
}
