package keyspacerole

import (
	"fmt"
	"path"
	"strings"

	apiv1 "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	dplanemgrv1 "github.com/tidbcloud/cluster-service-ng/api/dataplane-manager/v1"
	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	schemav1 "github.com/tidbcloud/cluster-service-ng/pkg/storage/etcd/schema/v1"
)

type Params struct {
	DataplaneID   string
	KeyspaceName  string
	BucketID      string
	AllowedPrefix string
	ExternalID    string
	Tags          map[string]string
}

func ShouldSkip(sharedPool *entity.SharedPool) bool {
	if sharedPool == nil {
		return false
	}
	return !isSupportedProvider(sharedPool.GetProvider().GetType())
}

// ShouldSkipForCluster reports whether per-cluster keyspace role lifecycle should be skipped.
// Keyspace role lifecycle only skips unsupported providers.
func ShouldSkipForCluster(sharedPool *entity.SharedPool, _ *entity.TiDBCluster) bool {
	if sharedPool == nil {
		return false
	}
	return ShouldSkip(sharedPool)
}

func BuildParams(sharedPool *entity.SharedPool, tidbCluster *entity.TiDBCluster, dataplaneID string) (*Params, error) {
	if sharedPool == nil {
		return nil, fmt.Errorf("sharedPool is required")
	}
	if tidbCluster == nil {
		return nil, fmt.Errorf("tidbCluster is required")
	}
	if dataplaneID == "" {
		dataplaneID = sharedPool.GetDataplaneName()
	}
	if dataplaneID == "" {
		return nil, fmt.Errorf("dataplane id is required")
	}

	clusterID := tidbCluster.GetId()
	if clusterID == "" {
		return nil, fmt.Errorf("tidb cluster id is required")
	}

	internalStorage := sharedPool.GetInternalStorage()
	if internalStorage == nil {
		return nil, fmt.Errorf("sharedPool internal storage is required")
	}
	if internalStorage.GetRegion() == "" {
		return nil, fmt.Errorf("sharedPool internal storage region is required")
	}

	tenantID := tidbCluster.GetTenantId()
	if tenantID == "" {
		tenantID = sharedPool.TenantId
	}
	if tenantID == "" {
		tenantID = schemav1.MultiTenantTag
	}

	return &Params{
		DataplaneID:  dataplaneID,
		KeyspaceName: clusterID,
		BucketID:     entity.GetInternalBucketId(sharedPool.GetId(), internalStorage.GetRegion()),
		// DM Import Into writes to <internal_bucket>/<tidb_cluster_id>/<task_id>.
		AllowedPrefix: path.Join(NormalizeStoragePrefix(internalStorage.GetPrefix()), clusterID),
		ExternalID:    clusterID,
		Tags: map[string]string{
			schemav1.LabelUserTenantKey:  tenantID,
			schemav1.LabelSharedPoolKey:  sharedPool.GetId(),
			schemav1.LabelUserClusterKey: clusterID,
			schemav1.LabelDataplaneKey:   dataplaneID,
			schemav1.LabelRegionKey:      internalStorage.GetRegion(),
		},
	}, nil
}

func NormalizeStoragePrefix(prefix string) string {
	return strings.Trim(strings.TrimSpace(prefix), "/")
}

func (p *Params) CreateRequest() *dplanemgrv1.CreateKeyspaceRoleRequest {
	return &dplanemgrv1.CreateKeyspaceRoleRequest{
		DataplaneId: p.DataplaneID,
		KeyspaceRole: &dplanemgrv1.KeyspaceRole{
			KeyspaceName:  p.KeyspaceName,
			BucketId:      p.BucketID,
			AllowedPrefix: p.AllowedPrefix,
			ExternalId:    p.ExternalID,
			Tags:          p.Tags,
		},
	}
}

func BuildDeleteRequest(sharedPool *entity.SharedPool, tidbCluster *entity.TiDBCluster, dataplaneID string) (*dplanemgrv1.DeleteKeyspaceRoleRequest, error) {
	if sharedPool == nil {
		return nil, fmt.Errorf("sharedPool is required")
	}
	if tidbCluster == nil {
		return nil, fmt.Errorf("tidbCluster is required")
	}
	if dataplaneID == "" {
		dataplaneID = sharedPool.GetDataplaneName()
	}
	if dataplaneID == "" {
		return nil, fmt.Errorf("dataplane id is required")
	}
	clusterID := tidbCluster.GetId()
	if clusterID == "" {
		return nil, fmt.Errorf("tidb cluster id is required")
	}

	return &dplanemgrv1.DeleteKeyspaceRoleRequest{
		DataplaneId:  dataplaneID,
		KeyspaceName: clusterID,
	}, nil
}

func isSupportedProvider(provider apiv1.ProviderType) bool {
	switch provider {
	case apiv1.ProviderType_AWS, apiv1.ProviderType_ALICLOUD:
		return true
	default:
		return false
	}
}
