// Copyright 2022 Clastix Labs
// SPDX-License-Identifier: Apache-2.0

package utilities

import (
	"bytes"
	"fmt"
	"sort"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/serializer/json"
	"k8s.io/apimachinery/pkg/util/sets"

	kamajiv1alpha1 "github.com/clastix/kamaji/api/v1alpha1"
	"github.com/clastix/kamaji/internal/constants"
)

const (
	separator = "-"
)

func KamajiLabels(tcpName, resourceName string) map[string]string {
	return map[string]string{
		constants.ProjectNameLabelKey:       constants.ProjectNameLabelValue,
		constants.ControlPlaneLabelKey:      tcpName,
		constants.ControlPlaneLabelResource: resourceName,
	}
}

func MergeMaps(maps ...map[string]string) map[string]string {
	result := map[string]string{}

	for _, m := range maps {
		for k, v := range m {
			result[k] = v
		}
	}

	return result
}

func AddTenantPrefix(name string, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) string {
	return fmt.Sprintf("%s%s%s", tenantControlPlane.GetName(), separator, name)
}

// EncodeToYaml returns the given object in yaml format and the error.
func EncodeToYaml(o runtime.Object) ([]byte, error) {
	scheme := runtime.NewScheme()

	encoder := json.NewSerializerWithOptions(json.SimpleMetaFactory{}, scheme, scheme, json.SerializerOptions{
		Yaml:   true,
		Pretty: false,
		Strict: false,
	})

	buf := bytes.NewBuffer([]byte{})

	if err := encoder.Encode(o, buf); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

func DecodeFromYAML(o string, to runtime.Object) (err error) {
	scheme := runtime.NewScheme()

	encoder := json.NewSerializerWithOptions(json.SimpleMetaFactory{}, scheme, scheme, json.SerializerOptions{
		Yaml:   true,
		Pretty: false,
		Strict: false,
	})

	if to, _, err = encoder.Decode([]byte(o), nil, to); err != nil { //nolint:ineffassign,staticcheck,wastedassign
		return
	}

	return
}

func DecodeFromJSON(o string, to runtime.Object) (err error) {
	scheme := runtime.NewScheme()

	encoder := json.NewSerializerWithOptions(json.SimpleMetaFactory{}, scheme, scheme, json.SerializerOptions{
		Yaml:   false,
		Pretty: false,
		Strict: false,
	})

	if to, _, err = encoder.Decode([]byte(o), nil, to); err != nil { //nolint:ineffassign,staticcheck,wastedassign
		return
	}

	return
}

// EncodeToJSON returns the given object in JSON format and the error, respecting the Kubernetes struct tags.
func EncodeToJSON(o runtime.Object) ([]byte, error) {
	scheme := runtime.NewScheme()

	encoder := json.NewSerializerWithOptions(json.SimpleMetaFactory{}, scheme, scheme, json.SerializerOptions{
		Yaml:   false,
		Pretty: false,
		Strict: false,
	})

	buf := bytes.NewBuffer([]byte{})

	if err := encoder.Encode(o, buf); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

// GetEffectiveCIDRs returns the CIDRs to use.
//
// If the new CIDRs field is populated, it is considered authoritative.
// The deprecated field is only used as a fallback for backward compatibility.
func GetEffectiveCIDRs(deprecated string, current []string) []string {
	if len(current) > 0 {
		return UniqueStrings(current)
	}

	if deprecated != "" {
		return []string{deprecated}
	}

	return nil
}

// UniqueStrings returns a slice of unique strings from the provided slice.
func UniqueStrings(input []string) []string {
	unique := sets.New[string](input...)

	result := unique.UnsortedList()
	sort.Strings(result)

	return result
}
