package manager

import (
	"log"
	"os"

	"code.justin.tv/common/ddbmetrics"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/dynamodb"
	"github.com/aws/aws-sdk-go/service/kms"
	"github.com/cactus/go-statsd-client/statsd"
	std "github.com/derekdowling/go-stdlogger"
)

// Manager is the struct that a caller interacts with for managing
// secrets.
type Manager struct {
	Envelope Envelope
	DynamoDB *dynamodb.DynamoDB
	Config   Config
	metrics  *ddbmetrics.Publisher
	Logger   std.Logger
}

// Config is a struct for configuring the manager
// XXX need to be able to provide an aws.Config for STS credentials,
// region et c
type Config struct {
	// AWS config structure. If left nil, will be replaced by a
	// default config with region us-west-2.
	AWSConfig *aws.Config
	// KMS KeyId - this is either a key alias prefixed by 'alias/',
	// a key ARN or a key UUID
	KeyID string
	// DynamoDB Table name
	TableName string
	// Provisioned Read and Write throughput when creating the
	// table, not used in normal operations. If left to 0 these
	// will be replaced by 1 by default.
	ProvisionedThroughputRead  int64
	ProvisionedThroughputWrite int64
	StatsdHostPort             string
	StatsdPrefix               string
	Logger                     std.Logger
}

// DefaultConfig returns a config struct with some sane defaults that
// is merged with the provided config in New
func DefaultConfig() Config {
	return Config{
		ProvisionedThroughputRead:  1,
		ProvisionedThroughputWrite: 1,
		AWSConfig:                  &aws.Config{Region: aws.String("us-west-2")},
		Logger:                     log.New(os.Stdout, "", log.LstdFlags|log.Lshortfile),
	}
}

func mergeConfig(provided *Config, defConfig Config) {
	if provided.AWSConfig == nil {
		provided.AWSConfig = defConfig.AWSConfig
	}
	if provided.ProvisionedThroughputRead == 0 {
		provided.ProvisionedThroughputRead = defConfig.ProvisionedThroughputRead
	}
	if provided.ProvisionedThroughputWrite == 0 {
		provided.ProvisionedThroughputWrite = defConfig.ProvisionedThroughputWrite
	}
	if provided.Logger == nil {
		provided.Logger = defConfig.Logger
	}
}

// New creates a Manager from config. Merges configuration with
// DefaultConfig()
func New(config Config) *Manager {
	var stats statsd.Statter
	defConfig := DefaultConfig()
	mergeConfig(&config, defConfig)

	if config.StatsdHostPort != "" || config.StatsdPrefix != "" {
		hostName, err := os.Hostname()
		if err != nil {
			hostName = "unknown"
		}

		stats, err = statsd.NewClient(config.StatsdHostPort, config.StatsdPrefix+"."+hostName)
		if err != nil {
			config.Logger.Printf("Couldn't start statsd: %s", err.Error())
			stats, err = statsd.NewNoopClient()
			if err != nil {
				// noop client won't return anything else
			}
		}
	} else {
		var err error
		stats, err = statsd.NewNoopClient()
		if err != nil {
			// noop client won't return anything else
		}
	}

	session := session.New(config.AWSConfig)

	ddb := dynamodb.New(session)
	m := ddbmetrics.New(ddb, stats)
	m.Start()
	return &Manager{
		DynamoDB: ddb,
		Config:   config,
		Envelope: Envelope{
			KMS: kms.New(session),
		},
		metrics: m,
		Logger:  config.Logger,
	}
}
