package prototool_test

import (
	"net/http"
	"strconv"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/stretchr/testify/assert"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/httpz"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/prototool"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool/testing/mock_prototool"
	"google.golang.org/protobuf/testing/protocmp"
)

func TestEquate(t *testing.T) {
	v1 := []*prototool.HeaderKV{
		mock_prototool.NewHeaderKV("k1", "v1", "v2"),
		mock_prototool.NewHeaderKV("k2", "v3", "v4"),
		nil,
	}
	v2 := []*prototool.HeaderKV{
		mock_prototool.NewHeaderKV("k2", "v3", "v4"),
		mock_prototool.NewHeaderKV("k1", "v1", "v2"),
		nil,
	}
	t.Run("slice", func(t *testing.T) {
		eq := cmp.Equal(v1, v2, protocmp.Transform(), mock_prototool.Equate())
		assert.True(t, eq)
	})
	t.Run("nested", func(t *testing.T) {
		m1 := &prototool.HttpResponse{
			StatusCode: http.StatusOK,
			Status:     "OK",
			Header:     v1,
		}
		m2 := &prototool.HttpResponse{
			StatusCode: http.StatusOK,
			Status:     "OK",
			Header:     v2,
		}
		eq := cmp.Equal(m1, m2, protocmp.Transform(), mock_prototool.Equate())
		assert.True(t, eq)
	})
}

func TestHeaderKVToHTTPHeader(t *testing.T) {
	tests := []struct {
		input          []*prototool.HeaderKV
		expectedOutput http.Header
	}{
		{
			input:          nil,
			expectedOutput: nil,
		},
		{
			input:          []*prototool.HeaderKV{},
			expectedOutput: nil,
		},
		{
			input: []*prototool.HeaderKV{
				mock_prototool.NewHeaderKV("key1", "s1", "s2"),
				mock_prototool.NewHeaderKV("key2"),
				mock_prototool.NewHeaderKV("key3", "s3", "s4"),
			},
			expectedOutput: http.Header{
				"Key1": []string{"s1", "s2"},
				"Key2": []string{},
				"Key3": []string{"s3", "s4"},
			},
		},
		{
			input: []*prototool.HeaderKV{
				mock_prototool.NewHeaderKV("content-type", "v1"),
				mock_prototool.NewHeaderKV("keep-alive", "1"),
				mock_prototool.NewHeaderKV("user-agent"),
			},
			expectedOutput: http.Header{
				httpz.ContentTypeHeader: []string{"v1"},
				httpz.KeepAliveHeader:   []string{"1"},
				httpz.UserAgentHeader:   []string{},
			},
		},
		{
			input: []*prototool.HeaderKV{
				mock_prototool.NewHeaderKV("NEL"),
			},
			expectedOutput: http.Header{
				httpz.NELHeader: []string{},
			},
		},
		{
			input: []*prototool.HeaderKV{
				mock_prototool.NewHeaderKV("Nel"),
			},
			expectedOutput: http.Header{
				httpz.NELHeader: []string{},
			},
		},
		{
			input: []*prototool.HeaderKV{
				mock_prototool.NewHeaderKV("nel"),
			},
			expectedOutput: http.Header{
				httpz.NELHeader: []string{},
			},
		},
		{
			input: []*prototool.HeaderKV{
				mock_prototool.NewHeaderKV("NeL"),
			},
			expectedOutput: http.Header{
				httpz.NELHeader: []string{},
			},
		},
	}
	for i, tc := range tests {
		t.Run(strconv.Itoa(i), func(t *testing.T) {
			meta := prototool.HeaderKVToHTTPHeader(tc.input)
			assert.Empty(t, cmp.Diff(tc.expectedOutput, meta))
		})
	}
}

func BenchmarkHTTPHeaderToHeaderKV(b *testing.B) {
	md := http.Header{
		"key1": []string{"s1", "s2"},
		"key2": []string{},
		"key3": []string{"s3", "s4"},
		"key4": []string{"s3", "s4"},
		"key5": []string{"s3", "s4"},
		"key6": []string{"s3", "s4"},
	}
	var sink []*prototool.HeaderKV
	b.ReportAllocs()

	for b.Loop() {
		sink = prototool.HTTPHeaderToHeaderKV(md)
	}
	_ = sink
}

func BenchmarkHeaderKVToHTTPHeader(b *testing.B) {
	input := []*prototool.HeaderKV{
		mock_prototool.NewHeaderKV("key1", "s1", "s2"),
		mock_prototool.NewHeaderKV("key2"),
		mock_prototool.NewHeaderKV("key3", "s3", "s4"),
		mock_prototool.NewHeaderKV("key4", "s3", "s4"),
		mock_prototool.NewHeaderKV("key5", "s3", "s4"),
		mock_prototool.NewHeaderKV("key6", "s3", "s4"),
	}
	var sink http.Header

	b.ReportAllocs()

	for b.Loop() {
		sink = prototool.HeaderKVToHTTPHeader(input)
	}
	_ = sink
}
