package c7s

import (
	"fmt"
	"strings"
)

//go:generate go run -tags generate ./internal/GenerateTwitchS2S2Config -t merge -o ./configs_gen.go
//go:generate go run -tags generate ./internal/GenerateTwitchS2S2Config -t tests -o ./configs_gen_test.go

// Config is the core configuration struct for the service
type Config struct {
	// Required Configuration
	// The S2S2 service name retrievable from the S2S dashboard.
	ClientServiceName string `c7:"clientServiceName"`

	// Optional
	// Number of access tokens to cache in memory.
	// Each access token is approximately 300 Bytes.
	// This value must be greater than or equal to 0.
	AccessTokenCacheSize int64 `c7:"accessTokenCacheSize"`

	// WARNING: This option is now deprecated in favor of ClientServiceName.
	ClientServiceURI string `c7:"clientServiceUri"`

	// Optional Configuration
	// If access logging is to be enabled. This defaults to disabled
	// Cloudwatch bottlenecks ingested requests at ~6k RPS
	EnableAccessLogging bool `c7:"enableAccessLogging"`

	// Below configuration is only used for testing.
	// Should generally NOT be set.
	AWSRegion string `c7:"awsRegion"`
	// generally https://names.STAGE.services.s2s.twitch.a2z.com
	ServiceURIByNameBase string `c7:"serviceURIByNameBase"`
	CalleeRealm          string `c7:"calleeRealm"`
	// See https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig
	DiscoveryEndpoint string `c7:"discoveryEndpoint"`
	Issuer            string `c7:"issuer"`
	TokenScope        string `c7:"tokenScope"`
}

func newErrMissingConfiguration(attributes ...string) *ErrMissingConfiguration {
	attributesSet := make(map[string]interface{})
	for _, attr := range attributes {
		attributesSet[attr] = nil
	}
	return &ErrMissingConfiguration{attributes: attributesSet}
}

// ErrMissingConfiguration is returned if a configuration attribute is missing.
type ErrMissingConfiguration struct {
	attributes map[string]interface{}
}

func (e ErrMissingConfiguration) Error() string {
	attributes := make([]string, 0, len(e.attributes))
	for attr := range e.attributes {
		attributes = append(attributes, attr)
	}
	return fmt.Sprintf("configuration required: %s", strings.Join(attributes, ","))
}

func (e *ErrMissingConfiguration) addAttributes(other []string) {
	for _, attr := range other {
		e.attributes[attr] = nil
	}
}

type errMissingConfigurationMergable interface {
	addAttributes(other []string)
}

// validateSpecialCases is used in the generated Validate function. this will
// check special cases that cannot be automatically generated.
func (cfg *Config) validateSpecialCases() error {
	missing := make([]string, 0)

	// either ClientServiceName or ClientServiceURI can be set.
	if cfg.ClientServiceName == "" && cfg.ClientServiceURI == "" {
		missing = append(missing, "ClientServiceName")
	}

	// AccessTokenCacheSize can't be negative
	if cfg.AccessTokenCacheSize <= 0 {
		missing = append(missing, "AccessTokenCacheSize")
	}

	if len(missing) > 0 {
		return newErrMissingConfiguration(missing...)
	}

	return nil
}
