package vo

import (
	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/serverless-service/idl/pbgen/proto/common"
	"github.com/tidbcloud/serverless-service/internal/domain/constant"
	"github.com/tidbcloud/serverless-service/internal/domain/entity"
)

func CommonToCloudStorage(
	cs *common.CloudStorage,
	externalId string,
) (*entity.CloudStorage, constant.CloudStorageType, bizerr.BizError) {
	if cs == nil {
		return nil, "", nil
	}
	out := &entity.CloudStorage{}
	var cloudStorageType constant.CloudStorageType
	switch cs.Type {
	case common.CloudStorageType_TIDB_CLOUD:
		cloudStorageType = constant.CloudStorageTypeTiDBCloud
	case common.CloudStorageType_S3:
		cloudStorageType = constant.CloudStorageTypeS3
		out.S3 = &entity.S3CloudStorage{}
		s3 := cs.GetS3()
		if s3 == nil {
			return nil, "", bizerr.New(bizerr.ErrCodeParams, "s3 cloud storage is not set")
		}
		if s3.GetUri() == "" {
			return nil, "", bizerr.New(bizerr.ErrCodeParams, "s3 uri is not set")
		}
		out.Uri = s3.GetUri()
		switch s3.AuthType {
		case common.S3CloudStorage_ROLE_ARN:
			if s3.GetRoleArn() == "" {
				return nil, "", bizerr.New(bizerr.ErrCodeParams, "s3 role arn is not set")
			}
			out.S3.RoleArn = &entity.S3CloudStorageRoleArn{
				RoleArn:    s3.GetRoleArn(),
				ExternalId: externalId,
			}
		case common.S3CloudStorage_ACCESS_KEY:
			if s3.GetAccessKey() == nil || s3.GetAccessKey().Id == "" || s3.GetAccessKey().Secret == "" {
				return nil, "", bizerr.New(bizerr.ErrCodeParams, "s3 access key is not set")
			}
			out.S3.AccessKey = &entity.S3CloudStorageAccessKey{
				AccessKeyId:     s3.GetAccessKey().Id,
				SecretAccessKey: s3.GetAccessKey().Secret,
			}
		default:
			return nil, "", bizerr.New(bizerr.ErrCodeParams, "unsupported s3 auth type '%s'", s3.AuthType)
		}
	case common.CloudStorageType_OSS:
		cloudStorageType = constant.CloudStorageTypeOSS
		out.OSS = &entity.OSSCloudStorage{}
		oss := cs.GetOss()
		if oss == nil {
			return nil, "", bizerr.New(bizerr.ErrCodeParams, "oss cloud storage is not set")
		}
		if oss.GetUri() == "" {
			return nil, "", bizerr.New(bizerr.ErrCodeParams, "oss uri is not set")
		}
		out.Uri = oss.GetUri()
		switch oss.AuthType {
		case common.OSSCloudStorage_ROLE_ARN:
			return nil, "", bizerr.New(
				bizerr.ErrCodeParams,
				"oss role arn is not supported, please use access key instead",
			)
		case common.OSSCloudStorage_ACCESS_KEY:
			if oss.GetAccessKey() == nil || oss.GetAccessKey().Id == "" || oss.GetAccessKey().Secret == "" {
				return nil, "", bizerr.New(bizerr.ErrCodeParams, "oss access key is not set")
			}
			out.OSS.AccessKey = &entity.OSSCloudStorageAccessKey{
				AccessKeyId:     oss.GetAccessKey().Id,
				AccessKeySecret: oss.GetAccessKey().Secret,
			}
		default:
			return nil, "", bizerr.New(bizerr.ErrCodeParams, "unsupported oss auth type '%s'", oss.AuthType)
		}
	case common.CloudStorageType_GCS:
		cloudStorageType = constant.CloudStorageTypeGCS
		out.GCS = &entity.GCSCloudStorage{}
		gcs := cs.GetGcs()
		if gcs == nil {
			return nil, "", bizerr.New(bizerr.ErrCodeParams, "gcs cloud storage is not set")
		}
		if gcs.GetUri() == "" {
			return nil, "", bizerr.New(bizerr.ErrCodeParams, "gcs uri is not set")
		}
		out.Uri = gcs.GetUri()
		if gcs.AuthType != common.GCSCloudStorage_SERVICE_ACCOUNT_KEY {
			return nil, "", bizerr.New(bizerr.ErrCodeParams, "unsupported gcs auth type '%s'", gcs.AuthType)
		}
		if gcs.GetServiceAccountKey() == "" {
			return nil, "", bizerr.New(bizerr.ErrCodeParams, "gcs service account key is not set")
		}
		out.GCS = &entity.GCSCloudStorage{
			ServiceAccountKey: gcs.GetServiceAccountKey(),
		}
	case common.CloudStorageType_AZURE_BLOB:
		cloudStorageType = constant.CloudStorageTypeAzureBlob
		out.AzureBlob = &entity.AzureBlobCloudStorage{}
		azure := cs.GetAzureBlob()
		if azure == nil {
			return nil, "", bizerr.New(bizerr.ErrCodeParams, "azure blob cloud storage is not set")
		}
		if azure.GetUri() == "" {
			return nil, "", bizerr.New(bizerr.ErrCodeParams, "azure blob uri is not set")
		}
		out.Uri = azure.GetUri()
		if azure.AuthType != common.AzureBlobCloudStorage_SAS_TOKEN {
			return nil, "", bizerr.New(bizerr.ErrCodeParams, "unsupported azure blob auth type '%s'", azure.AuthType)
		}
		if azure.GetSasToken() == "" {
			return nil, "", bizerr.New(bizerr.ErrCodeParams, "azure blob sas token is not set")
		}
		out.AzureBlob = &entity.AzureBlobCloudStorage{
			SasToken: azure.GetSasToken(),
		}
	default:
		return nil, "", bizerr.New(bizerr.ErrCodeParams, "unsupported cloud storage type '%s'", cs.Type)
	}
	out.Type = cloudStorageType
	return out, cloudStorageType, nil
}

