package e2eaccounts

// This package understands the relationship between the E2E test accounts.

import (
	"fmt"

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

const (
	MainAccountID       = "793846415324"
	PublisherAccountID  = "567312298267"
	SubscriberAccountID = "311539646041"
)

var (
	mainAccountAssumeRoleARN       = fmt.Sprintf("arn:aws:iam::%s:role/e2e-test-runner", MainAccountID)
	publisherAccountAssumeRoleARN  = fmt.Sprintf("arn:aws:iam::%s:role/e2e-publisher-role", PublisherAccountID)
	subscriberAccountAssumeRoleARN = fmt.Sprintf("arn:aws:iam::%s:role/e2e-subscriber-role", SubscriberAccountID)
)

// Singleton for account access credentials, use Credentials() to access
// from outside this package
var creds *Credentials

// Credentials for each E2E test account, usable by each phase of the E2E test
// to access the accounts
type Credentials struct {
	MainAccount       *credentials.Credentials
	PublisherAccount  *credentials.Credentials // accessed via assume-role from MainAccount creds
	SubscriberAccount *credentials.Credentials // accessed via assume-role from MainAccount creds
}

// Credentials returns the credential sets required to access other accounts.
// Calling this method before InitializeCredentials will result in a panic
func AccountCredentials() *Credentials {
	if creds == nil {
		panic("credentials not yet initialized")
	}
	return creds
}

// InitializeCredentials fetches credentials for each E2E test account. Will actually call Get()
// on each AWS account credential set, and will return an error if valid credentials can not
// be generated for any given account
// Does nothing if InitializeCredentials has already been called
func InitializeCredentials(sess *session.Session) error {
	if creds != nil {
		return nil
	}
	creds = &Credentials{}
	overrideSess := sess.Copy(&aws.Config{STSRegionalEndpoint: endpoints.RegionalSTSEndpoint})

	// Main account access setup
	creds.MainAccount = stscreds.NewCredentials(overrideSess, mainAccountAssumeRoleARN)
	_, err := creds.MainAccount.Get() // attempt to generate credentials
	if err != nil {
		return err
	}

	// Use main account credentials to access publisher account
	pubSts := sts.New(overrideSess, &aws.Config{Credentials: creds.MainAccount, STSRegionalEndpoint: endpoints.RegionalSTSEndpoint})
	creds.PublisherAccount = stscreds.NewCredentialsWithClient(pubSts, publisherAccountAssumeRoleARN)
	_, err = creds.PublisherAccount.Get() // attempt to generate credentials
	if err != nil {
		return err
	}

	// Use main account credentials to access subscriber account
	subSts := sts.New(overrideSess, &aws.Config{Credentials: creds.MainAccount, STSRegionalEndpoint: endpoints.RegionalSTSEndpoint})
	creds.SubscriberAccount = stscreds.NewCredentialsWithClient(subSts, subscriberAccountAssumeRoleARN)
	_, err = creds.SubscriberAccount.Get() // attempt to generate credentials
	if err != nil {
		return err
	}

	return nil
}

// MainConfig returns an aws.Config using the main account credentials
func (c *Credentials) MainConfig() *aws.Config {
	return &aws.Config{Credentials: c.MainAccount}
}

// PublisherConfig returns an aws.Config using the publisher account credentials
func (c *Credentials) PublisherConfig() *aws.Config {
	return &aws.Config{Credentials: c.PublisherAccount}
}

// SubscriberConfig returns an aws.Config using the subscriber account credentials
func (c *Credentials) SubscriberConfig() *aws.Config {
	return &aws.Config{Credentials: c.SubscriberAccount}
}
