package aoip

import (
	"context"
	"testing"
	"time"

	"aoiprxkit"
	"github.com/jan/fm-rds-tx/internal/ingest"
)

type stubReceiver struct {
	onStart func()
	onStop  func()
	stats   aoiprxkit.Stats
}

func (r *stubReceiver) Start(context.Context) error {
	if r.onStart != nil {
		r.onStart()
	}
	return nil
}

func (r *stubReceiver) Stop() error {
	if r.onStop != nil {
		r.onStop()
	}
	return nil
}

func (r *stubReceiver) Stats() aoiprxkit.Stats {
	return r.stats
}

func TestSourceEmitsChunksAndMapsStats(t *testing.T) {
	var handler aoiprxkit.FrameHandler
	rx := &stubReceiver{
		stats: aoiprxkit.Stats{
			PacketsGapLoss:  1,
			PacketsLateDrop: 2,
			JitterReorders:  1,
		},
	}
	src := New("aes67-test", aoiprxkit.Config{
		MulticastGroup:     "239.10.20.30",
		Port:               5004,
		PayloadType:        97,
		SampleRateHz:       48000,
		Channels:           2,
		Encoding:           "L24",
		PacketTime:         time.Millisecond,
		JitterDepthPackets: 6,
	}, WithReceiverFactory(func(_ aoiprxkit.Config, onFrame aoiprxkit.FrameHandler) (ReceiverClient, error) {
		handler = onFrame
		return rx, nil
	}))

	if err := src.Start(context.Background()); err != nil {
		t.Fatalf("start: %v", err)
	}
	defer src.Stop()

	handler(aoiprxkit.PCMFrame{
		SequenceNumber: 100,
		SampleRateHz:   48000,
		Channels:       2,
		Samples:        []int32{1, -1, 2, -2},
		ReceivedAt:     time.Now(),
	})
	handler(aoiprxkit.PCMFrame{
		SequenceNumber: 102,
		SampleRateHz:   48000,
		Channels:       2,
		Samples:        []int32{3, -3, 4, -4},
		ReceivedAt:     time.Now(),
	})

	chunk1 := readChunk(t, src.Chunks())
	if chunk1.Discontinuity {
		t.Fatalf("first chunk should not be discontinuity")
	}
	chunk2 := readChunk(t, src.Chunks())
	if !chunk2.Discontinuity {
		t.Fatalf("second chunk should be discontinuity on sequence gap")
	}

	stats := src.Stats()
	if stats.State != "running" {
		t.Fatalf("state=%q want running", stats.State)
	}
	if !stats.Connected {
		t.Fatalf("connected=false want true")
	}
	if stats.ChunksIn != 2 {
		t.Fatalf("chunksIn=%d want 2", stats.ChunksIn)
	}
	if stats.SamplesIn != 8 {
		t.Fatalf("samplesIn=%d want 8", stats.SamplesIn)
	}
	if stats.TransportLoss != 1 {
		t.Fatalf("transportLoss=%d want 1", stats.TransportLoss)
	}
	if stats.Reorders != 1 {
		t.Fatalf("reorders=%d want 1", stats.Reorders)
	}
	if stats.Underruns != 2 {
		t.Fatalf("underruns=%d want 2", stats.Underruns)
	}
	if stats.JitterDepth != 6 {
		t.Fatalf("jitterDepth=%d want 6", stats.JitterDepth)
	}
}

func TestSourceDescriptorSupportsDetailOverride(t *testing.T) {
	src := New("aes67-test", aoiprxkit.Config{
		MulticastGroup: "239.10.20.30",
		Port:           5004,
		SampleRateHz:   48000,
		Channels:       2,
	}, WithDetail("rtp://239.10.20.30:5004 (SAP s=AES67-MAIN)"), WithOrigin(ingest.SourceOrigin{
		Kind:       "sap-discovery",
		StreamName: "AES67-MAIN",
		Endpoint:   "rtp://239.10.20.30:5004",
	}))

	desc := src.Descriptor()
	if desc.Detail != "rtp://239.10.20.30:5004 (SAP s=AES67-MAIN)" {
		t.Fatalf("detail=%q", desc.Detail)
	}
	if desc.Origin == nil {
		t.Fatalf("expected descriptor origin")
	}
	if desc.Origin.Kind != "sap-discovery" {
		t.Fatalf("origin kind=%q", desc.Origin.Kind)
	}
	if desc.Origin.StreamName != "AES67-MAIN" {
		t.Fatalf("origin streamName=%q", desc.Origin.StreamName)
	}
}

func readChunk(t *testing.T, ch <-chan ingest.PCMChunk) ingest.PCMChunk {
	t.Helper()
	select {
	case chunk, ok := <-ch:
		if !ok {
			t.Fatal("chunk channel closed")
		}
		return chunk
	case <-time.After(500 * time.Millisecond):
		t.Fatal("timeout waiting for chunk")
		return ingest.PCMChunk{}
	}
}
