package manager

import (
	"net/http"
	"sync"
	"time"

	"code.justin.tv/systems/sandstorm/logging"
	"code.justin.tv/systems/sandstorm/queue"
	"code.justin.tv/systems/sandstorm/resource"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/gofrs/uuid"
)

var configLock sync.Mutex

// Config is a struct for configuring the manager
// XXX need to be able to provide an aws.Config for STS credentials,
// region etc
type Config struct {
	// AWS config structure. If left nil, will be replaced by a
	// default config with region us-west-2.
	AWSConfig *aws.Config
	Logger    logging.Logger

	Host              string
	InventoryInterval time.Duration
	ServiceName       string

	Queue      queue.Config
	InstanceID string

	//User performing the action
	ActionUser string

	// Environment to configure manager for
	Environment string
	// Sandstorm stack to use
	resource.Stack

	// Depreciated: determined by Environment
	KeyID string
	// Depreciated: determined by Environment
	TableName string
	// Depreciated: determined by Environment
	ProvisionedThroughputRead int64
	// Depreciated: determined by Environment
	ProvisionedThroughputWrite int64
	// Depreciated: determined by Environment
	InventoryRoleARN string
	// Depreciated: determined by Environment
	CloudWatchRoleARN string
	// Depreciated: determined by Environment
	InventoryStatusURL string
}

func (cfg *Config) stack() resource.Stack {
	configLock.Lock()
	if cfg.Stack == nil {
		stack := resource.ProductionStack()
		if cfg.Environment != "" {
			envStack, err := resource.NewGlobalStack(cfg.Environment)
			if err != nil {
				cfg.Logger.Errorf("Error configuring stack: %s", err.Error())
			} else {
				stack = envStack
			}
		}
		cfg.Stack = stack
	}
	configLock.Unlock()

	return cfg.Stack
}

// WithProxyFallback returns a Config that uses a proxy fallback
func (cfg *Config) WithProxyFallback(proxyURL, pop string, statter ProxyStatter) *Config {
	cfg.AWSConfig = cfg.awsConfigurer().
		AWSConfig(nil).
		WithHTTPClient(&http.Client{Transport: &wrappedRoundTripper{
			Logger:   cfg.Logger,
			Pop:      pop,
			ProxyURL: proxyURL,
			Statter:  statter,
		}})
	return cfg
}

// WithRole configures configuration to use a role
func (cfg *Config) WithRole(roleARN string) *Config {
	cfg.AWSConfig = cfg.awsConfigurer().
		AWSConfig(nil, roleARN)
	return cfg
}

func (cfg *Config) fillDefaults(other Config) error {
	if cfg.Logger == nil {
		cfg.Logger = other.Logger
	}
	if cfg.InstanceID == "" {
		id, err := uuid.NewV4()
		if err != nil {
			return err
		}
		cfg.InstanceID = id.String()
	}
	if cfg.AWSConfig == nil {
		cfg.AWSConfig = other.AWSConfig
	}
	if cfg.ActionUser == "" {
		cfg.ActionUser = other.awsConfigurer().GetAWSIdentity(other.awsCreds())
	}
	if cfg.Queue == (queue.Config{}) {
		cfg.Queue = other.Queue
	}

	cfg.Queue.AccountID = cfg.stack().AccountID()
	cfg.Queue.Region = cfg.stack().AWSRegion()
	cfg.Queue.TopicArn = cfg.stack().SecretsTopicArn()
	cfg.Queue.QueueNamePrefix = cfg.stack().SecretsQueueNamePrefix()
	return nil
}

func (cfg Config) awsConfigurerForRegion(region string) resource.AWSConfigurer {
	awsConfigurer := resource.AWSConfigurer{
		Region: region,
	}

	if cfg.AWSConfig != nil {
		awsConfigurer.HTTPClient = cfg.AWSConfig.HTTPClient
	}

	return awsConfigurer
}

func (cfg Config) awsConfigurer() resource.AWSConfigurer {
	return cfg.awsConfigurerForRegion(cfg.stack().AWSRegion())
}

func (cfg Config) awsCreds() *credentials.Credentials {
	if cfg.AWSConfig == nil {
		return nil
	}
	return cfg.AWSConfig.Credentials
}

// DefaultConfig returns a config struct with some sane defaults that
// is merged with the provided config in New
func DefaultConfig() Config {
	return Config{
		Environment: "production",
		AWSConfig:   resource.AWSConfig(nil),
		Logger:      &logging.NoopLogger{},
		Queue: queue.Config{
			QueueNamePrefix: defaultQueueNamePrefix,
		},
	}
}

func getDefaultConfigByEnvironment(env string) (cfg Config) {
	cfg = DefaultConfig()
	if env == "" || env == "production" {
		return
	}

	cfg.Logger = &logging.NoopLogger{}
	cfg.Queue = queue.Config{
		QueueNamePrefix: defaultQueueNamePrefix,
	}
	return
}
