// Package secretsmanager provide methods to get data from
// AWS Secret Manager
package secretsmanager

import (
	"encoding/json"
	"errors"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/secretsmanager"
	"github.com/aws/aws-sdk-go/service/secretsmanager/secretsmanageriface"
)

// Client represents an AWS Secrets Manager client
type Client struct {
	config *AWSConfig
	api    secretsmanageriface.SecretsManagerAPI
}

// New creates an AWS Session Manager Client
func New(config *AWSConfig) *Client {
	c := &Client{
		config: config,
	}

	s := c.newSession(config)
	c.api = secretsmanager.New(s)
	return c
}

func (c *Client) newSession(config *AWSConfig) *session.Session {
	// Initialize config with error verbosity
	sess := aws.NewConfig().WithCredentialsChainVerboseErrors(true)

	if config.Region != "" {
		sess = sess.WithRegion(config.Region)
	}

	opts := session.Options{
		Config: *sess,
	}

	return session.Must(session.NewSessionWithOptions(opts))
}

// GetSecret return an AWS Secret Manager secret
// in plain text from a given secret name
func (c *Client) GetSecret(spec *SecretSpec) (string, error) {
	params := &secretsmanager.GetSecretValueInput{
		SecretId:     aws.String(spec.Name),
		VersionStage: aws.String("AWSCURRENT"),
	}

	resp, err := c.api.GetSecretValue(params)
	if err != nil {
		return "", err
	}

	if resp.SecretString == nil {
		return "", errors.New("Secret is not string")
	}

	secret := SecretString{
		Name:         *resp.Name,
		SecretString: *resp.SecretString,
	}
	value, err := getSecretValue(&secret, spec)
	if err != nil {
		return "", err
	}

	return value, nil
}

func getSecretValue(s *SecretString, spec *SecretSpec) (string, error) {
	var secretValue map[string]string

	blob := []byte(s.SecretString)

	err := json.Unmarshal(blob, &secretValue)
	if err != nil {
		return "", err
	}

	// If key is not set and secret has multiple keys, return error
	if spec.Key == "" && len(secretValue) > 1 {
		return "", errors.New("Secret has multiple values and no key was set")
	}

	if spec.Key == "" {
		for _, v := range secretValue {
			return v, nil
		}
	}

	if v, ok := secretValue[spec.Key]; ok {
		return v, nil
	}

	return "", errors.New("No secret found")
}
