package headers

import (
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/bluenviron/gortsplib/v5/pkg/base"
)

func ptrOf[T any](v T) *T {
	return &v
}

var casesAuthenticate = []struct {
	name string
	vin  base.HeaderValue
	vout base.HeaderValue
	h    Authenticate
}{
	{
		"basic",
		base.HeaderValue{`Basic realm="4419b63f5e51"`},
		base.HeaderValue{`Basic realm="4419b63f5e51"`},
		Authenticate{
			Method: AuthMethodBasic,
			Realm:  "4419b63f5e51",
		},
	},
	{
		"digest 1",
		base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`},
		base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", ` +
			`stale="FALSE"`},
		Authenticate{
			Method: AuthMethodDigest,
			Realm:  "4419b63f5e51",
			Nonce:  "8b84a3b789283a8bea8da7fa7d41f08b",
			Stale:  ptrOf("FALSE"),
		},
	},
	{
		"digest 2",
		base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale=FALSE`},
		base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", ` +
			`stale="FALSE"`},
		Authenticate{
			Method: AuthMethodDigest,
			Realm:  "4419b63f5e51",
			Nonce:  "8b84a3b789283a8bea8da7fa7d41f08b",
			Stale:  ptrOf("FALSE"),
		},
	},
	{
		"digest 3",
		base.HeaderValue{`Digest realm="4419b63f5e51",nonce="133767111917411116111311118211673010032",  stale="FALSE"`},
		base.HeaderValue{`Digest realm="4419b63f5e51", nonce="133767111917411116111311118211673010032", ` +
			`stale="FALSE"`},
		Authenticate{
			Method: AuthMethodDigest,
			Realm:  "4419b63f5e51",
			Nonce:  "133767111917411116111311118211673010032",
			Stale:  ptrOf("FALSE"),
		},
	},
	{
		"digest after failed auth",
		base.HeaderValue{`Digest realm="Please log in with a valid username",` +
			`nonce="752a62306daf32b401a41004555c7663",opaque="",stale=FALSE`},
		base.HeaderValue{`Digest realm="Please log in with a valid username", ` +
			`nonce="752a62306daf32b401a41004555c7663", opaque="", stale="FALSE"`},
		Authenticate{
			Method: AuthMethodDigest,
			Realm:  "Please log in with a valid username",
			Nonce:  "752a62306daf32b401a41004555c7663",
			Opaque: ptrOf(""),
			Stale:  ptrOf("FALSE"),
		},
	},
	{
		"digest md5 explicit",
		base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", ` +
			`stale="FALSE", algorithm="MD5"`},
		base.HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", ` +
			`stale="FALSE", algorithm="MD5"`},
		Authenticate{
			Method:    AuthMethodDigest,
			Realm:     "4419b63f5e51",
			Nonce:     "8b84a3b789283a8bea8da7fa7d41f08b",
			Stale:     ptrOf("FALSE"),
			Algorithm: ptrOf(AuthAlgorithmMD5),
		},
	},
	{
		"digest sha256",
		base.HeaderValue{`Digest realm="IP Camera(AB705)", ` +
			`nonce="fcc86deace979a488b2bfb89f4d0812c", algorithm="SHA-256", stale="FALSE"`},
		base.HeaderValue{`Digest realm="IP Camera(AB705)", ` +
			`nonce="fcc86deace979a488b2bfb89f4d0812c", stale="FALSE", algorithm="SHA-256"`},
		Authenticate{
			Method:    AuthMethodDigest,
			Realm:     "IP Camera(AB705)",
			Nonce:     "fcc86deace979a488b2bfb89f4d0812c",
			Stale:     ptrOf("FALSE"),
			Algorithm: ptrOf(AuthAlgorithmSHA256),
		},
	},
}

func TestAuthenticateUnmarshal(t *testing.T) {
	for _, ca := range casesAuthenticate {
		t.Run(ca.name, func(t *testing.T) {
			var h Authenticate
			err := h.Unmarshal(ca.vin)
			require.NoError(t, err)
			require.Equal(t, ca.h, h)
		})
	}
}

func TestAuthenticateMarshal(t *testing.T) {
	for _, ca := range casesAuthenticate {
		t.Run(ca.name, func(t *testing.T) {
			vout := ca.h.Marshal()
			require.Equal(t, ca.vout, vout)
		})
	}
}

func FuzzAuthenticateUnmarshal(f *testing.F) {
	for _, ca := range casesAuthenticate {
		f.Add(ca.vin[0])
	}

	f.Fuzz(func(_ *testing.T, b string) {
		var h Authenticate
		err := h.Unmarshal(base.HeaderValue{b})
		if err != nil {
			return
		}

		h.Marshal()
	})
}

func TestAuthenticateAdditionalErrors(t *testing.T) {
	func() {
		var h Authenticate
		err := h.Unmarshal(base.HeaderValue{})
		require.Error(t, err)
	}()

	func() {
		var h Authenticate
		err := h.Unmarshal(base.HeaderValue{"a", "b"})
		require.Error(t, err)
	}()
}
