package config

import (
	"fmt"

	"code.justin.tv/hygienic/distconf"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3iface"
)

// environmentAWSAccountID returns the aws account ID for an environment
func environmentAWSAccountID(environment string) (string, error) {
	switch environment {
	case "testing":
		fallthrough
	case "staging":
		return "333838943856", nil
	case "production":
		return "193346998399", nil
	}
	return "", fmt.Errorf("Invalid environment: %s", environment)
}

// readRoleArn returns the role arn to read the configuration
func readRoleArn(environment string) (string, error) {
	awsAccountID, err := environmentAWSAccountID(environment)
	if err != nil {
		return "", err
	}

	return fmt.Sprintf(
		"arn:aws:iam::%s:role/beefcake-v3-%s-config-read",
		awsAccountID, environment,
	), nil
}

type pullingConfigError struct {
	err error

	bucket      string
	environment string
	key         string
}

func (e pullingConfigError) Error() string {
	return fmt.Sprintf(
		"Access denied to %s/%s for %s environment: %s",
		e.bucket, e.key, e.environment, e.err.Error())
}

func newS3Reader(environment string) (distconf.Reader, error) {
	roleArn, err := readRoleArn(environment)
	if err != nil {
		return nil, err
	}

	sess, err := session.NewSession(&aws.Config{Region: aws.String("us-west-2")})
	if err != nil {
		return nil, err
	}

	sess, err = session.NewSession(&aws.Config{
		Region:      aws.String("us-west-2"),
		Credentials: stscreds.NewCredentials(sess, roleArn),
	})
	if err != nil {
		return nil, err
	}

	return s3Reader{
		S3:          s3.New(sess),
		Environment: environment,
	}.Reader()
}

type s3Reader struct {
	S3          s3iface.S3API
	Environment string
}

func (sr s3Reader) Reader() (distconf.Reader, error) {
	res, err := sr.S3.GetObject(&s3.GetObjectInput{
		Bucket: aws.String(sr.bucket()),
		Key:    aws.String(sr.key()),
	})
	if err != nil {
		return nil, sr.pullingConfigError(err)
	}

	dr := &distconf.JSONConfig{}
	if err := dr.Refresh(res.Body); err != nil {
		return nil, sr.pullingConfigError(err)
	}
	return dr, nil
}

func (sr s3Reader) bucket() string {
	return fmt.Sprintf("beefcake-v3-%s", sr.Environment)
}

func (sr s3Reader) key() string {
	return "config.json"
}

func (sr s3Reader) pullingConfigError(err error) error {
	return pullingConfigError{
		err:         err,
		environment: sr.Environment,
		bucket:      sr.bucket(),
		key:         sr.key(),
	}
}
