package ldservices

import (
	"encoding/json"
	"fmt"

	"github.com/launchdarkly/go-sdk-common/v3/ldvalue"
	"github.com/launchdarkly/go-test-helpers/v3/httphelpers"
	"github.com/launchdarkly/go-test-helpers/v3/jsonhelpers"
)

type fakeFlagOrSegment struct {
	Key     string `json:"key"`
	Version int    `json:"version"`
}

// FlagOrSegment provides a simple object that has only "key" and "version" properties.
// This may be enough for some testing purposes that don't require full flag or segment data.
func FlagOrSegment(key string, version int) interface{} {
	return fakeFlagOrSegment{Key: key, Version: version}
}

// ServerSDKData is a convenience type for constructing a test server-side SDK data payload for PollingServiceHandler
// or StreamingServiceHandler. Its String() method returns a JSON object with the expected "flags" and "segments"
// properties.
//
//	data := NewServerSDKData().Flags(flag1, flag2)
//	handler := PollingServiceHandler(data)
type ServerSDKData struct {
	FlagsMap    map[string]interface{} `json:"flags"`
	SegmentsMap map[string]interface{} `json:"segments"`
}

// NewServerSDKData creates a ServerSDKData instance.
func NewServerSDKData() *ServerSDKData {
	return &ServerSDKData{make(map[string]interface{}), make(map[string]interface{})}
}

// String returns the JSON encoding of the struct as a string.
func (s *ServerSDKData) String() string {
	bytes, _ := json.Marshal(*s)
	return string(bytes)
}

// Flags adds the specified items to the struct's "flags" map.
//
// Each item may be either a object produced by FlagOrSegment or a real data model object from the ldmodel
// package. The minimum requirement is that when converted to JSON, it has a "key" property.
func (s *ServerSDKData) Flags(flags ...interface{}) *ServerSDKData {
	for _, flag := range flags {
		if key := getKeyFromJSON(flag); key != "" {
			s.FlagsMap[key] = flag
		}
	}
	return s
}

// Segments adds the specified items to the struct's "segments" map.
//
// Each item may be either a object produced by FlagOrSegment or a real data model object from the ldmodel
// package. The minimum requirement is that when converted to JSON, it has a "key" property.
func (s *ServerSDKData) Segments(segments ...interface{}) *ServerSDKData {
	for _, segment := range segments {
		if key := getKeyFromJSON(segment); key != "" {
			s.SegmentsMap[key] = segment
		}
	}
	return s
}

func getKeyFromJSON(item interface{}) string {
	return ldvalue.Parse(jsonhelpers.ToJSON(item)).GetByKey("key").StringValue()
}

// ToPutEvent creates an SSE event in the format that is used by the server-side SDK streaming endpoint.
func (s *ServerSDKData) ToPutEvent() httphelpers.SSEEvent {
	return httphelpers.SSEEvent{
		Event: "put",
		Data:  fmt.Sprintf(`{"path": "/", "data": %s}`, s),
	}
}
