package encryption

import (
	"time"

	"github.com/aws/aws-sdk-go/aws/client"
	"github.com/aws/aws-sdk-go/service/kms"
	"github.com/pkg/errors"

	"code.justin.tv/amzn/TwitchOLE/ole"
	"code.justin.tv/amzn/TwitchOLE/ole/oleiface"
)

type AuthorizedFieldClient struct {
	ole oleiface.OleAPI
}

type authorizedFieldClientConfig struct {
	ole *ole.KMSOleClientConfig
}

type AuthorizedFieldClientOption func(*authorizedFieldClientConfig)

// NewAuthorizedFieldClient creates a wrapper around an OLE client for use in encrypting and decrypting
// authorized fields of EventBus events.
func NewAuthorizedFieldClient(sess client.ConfigProvider, opts ...AuthorizedFieldClientOption) (*AuthorizedFieldClient, error) {
	cfg := defaultOLEConfig()
	for _, option := range opts {
		option(cfg)
	}

	kmsClient := kms.New(sess)
	c, err := ole.NewKMSOleClient(kmsClient, *cfg.ole)
	if err != nil {
		return nil, errors.Wrap(err, "could not initialize OLE client")
	}

	return &AuthorizedFieldClient{
		ole: c,
	}, nil
}

// WithCMKArn allows for overriding the default CMK used to perform cryptographic operations
// against. It is not recommended that this option is used unless you know what you are doing!
func WithCMKArn(arn string) AuthorizedFieldClientOption {
	return func(cfg *authorizedFieldClientConfig) {
		cfg.ole.CMKArn = arn
	}
}

// WithDataKeyTTL configures the default maximum time to live for a data encryption key. Use this
// option to tune performance of your AuthorizedFieldClient to optimize number of KMS calls the application makes.
func WithDataKeyTTL(ttl time.Duration) AuthorizedFieldClientOption {
	return func(cfg *authorizedFieldClientConfig) {
		cfg.ole.DataKeyTTL = ttl
	}
}

// WithDataKeyTTLSplay configures the random time added to the data encryption key TTL. Recommended
// for use when also using WithDataKeyTTL. A recommended value is 10% of the DataKeyTTL.
func WithDataKeyTTLSplay(splay time.Duration) AuthorizedFieldClientOption {
	return func(cfg *authorizedFieldClientConfig) {
		cfg.ole.DataKeyTTLSplay = splay
	}
}

// WithEncryptionKeyMaxUsage configures the number of times a single data encryption key may be used
// before it is discarded and replaced. For high throughput applications, this may need to be increased
// to reduce the number of calls to KMS for new data encryption keys.
func WithEncryptionKeyMaxUsage(max int64) AuthorizedFieldClientOption {
	return func(cfg *authorizedFieldClientConfig) {
		cfg.ole.EncryptionKeyMaxUsage = max
	}
}

func defaultOLEConfig() *authorizedFieldClientConfig {
	return &authorizedFieldClientConfig{
		ole: &ole.KMSOleClientConfig{
			CMKArn:                DefaultCMKArn,
			DataKeyTTL:            DefaultDataKeyTTL,
			DataKeyTTLSplay:       DefaultDataKeyTTLSplay,
			EncryptionKeyMaxUsage: DefaultEncryptionKeyMaxUsage,
			MaxCacheSize:          DefaultMaxCacheSize,
		},
	}
}
