package util

import (
	"context"
	"encoding/json"
	"fmt"

	kms20160120 "github.com/alibabacloud-go/kms-20160120/v3/client"
	teautil "github.com/alibabacloud-go/tea-utils/v2/service"
	"github.com/aws/aws-sdk-go-v2/aws"
	awsconfig "github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
	"github.com/aws/aws-sdk-go-v2/service/s3"
	"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
	"github.com/aws/aws-sdk-go-v2/service/sts"
	alicred "github.com/tidbcloud/aws-shared-provider/pkg/ali/cred"
	alikms "github.com/tidbcloud/aws-shared-provider/pkg/ali/kms"
	"github.com/tidbcloud/aws-shared-provider/pkg/auditlog/constant"
	"github.com/tidbcloud/aws-shared-provider/pkg/storage"
	"github.com/tidbcloud/aws-shared-provider/pkg/types"
)

func GetO11yS3Client(cloudProviderType, region string) (*s3.Client, error) {
	if cloudProviderType == types.ProviderTypeAWSFreeTier {
		return getO11yS3ClientWithAWS(region)
	} else if cloudProviderType == types.ProviderTypeAliCloudFreeTier {
		return getO11yS3ClientWithAliCloud(region)
	}
	return nil, fmt.Errorf("unsupported cloud provider type: %s", cloudProviderType)
}

func getO11yS3ClientWithAWS(region string) (*s3.Client, error) {
	awsCfg, err := awsconfig.LoadDefaultConfig(context.TODO(), awsconfig.WithRegion(region))
	if err != nil {
		return nil, fmt.Errorf("load aws configuration err: %w", err)
	}
	stsSvc := sts.NewFromConfig(awsCfg)
	assumeRoleProvider := stscreds.NewAssumeRoleProvider(stsSvc, constant.O11yRole)
	newCfg, err := awsconfig.LoadDefaultConfig(
		context.TODO(),
		awsconfig.WithRegion(region),
		awsconfig.WithCredentialsProvider(assumeRoleProvider),
	)
	if err != nil {
		return nil, fmt.Errorf("load aws o11y configuration err: %w", err)
	}
	return s3.NewFromConfig(newCfg), nil
}

func getO11yS3ClientWithAliCloud(region string) (*s3.Client, error) {
	provider, err := alicred.NewOssOIDCAssumeCredentialsProvider(
		constant.AliO11yRoleMaxSessionDuration,
		constant.AliO11yRole,
		region)
	if err != nil {
		return nil, fmt.Errorf("load aliyun cred provider err: %w", err)
	}

	endpoint := fmt.Sprintf("https://oss-%s-internal.aliyuncs.com", region)
	awsConfig, err := awsconfig.LoadDefaultConfig(
		context.TODO(),
		awsconfig.WithRegion(region),
		awsconfig.WithBaseEndpoint(endpoint),
		awsconfig.WithCredentialsProvider(provider),
	)
	if err != nil {
		return nil, fmt.Errorf("load aws o11y configuration err: %w", err)
	}

	return s3.NewFromConfig(awsConfig), nil
}

func GetRdsConfig(cloudProviderType, rdsSecretArn, region string) (*storage.RdsConfig, error) {
	if cloudProviderType == types.ProviderTypeAWSFreeTier {
		return getRdsConfigWithAWS(rdsSecretArn, region)
	} else if cloudProviderType == types.ProviderTypeAliCloudFreeTier {
		return getRdsConfigWithAliCloud(rdsSecretArn, region)
	}
	return nil, fmt.Errorf("unsupported cloud provider type: %s", cloudProviderType)
}

func getRdsConfigWithAWS(rdsSecretArn, region string) (*storage.RdsConfig, error) {
	awsCfg, err := awsconfig.LoadDefaultConfig(context.TODO(), awsconfig.WithRegion(region))
	if err != nil {
		return nil, fmt.Errorf("load aws configuration err: %w", err)
	}
	sm := secretsmanager.NewFromConfig(awsCfg)
	result, err := sm.GetSecretValue(context.TODO(), &secretsmanager.GetSecretValueInput{
		SecretId: aws.String(rdsSecretArn),
	})
	if err != nil {
		return nil, fmt.Errorf("load rds secret configuration err: %w", err)
	}
	rdsConfig := &storage.RdsConfig{}
	if err := json.Unmarshal([]byte(*result.SecretString), rdsConfig); err != nil {
		return nil, fmt.Errorf("unmarshal rds secret configuration err: %w", err)
	}
	return rdsConfig, nil
}

func getRdsConfigWithAliCloud(rdsSecretArn, region string) (*storage.RdsConfig, error) {
	kmsClient, err := alikms.CreateClient(region)
	if err != nil {
		return nil, fmt.Errorf("create kms client err: %w", err)
	}
	result, err := kmsClient.GetSecretValueWithOptions(&kms20160120.GetSecretValueRequest{
		SecretName: aws.String(rdsSecretArn),
	}, &teautil.RuntimeOptions{})
	if err != nil {
		return nil, fmt.Errorf("load rds secret configuration err: %w", err)
	}
	rdsConfig := &storage.RdsConfig{}
	if err := json.Unmarshal([]byte(*result.Body.SecretData), rdsConfig); err != nil {
		return nil, fmt.Errorf("unmarshal rds secret configuration err: %w", err)
	}
	return rdsConfig, nil
}

func GetS3Client(cloudProviderType, region string) (*s3.Client, error) {
	if cloudProviderType == types.ProviderTypeAWSFreeTier {
		return getS3ClientWithAWS(region)
	} else if cloudProviderType == types.ProviderTypeAliCloudFreeTier {
		return getS3ClientWithAliCloud(region)
	}
	return nil, fmt.Errorf("unsupported cloud provider type: %s", cloudProviderType)
}

func getS3ClientWithAWS(region string) (*s3.Client, error) {
	awsCfg, err := awsconfig.LoadDefaultConfig(context.TODO(), awsconfig.WithRegion(region))
	if err != nil {
		return nil, fmt.Errorf("load aws configuration err: %w", err)
	}
	return s3.NewFromConfig(awsCfg), nil
}

func getS3ClientWithAliCloud(region string) (*s3.Client, error) {
	provider, err := alicred.NewOssOIDCCredentialsProvider(0)
	if err != nil {
		return nil, fmt.Errorf("load aliyun cred provider err: %w", err)
	}

	endpoint := fmt.Sprintf("https://oss-%s-internal.aliyuncs.com", region)
	awsConfig, err := awsconfig.LoadDefaultConfig(
		context.TODO(),
		awsconfig.WithRegion(region),
		awsconfig.WithBaseEndpoint(endpoint),
		awsconfig.WithCredentialsProvider(provider),
	)
	if err != nil {
		return nil, fmt.Errorf("load aws o11y configuration err: %w", err)
	}

	return s3.NewFromConfig(awsConfig, func(o *s3.Options) {
		// https://github.com/siyuan-note/siyuan/issues/14053
		o.RequestChecksumCalculation = aws.RequestChecksumCalculationWhenRequired
		o.ResponseChecksumValidation = aws.ResponseChecksumValidationWhenRequired
	}), nil
}
