package cloud_storage

import (
	"fmt"
	"path/filepath"
	"strings"

	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/pkgv2/svcid"
)

const (
	errMsgS3AccessDeny         = "S3 access deny, please check your S3 access key or role arn and uri."
	errMsgGcsAccessDeny        = "GCS access deny, please check your GCS Service Account Key and uri."
	errMsgAzureBlobAccessDeny  = "Azure Blob access deny, please check your Azure Blob SAS token and uri."
	errMsgBucketNotFound       = "S3 bucket not found, please check your S3 bucket URI."
	errMsgOssAccessDeny        = "OSS access deny, please check your OSS access key and uri."
	errMsgImportEmptyFolder    = "The folder to import has no data files."
	errMsgInvalidOssBucketName = "Invalid OSS bucket name, please check your OSS bucket name and uri."
)

var (
	ErrCodeS3AccessDeny           = bizerr.NewBusinessErrCode(svcid.ImportService, 1, "ErrCodeS3AccessDeny")           // S3 access denied
	ErrCodeImportDataFileNotFound = bizerr.NewBusinessErrCode(svcid.ImportService, 2, "ErrCodeImportDataFileNotFound") // import data file not found
	ErrCodeImportEmptyFolder      = bizerr.NewBusinessErrCode(svcid.ImportService, 3, "ErrCodeImportEmptyFolder")      // data dir is empty
	ErrCodeS3BucketNotFound       = bizerr.NewBusinessErrCode(svcid.ImportService, 4, "ErrCodeS3BucketNotFound")       // S3 bucket not found
	ErrCodeGcsAccessDeny          = bizerr.NewBusinessErrCode(svcid.ImportService, 5, "ErrCodeGcsAccessDeny")          // GCS access denied
	ErrCodeAzureBlobAccessDeny    = bizerr.NewBusinessErrCode(svcid.ImportService, 6, "ErrCodeAzureBlobAccessDeny")    // Azure Blob access denied
	ErrCodeOssAccessDeny          = bizerr.NewBusinessErrCode(svcid.ImportService, 7, "ErrCodeOssAccessDeny")          // OSS access denied
	ErrInvalidOssBucketName       = bizerr.NewBusinessErrCode(svcid.ImportService, 8, "ErrInvalidOssBucketName")       // Invalid OSS bucket name
)

// CompressionExtensions Common compression extensions for schema files
var CompressionExtensions = []string{"", ".gz", ".gzip", ".zst", ".zstd", ".snappy"}

// SchemaFileFormat defines the format pattern for schema files
const SchemaFileFormat = "%s%s.%s-schema.sql"

func GenerateOssEndpoint(ossRegion string) string {
	return fmt.Sprintf("https://oss-%s.aliyuncs.com", ossRegion)
}

func GenerateOssInternalEndpoint(ossRegion string) string {
	return fmt.Sprintf("https://oss-%s-internal.aliyuncs.com", ossRegion)
}

func EscapePath(uri string) string {
	// don't use this function when the uri contains query and fragment parts, and no need to use this function when the uri is already escaped
	// s3 url format: s3://bucket-name/prefix, s3 bucket-name does not allow '/', '?' and '#', and there is no query and fragment parts
	// azure storage account name does not allow '/', '?' and '#', and there is no query and fragment parts
	// gcs url format: gs://bucket-name/prefix, gs bucket-name does not allow '/', '?' and '#', and there is no query and fragment parts
	uri = strings.ReplaceAll(uri, "?", "%3F")
	uri = strings.ReplaceAll(uri, "#", "%23")
	return uri
}

func GetBaseDir(dataFile string) string {
	baseDir := filepath.Dir(dataFile)
	if baseDir == "." {
		baseDir = ""
	}
	if baseDir != "" && !strings.HasSuffix(baseDir, "/") {
		baseDir = baseDir + "/"
	}
	return baseDir
}

// getSchemaFilePath returns the full path for schema files without compression extension
func getSchemaFilePath(prefix *string, subDir, schema, table string) string {
	if prefix != nil {
		subDir = GetBaseDir(*prefix) + subDir
	}
	return fmt.Sprintf(SchemaFileFormat, subDir, schema, table)
}

// matchSchemaFileExtension checks if a file path matches any of the expected schema file patterns
// with various compression extensions
func matchSchemaFileExtension(filePath, expectedFilePath string) bool {
	for _, ext := range CompressionExtensions {
		expectedFullPath := expectedFilePath + ext
		if filePath == expectedFullPath {
			return true
		}
	}
	return false
}

func getRelativeFilePath(prefix *string, key string) string {
	if prefix == nil {
		return key
	}
	return strings.TrimPrefix(key, *prefix)
}
