package sandstorm

import (
	"fmt"
	"time"

	"code.justin.tv/d8a/buddy/lib/awsutil"
	"code.justin.tv/d8a/buddy/lib/config"
	"code.justin.tv/systems/sandstorm/manager"

	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
	"github.com/aws/aws-sdk-go/service/sts"
)

type Sandstorm struct {
	manager *manager.Manager
	config  *config.ConfigFile
}

type SandstormAPI interface {
	Manager() *manager.Manager
	GetUserPassword(cluster *config.Cluster, userName string) (string, error)
	WriteUserPassword(cluster *config.Cluster, userName string, newPassword string) error
	GetStorePassword() (string, error)
	WriteStorePassword(newPassword string) error
	SecretExists(secretPath string) bool
	DefaultSecretPath(clusterName string, userName string) string
}

func New(configFile *config.ConfigFile, region string, role string, profile string) (*Sandstorm, error) {
	config, awsSession, err := awsutil.GetSession(region, profile, 3)
	if err != nil {
		return nil, err
	}

	stsClient := sts.New(awsSession)
	arp := &stscreds.AssumeRoleProvider{
		Duration:     900 * time.Second,
		ExpiryWindow: 10 * time.Second,
		RoleARN:      role,
		Client:       stsClient,
	}

	creds := credentials.NewCredentials(arp)
	config.WithCredentials(creds)

	return &Sandstorm{
		config: configFile,
		manager: manager.New(manager.Config{
			AWSConfig: config,
			TableName: "sandstorm-production",
			KeyID:     "alias/sandstorm-production",
		}),
	}, nil
}

func (sandstorm *Sandstorm) Manager() *manager.Manager {
	return sandstorm.manager
}

//Long term, what this should do is pull override secret paths from the data store
//and if not present use the default based on the team/username.  But no datastore atm
//so we're just gonna throw all users in the config file for now
func (sandstorm *Sandstorm) GetUserPassword(cluster *config.Cluster, userName string) (string, error) {
	foundUser := cluster.GetUser(userName)
	if foundUser == nil {
		return "", fmt.Errorf("Could not find user %s of cluster %s in the local config", userName, cluster.Name)
	}

	secret, err := sandstorm.manager.Get(foundUser.Secret)
	if err != nil {
		return "", err
	}

	if secret == nil {
		return "", fmt.Errorf("The secret %s did not exist in sandstorm- have you correctly configured the user %s for the cluster %s?", foundUser.Secret, userName, cluster.Name)
	}
	return string(secret.Plaintext), nil
}

func (sandstorm *Sandstorm) WriteUserPassword(cluster *config.Cluster, userName string, newPassword string) error {
	foundUser := cluster.GetUser(userName)
	if foundUser == nil {
		return fmt.Errorf("Could not find user %s of cluster %s in the local config", userName, cluster.Name)
	}

	return sandstorm.manager.Put(&manager.Secret{
		Name:      foundUser.Secret,
		Plaintext: []byte(newPassword),
	})
}

const storeSecretPath string = "%s/rds-buddy-store/%s/buddy"

func (sandstorm *Sandstorm) GetStorePassword() (string, error) {
	secretPath := fmt.Sprintf(storeSecretPath, sandstorm.config.SandstormTeam, sandstorm.config.Environment)

	secret, err := sandstorm.manager.Get(secretPath)
	if err != nil {
		return "", err
	}

	if secret == nil {
		return "", fmt.Errorf("The buddy datastore secret did not exist in sandstorm- this should not be possible?")
	}
	return string(secret.Plaintext), nil
}

func (sandstorm *Sandstorm) WriteStorePassword(newPassword string) error {
	secretPath := fmt.Sprintf(storeSecretPath, sandstorm.config.SandstormTeam, sandstorm.config.Environment)
	return sandstorm.manager.Put(&manager.Secret{
		Name:      secretPath,
		Plaintext: []byte(newPassword),
	})
}

func (sandstorm *Sandstorm) SecretExists(secretPath string) bool {
	secret, err := sandstorm.manager.Get(secretPath)
	return err == nil && secret != nil
}

func (sandstorm *Sandstorm) DefaultSecretPath(clusterName string, userName string) string {
	return fmt.Sprintf("%s/%s/%s/%s", sandstorm.config.SandstormTeam, clusterName, sandstorm.config.Environment, userName)
}
