package config

import (
	"fmt"

	"code.justin.tv/hygienic/distconf"
)

// LoadWithS3 loads a configuration from s3
func LoadWithS3(environment string) (*Config, error) {
	s3Reader, err := newS3Reader(environment)
	if err != nil {
		return nil, err
	}

	cfg := Load(&distconf.Distconf{
		Readers: []distconf.Reader{
			s3Reader,
		},
	})
	if err := cfg.Validate(); err != nil {
		return nil, err
	}
	return cfg, nil
}

// Load return a configuration
func Load(dconf *distconf.Distconf) *Config {
	return &Config{
		AWSRegion:                  dconf.Str("AWSRegion", ""),
		Environment:                dconf.Str("Environment", ""),
		S2SServiceName:             dconf.Str("S2SServiceName", ""),
		ECSClusterName:             dconf.Str("ECSClusterName", ""),
		ECSDeployRoleArn:           dconf.Str("ECSDeployRoleArn", ""),
		ECSServiceName:             dconf.Str("ECSServiceName", ""),
		LegacyPermissionsHashKey:   dconf.Str("LegacyPermissionsHashKey", ""),
		LegacyPermissionsTableName: dconf.Str("LegacyPermissionsTableName", ""),
		RolesHashKey:               dconf.Str("RolesHashKey", ""),
		RolesTableName:             dconf.Str("RolesTableName", ""),
		UsersTableName:             dconf.Str("UsersTableName", ""),
		TaskContainerPort:          dconf.Int("TaskContainerPort", 0),
		TaskExecutionRoleArn:       dconf.Str("TaskExecutionRoleArn", ""),
		TaskFamily:                 dconf.Str("TaskFamily", ""),
		TaskLogGroup:               dconf.Str("TaskLogGroup", ""),
		TaskLogStreamPrefix:        dconf.Str("TaskLogStreamPrefix", ""),
		TaskServiceRoleArn:         dconf.Str("TaskServiceRoleArn", ""),
		UsersHashKey:               dconf.Str("UsersHashKey", ""),

		LambdasS3Bucket:                       dconf.Str("LambdasS3Bucket", ""),
		SyncRolesToUsersLambdaKey:             dconf.Str("SyncRolesToUsersLambdaKey", ""),
		SyncRolesToLegacyPermissionsLambdaKey: dconf.Str("SyncRolesToLegacyPermissionsLambdaKey", ""),
		SyncLegacyPermissionsToRolesLambdaKey: dconf.Str("SyncLegacyPermissionsToRolesLambdaKey", ""),

		IntegrationS2SServiceName: dconf.Str("IntegrationS2SServiceName", ""),
	}
}

// Config is the beefcake service configuration
type Config struct {
	AWSRegion      *distconf.Str
	Environment    *distconf.Str
	S2SServiceName *distconf.Str

	ECSClusterName   *distconf.Str
	ECSDeployRoleArn *distconf.Str
	ECSServiceName   *distconf.Str

	LegacyPermissionsHashKey   *distconf.Str
	LegacyPermissionsTableName *distconf.Str

	RolesHashKey   *distconf.Str
	RolesTableName *distconf.Str

	LambdasS3Bucket                       *distconf.Str
	SyncRolesToUsersLambdaKey             *distconf.Str
	SyncRolesToLegacyPermissionsLambdaKey *distconf.Str
	SyncLegacyPermissionsToRolesLambdaKey *distconf.Str

	IntegrationS2SServiceName *distconf.Str

	TaskContainerPort    *distconf.Int
	TaskExecutionRoleArn *distconf.Str
	TaskFamily           *distconf.Str
	TaskLogGroup         *distconf.Str
	TaskLogStreamPrefix  *distconf.Str
	TaskServiceRoleArn   *distconf.Str

	UsersHashKey   *distconf.Str
	UsersTableName *distconf.Str
}

type configMissingError struct {
	key string
}

func (e configMissingError) Error() string {
	return fmt.Sprintf("Missing '%s' in configuration.", e.key)
}

// Validate the configuration for missing values
func (cfg Config) Validate() error {
	for _, tt := range []struct {
		Key   string
		Value string
	}{
		{Key: "AWSRegion", Value: cfg.AWSRegion.Get()},
		{Key: "Environment", Value: cfg.Environment.Get()},
		{Key: "ECSClusterName", Value: cfg.ECSClusterName.Get()},
		{Key: "ECSDeployRoleArn", Value: cfg.ECSDeployRoleArn.Get()},
		{Key: "ECSServiceName", Value: cfg.ECSServiceName.Get()},
		{Key: "LegacyPermissionsHashKey", Value: cfg.LegacyPermissionsHashKey.Get()},
		{Key: "LegacyPermissionsTableName", Value: cfg.LegacyPermissionsTableName.Get()},
		{Key: "RolesHashKey", Value: cfg.RolesHashKey.Get()},
		{Key: "RolesTableName", Value: cfg.RolesTableName.Get()},
		{Key: "SyncRolesToUsersLambdaKey", Value: cfg.SyncRolesToUsersLambdaKey.Get()},
		{Key: "SyncRolesToLegacyPermissionsLambdaKey", Value: cfg.SyncRolesToLegacyPermissionsLambdaKey.Get()},
		{Key: "SyncLegacyPermissionsToRolesLambdaKey", Value: cfg.SyncLegacyPermissionsToRolesLambdaKey.Get()},
		{Key: "TaskExecutionRoleArn", Value: cfg.TaskExecutionRoleArn.Get()},
		{Key: "TaskFamily", Value: cfg.TaskFamily.Get()},
		{Key: "TaskLogGroup", Value: cfg.TaskLogGroup.Get()},
		{Key: "TaskLogStreamPrefix", Value: cfg.TaskLogStreamPrefix.Get()},
		{Key: "TaskServiceRoleArn", Value: cfg.TaskServiceRoleArn.Get()},
		{Key: "TaskContainerPort", Value: fmt.Sprintf("%d", cfg.TaskContainerPort.Get())},
		{Key: "UsersHashKey", Value: cfg.UsersHashKey.Get()},
		{Key: "UsersTableName", Value: cfg.UsersTableName.Get()},
	} {
		if tt.Value == "" {
			return configMissingError{key: tt.Key}
		}
	}
	return nil

}

// LambdaS3Key returns the s3 key a lambda is uploaded to
func (cfg Config) LambdaS3Key(lambda string) (string, error) {
	var key *distconf.Str
	switch lambda {
	case "sync-roles-to-users":
		key = cfg.SyncRolesToUsersLambdaKey
	case "sync-roles-to-legacy-permissions":
		key = cfg.SyncRolesToLegacyPermissionsLambdaKey
	case "sync-legacy-permissions-to-roles":
		key = cfg.SyncLegacyPermissionsToRolesLambdaKey
	default:
		return "", fmt.Errorf("invalid lambda: %s", lambda)
	}

	keyValue := key.Get()
	if keyValue == "" {
		return "", fmt.Errorf("configuration does not have key for lambda: %s", lambda)
	}
	return keyValue, nil
}
