package factory

import (
	"bytes"
	"context"
	"errors"
	"testing"
	"time"

	"aoiprxkit"

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

func TestBuildSourceNone(t *testing.T) {
	cfg := config.Default()
	cfg.Ingest.Kind = "none"
	src, ingress, err := BuildSource(cfg, Deps{})
	if err != nil {
		t.Fatalf("build source: %v", err)
	}
	if src != nil || ingress != nil {
		t.Fatalf("expected nil source and ingress for kind=none")
	}
}

func TestBuildSourceHTTPRawProvidesIngress(t *testing.T) {
	cfg := config.Default()
	cfg.Ingest.Kind = "http-raw"
	src, ingress, err := BuildSource(cfg, Deps{})
	if err != nil {
		t.Fatalf("build source: %v", err)
	}
	if src == nil {
		t.Fatalf("expected source")
	}
	if ingress == nil {
		t.Fatalf("expected ingress for http-raw")
	}
}

func TestBuildSourceKindIsNormalized(t *testing.T) {
	cfg := config.Default()
	cfg.Ingest.Kind = "  HTTP-RAW "
	src, ingress, err := BuildSource(cfg, Deps{})
	if err != nil {
		t.Fatalf("build source: %v", err)
	}
	if src == nil || ingress == nil {
		t.Fatalf("expected source and ingress for normalized http-raw kind")
	}
	if got := src.Descriptor().Kind; got != "http-raw" {
		t.Fatalf("source kind=%q want http-raw", got)
	}
}

func TestBuildSourceStdin(t *testing.T) {
	cfg := config.Default()
	cfg.Ingest.Kind = "stdin"
	src, ingress, err := BuildSource(cfg, Deps{Stdin: bytes.NewReader(nil)})
	if err != nil {
		t.Fatalf("build source: %v", err)
	}
	if src == nil {
		t.Fatalf("expected source")
	}
	if ingress != nil {
		t.Fatalf("expected no ingress for stdin")
	}
	if got := src.Descriptor().Kind; got != "stdin-pcm" {
		t.Fatalf("source kind=%s", got)
	}
}

func TestBuildSourceIcecastUsesDecoderPreference(t *testing.T) {
	cfg := config.Default()
	cfg.Ingest.Kind = "icecast"
	cfg.Ingest.Icecast.URL = "http://localhost:8000/stream"
	cfg.Ingest.Icecast.Decoder = "ffmpeg"
	src, ingress, err := BuildSource(cfg, Deps{})
	if err != nil {
		t.Fatalf("build source: %v", err)
	}
	if src == nil {
		t.Fatalf("expected source")
	}
	if ingress != nil {
		t.Fatalf("expected no ingress for icecast")
	}
	if got := src.Descriptor().Codec; got != "ffmpeg" {
		t.Fatalf("codec=%s want ffmpeg", got)
	}
	if got := src.Descriptor().Origin; got == nil || got.Kind != "url" {
		t.Fatalf("expected icecast origin kind url, got %+v", got)
	}
}

func TestBuildSourceSRT(t *testing.T) {
	cfg := config.Default()
	cfg.Ingest.Kind = "srt"
	cfg.Ingest.SRT.URL = "srt://127.0.0.1:9000?mode=listener"
	cfg.Ingest.SRT.Mode = "listener"
	cfg.Ingest.SRT.SampleRateHz = 48000
	cfg.Ingest.SRT.Channels = 2

	src, ingress, err := BuildSource(cfg, Deps{})
	if err != nil {
		t.Fatalf("build source: %v", err)
	}
	if src == nil {
		t.Fatalf("expected source")
	}
	if ingress != nil {
		t.Fatalf("expected no ingress for srt")
	}
	if got := src.Descriptor().Kind; got != "srt" {
		t.Fatalf("source kind=%s", got)
	}
	if got := src.Descriptor().Origin; got == nil || got.Kind != "url" || got.Mode != "listener" {
		t.Fatalf("expected srt origin url/listener, got %+v", got)
	}
}

func TestBuildSourceAES67(t *testing.T) {
	cfg := config.Default()
	cfg.Ingest.Kind = "aes67"
	cfg.Ingest.AES67.MulticastGroup = "239.69.10.20"
	cfg.Ingest.AES67.Port = 5008
	cfg.Ingest.AES67.PayloadType = 98
	cfg.Ingest.AES67.SampleRateHz = 48000
	cfg.Ingest.AES67.Channels = 2
	cfg.Ingest.AES67.Encoding = "L24"
	cfg.Ingest.AES67.PacketTimeMs = 1
	cfg.Ingest.AES67.JitterDepthPackets = 6

	src, ingress, err := BuildSource(cfg, Deps{})
	if err != nil {
		t.Fatalf("build source: %v", err)
	}
	if src == nil {
		t.Fatalf("expected source")
	}
	if ingress != nil {
		t.Fatalf("expected no ingress for aes67")
	}
	if got := src.Descriptor().Kind; got != "aes67" {
		t.Fatalf("source kind=%s", got)
	}
}

func TestBuildSourceAES67FromInlineSDP(t *testing.T) {
	cfg := config.Default()
	cfg.Ingest.Kind = "aes67"
	cfg.Ingest.AES67.MulticastGroup = ""
	cfg.Ingest.AES67.SDP = "v=0\r\ns=demo\r\nc=IN IP4 239.10.20.30\r\nm=audio 5004 RTP/AVP 97\r\na=rtpmap:97 L24/48000/2\r\na=ptime:1\r\n"

	src, _, err := BuildSource(cfg, Deps{})
	if err != nil {
		t.Fatalf("build source: %v", err)
	}
	desc := src.Descriptor()
	if desc.Transport != "rtp" {
		t.Fatalf("transport=%q want rtp", desc.Transport)
	}
	if desc.SampleRateHz != 48000 || desc.Channels != 2 {
		t.Fatalf("shape=%d/%d", desc.SampleRateHz, desc.Channels)
	}
	if desc.Origin == nil || desc.Origin.Kind != "sdp-inline" {
		t.Fatalf("origin=%+v want sdp-inline", desc.Origin)
	}
	if desc.Origin.Endpoint != "rtp://239.10.20.30:5004" {
		t.Fatalf("origin endpoint=%q", desc.Origin.Endpoint)
	}
}

func TestBuildSourceAES67WithDiscovery(t *testing.T) {
	cfg := config.Default()
	cfg.Ingest.Kind = "aes67"
	cfg.Ingest.AES67.MulticastGroup = ""
	cfg.Ingest.AES67.Port = 0
	cfg.Ingest.AES67.Discovery.StreamName = "AES67-MAIN"
	cfg.Ingest.AES67.Discovery.TimeoutMs = 1500

	var gotReq AES67DiscoverRequest
	src, _, err := BuildSource(cfg, Deps{
		AES67Discover: func(_ context.Context, req AES67DiscoverRequest) (aoiprxkit.SAPAnnouncement, error) {
			gotReq = req
			return aoiprxkit.SAPAnnouncement{
				SDP: "v=0\r\ns=AES67-MAIN\r\nc=IN IP4 239.10.20.30\r\nm=audio 5004 RTP/AVP 97\r\na=rtpmap:97 L24/48000/2\r\na=ptime:1\r\n",
			}, nil
		},
	})
	if err != nil {
		t.Fatalf("build source: %v", err)
	}
	if gotReq.StreamName != "AES67-MAIN" {
		t.Fatalf("discovery streamName=%q want AES67-MAIN", gotReq.StreamName)
	}
	if gotReq.Timeout != 1500*time.Millisecond {
		t.Fatalf("discovery timeout=%s want 1500ms", gotReq.Timeout)
	}
	desc := src.Descriptor()
	if desc.Detail != "rtp://239.10.20.30:5004 (SAP s=AES67-MAIN)" {
		t.Fatalf("descriptor detail=%q", desc.Detail)
	}
	if desc.Origin == nil || desc.Origin.Kind != "sap-discovery" {
		t.Fatalf("origin=%+v want sap-discovery", desc.Origin)
	}
	if desc.Origin.StreamName != "AES67-MAIN" {
		t.Fatalf("origin streamName=%q", desc.Origin.StreamName)
	}
}

func TestBuildSourceAES67DiscoveryError(t *testing.T) {
	cfg := config.Default()
	cfg.Ingest.Kind = "aes67"
	cfg.Ingest.AES67.MulticastGroup = ""
	cfg.Ingest.AES67.Port = 0
	cfg.Ingest.AES67.Discovery.StreamName = "AES67-MAIN"

	_, _, err := BuildSource(cfg, Deps{
		AES67Discover: func(_ context.Context, req AES67DiscoverRequest) (aoiprxkit.SAPAnnouncement, error) {
			_ = req
			return aoiprxkit.SAPAnnouncement{}, errors.New("timeout")
		},
	})
	if err == nil {
		t.Fatalf("expected discovery error")
	}
}

func TestBuildSourceUnsupportedKind(t *testing.T) {
	cfg := config.Default()
	cfg.Ingest.Kind = "nope"
	_, _, err := BuildSource(cfg, Deps{})
	if err == nil {
		t.Fatalf("expected error")
	}
}

func TestSampleRateForKind(t *testing.T) {
	cfg := config.Default()
	cfg.Ingest.Kind = "stdin"
	cfg.Ingest.Stdin.SampleRateHz = 48000
	if got := SampleRateForKind(cfg); got != 48000 {
		t.Fatalf("stdin sample rate=%d", got)
	}

	cfg.Ingest.Kind = "http-raw"
	cfg.Ingest.HTTPRaw.SampleRateHz = 32000
	if got := SampleRateForKind(cfg); got != 32000 {
		t.Fatalf("http-raw sample rate=%d", got)
	}

	cfg.Ingest.Kind = "icecast"
	if got := SampleRateForKind(cfg); got != 44100 {
		t.Fatalf("icecast sample rate=%d", got)
	}

	cfg.Ingest.Kind = "srt"
	cfg.Ingest.SRT.SampleRateHz = 48000
	if got := SampleRateForKind(cfg); got != 48000 {
		t.Fatalf("srt sample rate=%d", got)
	}

	cfg.Ingest.Kind = "aes67"
	cfg.Ingest.AES67.SampleRateHz = 32000
	if got := SampleRateForKind(cfg); got != 32000 {
		t.Fatalf("aes67 sample rate=%d", got)
	}
}
