package prototool

import (
	"fmt"
	"net/http"
	"net/textproto"
	"net/url"

	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v18/internal/tool"
	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/proto"
	"sigs.k8s.io/yaml"
)

func ParseYAMLToProto(rawYAML []byte, msg proto.Message) error {
	rawJSON, err := yaml.YAMLToJSON(rawYAML)
	if err != nil {
		return fmt.Errorf("YAMLToJSON: %w", err)
	}
	if string(rawJSON) == "null" { // doesn't allocate
		// Empty YAML
		return nil
	}
	err = protojson.Unmarshal(rawJSON, msg)
	if err != nil {
		return fmt.Errorf("protojson.Unmarshal: %w", err)
	}
	return nil
}

func URLValuesToQueryKV(from url.Values) []*QueryKV {
	return tool.StringMapToSliceWithBytes[*QueryKV, url.Values, []*QueryKV](
		from,
		func(key string, elem [][]byte) *QueryKV {
			return &QueryKV{
				Key: []byte(key),
				Value: &QueryValues{
					Value: elem,
				},
			}
		},
	)
}

func QueryKVToURLValues(from []*QueryKV) url.Values {
	return tool.SliceToMap[string, string, *QueryKV, []string, []*QueryKV, url.Values](
		from,
		func(v *QueryKV) int {
			return len(v.Value.Value)
		},
		func(kv *QueryKV, sink []string) []string {
			for _, v := range kv.Value.Value {
				sink = append(sink, string(v))
			}
			return sink
		},
		func(kv *QueryKV, elem []string) (string, []string) {
			return string(kv.Key), elem
		},
	)
}

// HeaderKVToHTTPHeader converts the internal []*HeaderKV type to http.Header.
// The returned http.Header has canonicalized keys.
func HeaderKVToHTTPHeader(from []*HeaderKV) http.Header {
	return tool.SliceToMap[string, string, *HeaderKV, []string, []*HeaderKV, http.Header](
		from,
		func(v *HeaderKV) int {
			return len(v.Value.Value)
		},
		func(kv *HeaderKV, sink []string) []string {
			for _, v := range kv.Value.Value {
				sink = append(sink, string(v))
			}
			return sink
		},
		func(kv *HeaderKV, elem []string) (string, []string) {
			return textproto.CanonicalMIMEHeaderKey(string(kv.Key)), elem
		},
	)
}

func HTTPHeaderToHeaderKV(from http.Header) []*HeaderKV {
	return tool.StringMapToSliceWithBytes[*HeaderKV, http.Header, []*HeaderKV](
		from,
		func(key string, elem [][]byte) *HeaderKV {
			return &HeaderKV{
				Key: []byte(key),
				Value: &HeaderValues{
					Value: elem,
				},
			}
		},
	)
}
