package export

import (
	"context"
	"strings"

	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/infra/repoimpl/schema"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

const exportEncryptedValuePrefix = "enc::"

func (r *exportRepoImpl) encryptExport(ctx context.Context, in *schema.Export) (*schema.Export, bizerr.BizError) {
	if in == nil {
		return nil, bizerr.New(bizerr.ErrCodeParams, "export is required")
	}

	password, err := r.encryptValue(ctx, in.EncryptedPassword)
	if err != nil {
		return nil, bizerr.WrapError(err, "failed to encrypt export password")
	}
	in.EncryptedPassword = password

	if err := r.encryptCloudStorage(ctx, in.CloudStorage); err != nil {
		return nil, err
	}

	return in, nil
}

func (r *exportRepoImpl) decryptExport(ctx context.Context, in *schema.Export) (*schema.Export, bizerr.BizError) {
	if in == nil {
		return nil, bizerr.New(bizerr.ErrCodeParams, "export is required")
	}

	password, err := r.decryptValue(ctx, in.EncryptedPassword)
	if err != nil {
		return nil, bizerr.WrapError(err, "failed to decrypt export password")
	}
	in.EncryptedPassword = password

	if err := r.decryptCloudStorage(ctx, in.CloudStorage); err != nil {
		return nil, err
	}

	return in, nil
}

func (r *exportRepoImpl) encryptCloudStorage(ctx context.Context, storage *schema.CloudStorage) bizerr.BizError {
	if storage == nil {
		return nil
	}

	if storage.S3 != nil && storage.S3.AccessKey != nil {
		secret, err := r.encryptValue(ctx, storage.S3.AccessKey.Secret)
		if err != nil {
			return bizerr.WrapError(err, "failed to encrypt export s3 access key secret")
		}
		storage.S3.AccessKey.Secret = secret
	}

	if storage.Gcs != nil {
		serviceAccountKey, err := r.encryptValue(ctx, storage.Gcs.ServiceAccountKey)
		if err != nil {
			return bizerr.WrapError(err, "failed to encrypt export gcs service account key")
		}
		storage.Gcs.ServiceAccountKey = serviceAccountKey
	}

	if storage.AzureBlob != nil {
		sasToken, err := r.encryptValue(ctx, storage.AzureBlob.SasToken)
		if err != nil {
			return bizerr.WrapError(err, "failed to encrypt export azure blob sas token")
		}
		storage.AzureBlob.SasToken = sasToken
	}

	if storage.Oss != nil && storage.Oss.AccessKey != nil {
		secret, err := r.encryptValue(ctx, storage.Oss.AccessKey.Secret)
		if err != nil {
			return bizerr.WrapError(err, "failed to encrypt export oss access key secret")
		}
		storage.Oss.AccessKey.Secret = secret
	}
	return nil
}

func (r *exportRepoImpl) decryptCloudStorage(ctx context.Context, storage *schema.CloudStorage) bizerr.BizError {
	if storage == nil {
		return nil
	}

	if storage.S3 != nil && storage.S3.AccessKey != nil {
		secret, err := r.decryptValue(ctx, storage.S3.AccessKey.Secret)
		if err != nil {
			return bizerr.WrapError(err, "failed to decrypt export s3 access key secret")
		}
		storage.S3.AccessKey.Secret = secret
	}

	if storage.Gcs != nil {
		serviceAccountKey, err := r.decryptValue(ctx, storage.Gcs.ServiceAccountKey)
		if err != nil {
			return bizerr.WrapError(err, "failed to decrypt export gcs service account key")
		}
		storage.Gcs.ServiceAccountKey = serviceAccountKey
	}

	if storage.AzureBlob != nil {
		sasToken, err := r.decryptValue(ctx, storage.AzureBlob.SasToken)
		if err != nil {
			return bizerr.WrapError(err, "failed to decrypt export azure blob sas token")
		}
		storage.AzureBlob.SasToken = sasToken
	}

	if storage.Oss != nil && storage.Oss.AccessKey != nil {
		secret, err := r.decryptValue(ctx, storage.Oss.AccessKey.Secret)
		if err != nil {
			return bizerr.WrapError(err, "failed to decrypt export oss access key secret")
		}
		storage.Oss.AccessKey.Secret = secret
	}

	return nil
}

func (r *exportRepoImpl) encryptValue(ctx context.Context, value string) (string, bizerr.BizError) {
	if value == "" || strings.HasPrefix(value, exportEncryptedValuePrefix) {
		return value, nil
	}

	encryptedValue, err := r.encryptor.EncryptBase64(ctx, value)
	if err != nil {
		return "", err
	}

	return exportEncryptedValuePrefix + encryptedValue, nil
}

func (r *exportRepoImpl) decryptValue(ctx context.Context, value string) (string, bizerr.BizError) {
	if value == "" || !strings.HasPrefix(value, exportEncryptedValuePrefix) {
		return value, nil
	}

	decryptedValue, err := r.encryptor.DecryptBase64(ctx, strings.TrimPrefix(value, exportEncryptedValuePrefix))
	if err != nil {
		return "", err
	}

	return decryptedValue, nil
}
