package webrtc

import (
	"testing"
	"time"

	"github.com/bluenviron/gortsplib/v5/pkg/format"
	"github.com/bluenviron/mediamtx/internal/conf"
	"github.com/bluenviron/mediamtx/internal/stream"
	"github.com/bluenviron/mediamtx/internal/test"
	"github.com/pion/rtp"
	"github.com/pion/webrtc/v4"
	"github.com/stretchr/testify/require"
)

func TestToStreamNoSupportedCodecs(t *testing.T) {
	pc := &PeerConnection{}
	_, err := ToStream(pc, nil)
	require.Equal(t, errNoSupportedCodecsTo, err)
}

// this is impossible to test since unsupported tracks cause an error
// as they are not included inside incomingVideoCodecs or incomingAudioCodecs
// func TestToStreamSkipUnsupportedTracks(t *testing.T)

var toFromStreamCases = []struct {
	name       string
	in         format.Format
	webrtcCaps webrtc.RTPCodecCapability
	out        format.Format
}{
	{
		"av1",
		&format.AV1{
			PayloadTyp: 96,
		},
		webrtc.RTPCodecCapability{
			MimeType:  "video/AV1",
			ClockRate: 90000,
		},
		&format.AV1{
			PayloadTyp: 96,
		},
	},
	{
		"vp9",
		&format.VP9{
			PayloadTyp: 96,
		},
		webrtc.RTPCodecCapability{
			MimeType:    "video/VP9",
			ClockRate:   90000,
			SDPFmtpLine: "profile-id=0",
		},
		&format.VP9{
			PayloadTyp: 96,
		},
	},
	{
		"vp8",
		&format.VP8{
			PayloadTyp: 96,
		},
		webrtc.RTPCodecCapability{
			MimeType:  "video/VP8",
			ClockRate: 90000,
		},
		&format.VP8{
			PayloadTyp: 96,
		},
	},
	{
		"h265",
		&format.H265{
			PayloadTyp: 96,
		},
		webrtc.RTPCodecCapability{
			MimeType:    "video/H265",
			ClockRate:   90000,
			SDPFmtpLine: "level-id=93;profile-id=1;tier-flag=0;tx-mode=SRST",
		},
		&format.H265{
			PayloadTyp: 96,
		},
	},
	{
		"h264",
		test.FormatH264,
		webrtc.RTPCodecCapability{
			MimeType:    "video/H264",
			ClockRate:   90000,
			SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f",
		},
		&format.H264{
			PayloadTyp:        96,
			PacketizationMode: 1,
		},
	},
	{
		"opus multichannel",
		&format.Opus{
			PayloadTyp:   112,
			ChannelCount: 6,
		},
		webrtc.RTPCodecCapability{
			MimeType:    "audio/multiopus",
			ClockRate:   48000,
			Channels:    6,
			SDPFmtpLine: "channel_mapping=0,4,1,2,3,5;num_streams=4;coupled_streams=2",
		},
		&format.Opus{
			PayloadTyp:   96,
			ChannelCount: 6,
		},
	},
	{
		"opus stereo",
		&format.Opus{
			PayloadTyp:   111,
			ChannelCount: 2,
		},
		webrtc.RTPCodecCapability{
			MimeType:    "audio/opus",
			ClockRate:   48000,
			Channels:    2,
			SDPFmtpLine: "minptime=10;useinbandfec=1;stereo=1;sprop-stereo=1",
		},
		&format.Opus{
			PayloadTyp:   96,
			ChannelCount: 2,
		},
	},
	{
		"opus mono",
		&format.Opus{
			PayloadTyp:   111,
			ChannelCount: 1,
		},
		webrtc.RTPCodecCapability{
			MimeType:    "audio/opus",
			ClockRate:   48000,
			Channels:    2,
			SDPFmtpLine: "minptime=10;useinbandfec=1",
		},
		&format.Opus{
			PayloadTyp:   96,
			ChannelCount: 1,
		},
	},
	{
		"g722",
		&format.G722{},
		webrtc.RTPCodecCapability{
			MimeType:  "audio/G722",
			ClockRate: 8000,
		},
		&format.G722{},
	},
	{
		"g711 pcma 8khz mono",
		&format.G711{
			PayloadTyp:   8,
			SampleRate:   8000,
			ChannelCount: 1,
		},
		webrtc.RTPCodecCapability{
			MimeType:  "audio/PCMA",
			ClockRate: 8000,
		},
		&format.G711{
			PayloadTyp:   8,
			SampleRate:   8000,
			ChannelCount: 1,
		},
	},
	{
		"g711 pcmu 8khz mono",
		&format.G711{
			MULaw:        true,
			PayloadTyp:   0,
			SampleRate:   8000,
			ChannelCount: 1,
		},
		webrtc.RTPCodecCapability{
			MimeType:  "audio/PCMU",
			ClockRate: 8000,
		},
		&format.G711{
			MULaw:        true,
			PayloadTyp:   0,
			SampleRate:   8000,
			ChannelCount: 1,
		},
	},
	{
		"g711 pcma 8khz stereo",
		&format.G711{
			PayloadTyp:   96,
			SampleRate:   8000,
			ChannelCount: 2,
		},
		webrtc.RTPCodecCapability{
			MimeType:  "audio/PCMA",
			ClockRate: 8000,
			Channels:  2,
		},
		&format.G711{
			PayloadTyp:   119,
			SampleRate:   8000,
			ChannelCount: 2,
		},
	},
	{
		"g711 pcmu 8khz stereo",
		&format.G711{
			MULaw:        true,
			PayloadTyp:   96,
			SampleRate:   8000,
			ChannelCount: 2,
		},
		webrtc.RTPCodecCapability{
			MimeType:  "audio/PCMU",
			ClockRate: 8000,
			Channels:  2,
		},
		&format.G711{
			MULaw:        true,
			PayloadTyp:   118,
			SampleRate:   8000,
			ChannelCount: 2,
		},
	},
	{
		"g711 pcma 16khz stereo",
		&format.G711{
			PayloadTyp:   96,
			SampleRate:   16000,
			ChannelCount: 2,
		},
		webrtc.RTPCodecCapability{
			MimeType:  "audio/L16",
			ClockRate: 16000,
			Channels:  2,
		},
		&format.LPCM{
			PayloadTyp:   96,
			BitDepth:     16,
			SampleRate:   16000,
			ChannelCount: 2,
		},
	},
	{
		"g711 pcmu 16khz stereo",
		&format.G711{
			MULaw:        true,
			PayloadTyp:   96,
			SampleRate:   16000,
			ChannelCount: 2,
		},
		webrtc.RTPCodecCapability{
			MimeType:  "audio/L16",
			ClockRate: 16000,
			Channels:  2,
		},
		&format.LPCM{
			PayloadTyp:   96,
			BitDepth:     16,
			SampleRate:   16000,
			ChannelCount: 2,
		},
	},
	{
		"l16 8khz stereo",
		&format.LPCM{
			PayloadTyp:   96,
			BitDepth:     16,
			SampleRate:   8000,
			ChannelCount: 2,
		},
		webrtc.RTPCodecCapability{
			MimeType:  "audio/L16",
			ClockRate: 8000,
			Channels:  2,
		},
		&format.LPCM{
			PayloadTyp:   96,
			BitDepth:     16,
			SampleRate:   8000,
			ChannelCount: 2,
		},
	},
	{
		"l16 16khz stereo",
		&format.LPCM{
			PayloadTyp:   96,
			BitDepth:     16,
			SampleRate:   16000,
			ChannelCount: 2,
		},
		webrtc.RTPCodecCapability{
			MimeType:  "audio/L16",
			ClockRate: 16000,
			Channels:  2,
		},
		&format.LPCM{
			PayloadTyp:   96,
			BitDepth:     16,
			SampleRate:   16000,
			ChannelCount: 2,
		},
	},
	{
		"l16 48khz stereo",
		&format.LPCM{
			PayloadTyp:   96,
			BitDepth:     16,
			SampleRate:   48000,
			ChannelCount: 2,
		},
		webrtc.RTPCodecCapability{
			MimeType:  "audio/L16",
			ClockRate: 48000,
			Channels:  2,
		},
		&format.LPCM{
			PayloadTyp:   96,
			BitDepth:     16,
			SampleRate:   48000,
			ChannelCount: 2,
		},
	},
}

