package featuremgmt

import (
	"context"
	"fmt"

	"github.com/open-feature/go-sdk/openfeature"
	goffmodel "github.com/thomaspoignant/go-feature-flag/cmd/relayproxy/model"

	"github.com/grafana/grafana/pkg/infra/log"
	"github.com/grafana/grafana/pkg/setting"
)

// StaticFlagEvaluator provides methods for evaluating static feature flags
// it is only used when static provider is configured
type StaticFlagEvaluator interface {
	EvalFlag(ctx context.Context, flagKey string) (goffmodel.OFREPEvaluateSuccessResponse, error)
	EvalAllFlags(ctx context.Context) (goffmodel.OFREPBulkEvaluateSuccessResponse, error)
}

// CreateStaticEvaluator is a dependency for ofrep APIBuilder
func CreateStaticEvaluator(cfg *setting.Cfg) (StaticFlagEvaluator, error) {
	if cfg.OpenFeature.ProviderType != setting.StaticProviderType {
		return nil, fmt.Errorf("provider is not a static provider, type %s", setting.StaticProviderType)
	}

	staticFlags, err := setting.ReadFeatureTogglesFromInitFile(cfg.Raw.Section("feature_toggles"))
	if err != nil {
		return nil, fmt.Errorf("failed to read feature flags from config: %w", err)
	}

	staticProvider, err := newStaticProvider(staticFlags)
	if err != nil {
		return nil, fmt.Errorf("failed to create static provider: %w", err)
	}

	p, ok := staticProvider.(*inMemoryBulkProvider)
	if !ok {
		return nil, fmt.Errorf("static provider is not of type inMemoryBulkProvider")
	}

	c := openfeature.NewDefaultClient()

	return &staticEvaluator{
		provider: p,
		client:   c,
		log:      log.New("static-evaluator"),
	}, nil
}

// staticEvaluator implements StaticFlagEvaluator for static providers
type staticEvaluator struct {
	provider *inMemoryBulkProvider
	client   openfeature.IClient
	log      log.Logger
}

func (s *staticEvaluator) EvalFlag(ctx context.Context, flagKey string) (goffmodel.OFREPEvaluateSuccessResponse, error) {
	result, err := s.client.BooleanValueDetails(ctx, flagKey, false, openfeature.TransactionContext(ctx))
	if err != nil {
		return goffmodel.OFREPEvaluateSuccessResponse{}, fmt.Errorf("failed to evaluate flag %s: %w", flagKey, err)
	}
	resp := goffmodel.OFREPEvaluateSuccessResponse{
		Key:      flagKey,
		Value:    result.Value,
		Reason:   "static provider evaluation result",
		Variant:  result.Variant,
		Metadata: result.FlagMetadata,
	}

	return resp, nil
}

func (s *staticEvaluator) EvalAllFlags(ctx context.Context) (goffmodel.OFREPBulkEvaluateSuccessResponse, error) {
	flags, err := s.provider.ListFlags()
	if err != nil {
		return goffmodel.OFREPBulkEvaluateSuccessResponse{}, fmt.Errorf("static provider failed to list all flags: %w", err)
	}

	allFlags := make([]goffmodel.OFREPFlagBulkEvaluateSuccessResponse, 0, len(flags))
	for _, flagKey := range flags {
		result, err := s.client.BooleanValueDetails(ctx, flagKey, false, openfeature.TransactionContext(ctx))
		if err != nil {
			s.log.Error("failed to evaluate flag during bulk evaluation", "flagKey", flagKey, "error", err)
			continue
		}

		allFlags = append(allFlags, goffmodel.OFREPFlagBulkEvaluateSuccessResponse{
			OFREPEvaluateSuccessResponse: goffmodel.OFREPEvaluateSuccessResponse{
				Key:      flagKey,
				Value:    result.Value,
				Reason:   "static provider evaluation result",
				Variant:  result.Variant,
				Metadata: result.FlagMetadata,
			},
			ErrorCode:    string(result.ErrorCode),
			ErrorDetails: result.ErrorMessage,
		})
	}

	return goffmodel.OFREPBulkEvaluateSuccessResponse{Flags: allFlags}, nil
}
