package aws

import (
	"context"

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

// The endpoint_url supplied here is used for specific AWS service (Cloudwatch / Timestream / etc.)
type CredentialConfig struct {
	Region               string `toml:"region"`
	AccessKey            string `toml:"access_key"`
	SecretKey            string `toml:"secret_key"`
	RoleARN              string `toml:"role_arn"`
	Profile              string `toml:"profile"`
	Filename             string `toml:"shared_credential_file"`
	Token                string `toml:"token"`
	EndpointURL          string `toml:"endpoint_url"`
	RoleSessionName      string `toml:"role_session_name"`
	WebIdentityTokenFile string `toml:"web_identity_token_file"`
}

func (c *CredentialConfig) Credentials() (aws.Config, error) {
	if c.RoleARN != "" {
		return c.configWithAssumeCredentials()
	}
	return c.configWithRootCredentials()
}

func (c *CredentialConfig) configWithRootCredentials() (aws.Config, error) {
	options := []func(*config.LoadOptions) error{
		config.WithRegion(c.Region),
	}

	if c.Profile != "" {
		options = append(options, config.WithSharedConfigProfile(c.Profile))
	}
	if c.Filename != "" {
		options = append(options, config.WithSharedCredentialsFiles([]string{c.Filename}))
	}

	if c.AccessKey != "" || c.SecretKey != "" {
		provider := credentials.NewStaticCredentialsProvider(c.AccessKey, c.SecretKey, c.Token)
		options = append(options, config.WithCredentialsProvider(provider))
	}

	return config.LoadDefaultConfig(context.Background(), options...)
}

func (c *CredentialConfig) configWithAssumeCredentials() (aws.Config, error) {
	// To generate credentials using assumeRole, we need to create AWS STS client with the default AWS endpoint,
	defaultConfig, err := c.configWithRootCredentials()
	if err != nil {
		return aws.Config{}, err
	}

	var provider aws.CredentialsProvider
	stsService := sts.NewFromConfig(defaultConfig)
	if c.WebIdentityTokenFile != "" {
		provider = stscreds.NewWebIdentityRoleProvider(
			stsService,
			c.RoleARN,
			stscreds.IdentityTokenFile(c.WebIdentityTokenFile),
			func(opts *stscreds.WebIdentityRoleOptions) {
				if c.RoleSessionName != "" {
					opts.RoleSessionName = c.RoleSessionName
				}
			},
		)
	} else {
		provider = stscreds.NewAssumeRoleProvider(stsService, c.RoleARN, func(opts *stscreds.AssumeRoleOptions) {
			if c.RoleSessionName != "" {
				opts.RoleSessionName = c.RoleSessionName
			}
		})
	}

	defaultConfig.Credentials = aws.NewCredentialsCache(provider)
	return defaultConfig, nil
}
