package template

import (
	"errors"
	"fmt"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

var errHasExists = errors.New("key has exists")

type Interface interface {
	Add(key string, obj client.Object) error
	Get(key string) (client.Object, bool)
	Flush(data map[string]string) error
}

func New(scheme *runtime.Scheme) Interface {
	return &repo{
		codecs:    serializer.NewCodecFactory(scheme),
		templates: make(map[string]client.Object),
	}
}

type repo struct {
	codecs    serializer.CodecFactory
	templates map[string]client.Object
}

func (r *repo) Add(key string, obj client.Object) error {
	if _, ok := r.templates[key]; ok {
		return fmt.Errorf("key %s: %w", key, errHasExists)
	}

	r.templates[key] = obj

	return nil
}

func (r *repo) Get(key string) (client.Object, bool) {
	tmpl, ok := r.templates[key]
	if !ok {
		return nil, false
	}

	obj, ok := tmpl.DeepCopyObject().(client.Object)
	if !ok {
		return nil, false
	}

	return obj, true
}

func (r *repo) Flush(data map[string]string) error {
	newTemplates := map[string]client.Object{}

	for key, obj := range r.templates {
		val, ok := data[key]
		if !ok {
			return fmt.Errorf("key %s does not exist", key)
		}
		newObj, ok := obj.DeepCopyObject().(client.Object)
		if !ok {
			return fmt.Errorf("object with key %s is not client.Object", key)
		}
		if err := runtime.DecodeInto(r.codecs.UniversalDecoder(), []byte(val), newObj); err != nil {
			return fmt.Errorf("can't decode val of key %s to obj %v: %w", key, obj.GetObjectKind().GroupVersionKind(), err)
		}
		newTemplates[key] = newObj
	}

	r.templates = newTemplates

	return nil
}
