package entity

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"net/url"
	"strconv"

	encryptor_wrapper "github.com/tidbcloud/dataflow-service/pkg/encryptor-wrapper"
)

var BucketSchemaMap = map[string]string{"gcp": "gs", "aws": "s3", "aliyun": "oss"}

// UIRL get bucket uri
func (b *Bucket) URI() string {
	switch b.Provider {
	case InfraProviderTypeAKS, InfraProviderTypeAzure:
		accountName := GenStorageAccountName(b.Name)
		containerName := b.Name
		return fmt.Sprintf("https://%s.blob.core.windows.net/%s", accountName, containerName)
	default:
		return fmt.Sprintf("%s://%s", BucketSchemaMap[b.Provider], b.Name)
	}
}

func (b *Bucket) Uint64ClusterID() uint64 {
	if b.ClusterID == "" {
		return 0
	}
	i, err := strconv.ParseUint(b.ClusterID, 10, 64)
	if err != nil {
		panic(err)
	}
	return i
}

func (b *Bucket) Uint64ProjectID() uint64 {
	i, err := strconv.ParseUint(b.ProjectID, 10, 64)
	if err != nil {
		panic(err)
	}
	return i
}

func (b *Bucket) Uint64TenantID() uint64 {
	i, err := strconv.ParseUint(b.TenantID, 10, 64)
	if err != nil {
		panic(err)
	}
	return i
}

func (b *Bucket) MainRegion() string {
	return b.Regions[0]
}

type BucketComponentTagValue string

const (
	BucketComponentTagValueBackup        BucketComponentTagValue = "backup"
	BucketComponentTagValueRecoveryGroup BucketComponentTagValue = "recovery-group"
)

func (b *Bucket) GenResourceComponentTagValue() string {
	if b.Extension == nil {
		return string(BucketComponentTagValueBackup)
	}
	businessPurposeType, ok := b.Extension["type"]
	if !ok {
		return string(BucketComponentTagValueBackup)
	}
	if businessPurposeType == "recovery-group" {
		return string(BucketComponentTagValueRecoveryGroup)
	}
	return string(BucketComponentTagValueBackup)
}

const (
	tokenExtKey      = "sasToken"
	extKeyRTCEnabled = "rtc_enabled"
)

// GetSASToken gets the encrypted token from bucket.Extension
// and then use the descryptor to decrypt it
func (b *Bucket) GetSASToken(encryptor encryptor_wrapper.Encryptor) (string, error) {
	if b.InfraProviderType != InfraProviderTypeAKS && b.InfraProviderType != InfraProviderTypeAzure {
		return "", fmt.Errorf("only infra provider type azure and aks support this")
	}
	if b.Extension == nil || b.Extension[tokenExtKey] == "" {
		return "", fmt.Errorf("no SAS token found")
	}
	encryptedToken := b.Extension[tokenExtKey]
	token, err := encryptor.DecryptBase64(encryptedToken)
	if err != nil {
		return "", err
	}
	return token, nil
}

// SetSASToken uses the encryptor to encrypt SAS token and saves it to bucket.Extension
func (b *Bucket) SetSASToken(encryptor encryptor_wrapper.Encryptor, token string) error {
	if b.Extension == nil {
		b.Extension = JSONMap{}
	}
	encryptedToken, err := encryptor.EncryptBase64(token)
	if err != nil {
		return err
	}
	b.Extension[tokenExtKey] = encryptedToken
	return nil
}

// GetRTCEnabled reports whether S3 Replication Time Control is active on this bucket.
func (b *Bucket) GetRTCEnabled() bool {
	if b.Extension == nil {
		return false
	}
	val, ok := b.Extension[extKeyRTCEnabled]
	if !ok {
		return false
	}
	parsed, _ := strconv.ParseBool(val)
	return parsed
}

// SetRTCEnabled persists the RTC state in the bucket's extension map.
func (b *Bucket) SetRTCEnabled(enabled bool) {
	if b.Extension == nil {
		b.Extension = JSONMap{}
	}
	b.Extension[extKeyRTCEnabled] = strconv.FormatBool(enabled)
}

// GenStorageAccountName gets a storage account name from bucket name
// Because Azure Stroage Account name has following limitations:
// - 3 and 24 characters in length
// - use numbers and lower-case only
func GenStorageAccountName(name string) string {
	hash := md5.Sum([]byte(name))
	return hex.EncodeToString(hash[:])[:24]
}

func (b *Bucket) URIInBucket(uri string) (string, error) {
	if b.Provider != string(CloudProviderTypeAWS) {
		return uri, nil
	}

	u, err := url.Parse(uri)
	if err != nil {
		return "", fmt.Errorf("failed to parse URI: %w", err)
	}
	u.Host = b.Name
	return u.String(), nil
}

// GetBucketByRegion returns the BackupSettingBucket that covers the requested region.
//
// Returns an empty BackupSettingBucket and false when:
//   - region is empty, or
//   - no configured bucket covers the requested region.
func (b *BucketSetting) GetBucketByRegion(region string) (BackupSettingBucket, bool) {
	if b == nil {
		return BackupSettingBucket{}, false
	}
	if region == "" {
		return BackupSettingBucket{}, false
	}
	for _, bucket := range b.Buckets {
		for _, r := range bucket.Regions {
			if r == region {
				return bucket, true
			}
		}
	}
	return BackupSettingBucket{}, false
}

func (b *BucketSetting) SecondaryRegions() []string {
	regions := make([]string, 0, len(b.Buckets))
	for _, bucket := range b.Buckets {
		if bucket.Master {
			continue
		}
		if len(bucket.Regions) > 0 {
			regions = append(regions, bucket.Regions...)
		}
	}
	return regions
}