func CloudStorageToCommon(
	cloudStorageType constant.CloudStorageType,
	cs *entity.CloudStorage,
) *common.CloudStorage {
	out := &common.CloudStorage{}
	switch cloudStorageType {
	case constant.CloudStorageTypeTiDBCloud:
		out.Type = common.CloudStorageType_TIDB_CLOUD
	case constant.CloudStorageTypeS3:
		out.Type = common.CloudStorageType_S3
		s3 := &common.S3CloudStorage{
			Uri: cs.Uri,
		}
		if cs.S3.RoleArn != nil {
			s3.AuthType = common.S3CloudStorage_ROLE_ARN
			s3.Auth = &common.S3CloudStorage_RoleArn{RoleArn: cs.S3.RoleArn.RoleArn}
		} else if cs.S3.AccessKey != nil {
			s3.AuthType = common.S3CloudStorage_ACCESS_KEY
			s3.Auth = &common.S3CloudStorage_AccessKey_{
				AccessKey: &common.S3CloudStorage_AccessKey{
					Id:     cs.S3.AccessKey.AccessKeyId,
					Secret: SecretData,
				},
			}
		}
		out.Storage = &common.CloudStorage_S3{S3: s3}
	case constant.CloudStorageTypeOSS:
		out.Type = common.CloudStorageType_OSS
		oss := &common.OSSCloudStorage{
			Uri: cs.Uri,
		}
		if cs.OSS.RoleArn != nil {
			oss.AuthType = common.OSSCloudStorage_ROLE_ARN
			oss.Auth = &common.OSSCloudStorage_RoleArn{RoleArn: cs.OSS.RoleArn.RoleArn}
		} else if cs.OSS.AccessKey != nil {
			oss.AuthType = common.OSSCloudStorage_ACCESS_KEY
			oss.Auth = &common.OSSCloudStorage_AccessKey_{
				AccessKey: &common.OSSCloudStorage_AccessKey{
					Id:     cs.OSS.AccessKey.AccessKeyId,
					Secret: SecretData,
				},
			}
		}
		out.Storage = &common.CloudStorage_Oss{Oss: oss}
	case constant.CloudStorageTypeGCS:
		out.Type = common.CloudStorageType_GCS
		gcs := &common.GCSCloudStorage{
			Uri:      cs.Uri,
			AuthType: common.GCSCloudStorage_SERVICE_ACCOUNT_KEY,
			Auth: &common.GCSCloudStorage_ServiceAccountKey{
				ServiceAccountKey: SecretData,
			},
		}
		out.Storage = &common.CloudStorage_Gcs{Gcs: gcs}
	case constant.CloudStorageTypeAzureBlob:
		out.Type = common.CloudStorageType_AZURE_BLOB
		azure := &common.AzureBlobCloudStorage{
			Uri:      cs.Uri,
			AuthType: common.AzureBlobCloudStorage_SAS_TOKEN,
			Auth: &common.AzureBlobCloudStorage_SasToken{
				SasToken: SecretData,
			},
		}
		out.Storage = &common.CloudStorage_AzureBlob{AzureBlob: azure}
	}
	return out
}
