//go:generate ${GOBIN}/mockgen -destination config_loader_mock.go -package=loader ${GO_MODULE}/config/loader ConfigurationSource
package loader

import (
	"context"
	"fmt"

	"dario.cat/mergo"
	"github.com/go-playground/validator/v10"
)

var validate = validator.New()

type ConfigurationSource[T any] interface {
	Load(ctx context.Context, pre *T) (*T, error)
}

type ConfigurationLoader[T any] struct {
	sources      []ConfigurationSource[T]
	skipValidate bool
}

func NewConfigurationLoader[T any](sources []ConfigurationSource[T], skipValidate bool) *ConfigurationLoader[T] {
	return &ConfigurationLoader[T]{
		sources:      sources,
		skipValidate: skipValidate,
	}
}

func (l *ConfigurationLoader[T]) Load(ctx context.Context) (*T, error) {
	finalConf := new(T)

	for _, source := range l.sources {
		curConf, err := source.Load(ctx, finalConf)
		if err != nil {
			return nil, err
		}
		if curConf == nil {
			continue
		}

		if err := mergo.MergeWithOverwrite(finalConf, curConf); err != nil {
			return nil, fmt.Errorf("failed to merge config: %w", err)
		}
	}

	if !l.skipValidate {
		if err := validate.Struct(finalConf); err != nil {
			return nil, fmt.Errorf("failed to validate config: %w", err)
		}
	}

	return finalConf, nil
}