func TestToStream(t *testing.T) {
	for _, ca := range toFromStreamCases {
		t.Run(ca.name, func(t *testing.T) {
			pc1 := &PeerConnection{
				LocalRandomUDP:     true,
				IPsFromInterfaces:  true,
				HandshakeTimeout:   conf.Duration(10 * time.Second),
				TrackGatherTimeout: conf.Duration(2 * time.Second),
				Publish:            true,
				OutgoingTracks: []*OutgoingTrack{{
					Caps: ca.webrtcCaps,
				}},
				Log: test.NilLogger,
			}
			err := pc1.Start()
			require.NoError(t, err)
			defer pc1.Close()

			pc2 := &PeerConnection{
				LocalRandomUDP:     true,
				IPsFromInterfaces:  true,
				HandshakeTimeout:   conf.Duration(10 * time.Second),
				TrackGatherTimeout: conf.Duration(2 * time.Second),
				Publish:            false,
				Log:                test.NilLogger,
			}
			err = pc2.Start()
			require.NoError(t, err)
			defer pc2.Close()

			offer, err := pc1.CreatePartialOffer()
			require.NoError(t, err)

			answer, err := pc2.CreateFullAnswer(offer)
			require.NoError(t, err)

			err = pc1.SetAnswer(answer)
			require.NoError(t, err)

			go func() {
				for {
					select {
					case cnd := <-pc1.NewLocalCandidate():
						err2 := pc2.AddRemoteCandidate(cnd)
						require.NoError(t, err2)

					case <-pc1.Connected():
						return
					}
				}
			}()

			err = pc1.WaitUntilConnected()
			require.NoError(t, err)

			err = pc2.WaitUntilConnected()
			require.NoError(t, err)

			err = pc1.OutgoingTracks[0].WriteRTP(&rtp.Packet{
				Header: rtp.Header{
					Version:        2,
					Marker:         true,
					PayloadType:    111,
					SequenceNumber: 1123,
					Timestamp:      45343,
					SSRC:           563424,
				},
				Payload: []byte{5, 2},
			})
			require.NoError(t, err)

			err = pc2.GatherIncomingTracks()
			require.NoError(t, err)

			var stream *stream.Stream
			medias, err := ToStream(pc2, &stream)
			require.NoError(t, err)
			require.Equal(t, ca.out, medias[0].Formats[0])
		})
	}
}
