package aoiprxkit

import (
	"context"
	"fmt"
	"io"
	"sync"
	"time"
)

type StreamReceiverConfig struct {
	SourceLabel string
}

type StreamReceiver struct {
	cfg     StreamReceiverConfig
	opener  func(context.Context) (io.ReadCloser, error)
	onFrame FrameHandler

	mu     sync.Mutex
	rc     io.ReadCloser
	cancel context.CancelFunc
	done   chan struct{}
}

func NewStreamReceiver(cfg StreamReceiverConfig, opener func(context.Context) (io.ReadCloser, error), onFrame FrameHandler) (*StreamReceiver, error) {
	if opener == nil {
		return nil, fmt.Errorf("opener must not be nil")
	}
	if onFrame == nil {
		return nil, fmt.Errorf("onFrame must not be nil")
	}
	return &StreamReceiver{cfg: cfg, opener: opener, onFrame: onFrame, done: make(chan struct{})}, nil
}

func (r *StreamReceiver) Start(ctx context.Context) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	if r.rc != nil {
		return fmt.Errorf("stream receiver already started")
	}
	cctx, cancel := context.WithCancel(ctx)
	rc, err := r.opener(cctx)
	if err != nil {
		cancel()
		return err
	}
	r.rc = rc
	r.cancel = cancel
	r.done = make(chan struct{})
	go r.loop(cctx, rc)
	return nil
}

func (r *StreamReceiver) Stop() error {
	r.mu.Lock()
	rc := r.rc
	cancel := r.cancel
	done := r.done
	r.rc = nil
	r.cancel = nil
	r.mu.Unlock()

	if cancel != nil {
		cancel()
	}
	if rc != nil {
		_ = rc.Close()
	}
	if done != nil {
		<-done
	}
	return nil
}

func (r *StreamReceiver) loop(ctx context.Context, rc io.ReadCloser) {
	defer close(r.done)
	for {
		select {
		case <-ctx.Done():
			return
		default:
		}
		hdr, err := ReadStreamHeader(rc)
		if err != nil {
			return
		}
		payload := make([]byte, hdr.PayloadBytes)
		if _, err := io.ReadFull(rc, payload); err != nil {
			return
		}
		switch hdr.Codec {
		case StreamCodecPCM_S32LE:
			samples, err := DecodeS32LE(payload, int(hdr.Channels))
			if err != nil {
				continue
			}
			r.onFrame(PCMFrame{
				SequenceNumber: uint16(hdr.Sequence & 0xffff),
				Timestamp:      uint32(hdr.Timestamp & 0xffffffff),
				SampleRateHz:   int(hdr.SampleRateHz),
				Channels:       int(hdr.Channels),
				Samples:        samples,
				ReceivedAt:     time.Now(),
				Source:         r.cfg.SourceLabel,
			})
		case StreamCodecOpus:
			// Reserved for later phase. Not decoded in this module revision.
			continue
		default:
			continue
		}
	}
}
