package resourcepool

import (
	"slices"
	"strings"

	"github.com/tidbcloud/tiinf/sdk/auditlog"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	openapingv1beta2 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/nextgen/v1beta2"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/adapter/eventbus"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/pkg/common"
)

const (
	resourcePoolUpdateDisplayNameAction = "update_display_name"
	resourcePoolUpdateVcpuLimitAction   = "update_vcpu_limit"
	resourcePoolResumeAction            = "resume"
)

func newResourcePoolAuditDetails(
	resourcePoolID string,
	pool *openapingv1beta2.ResourcePool,
	servicePlan globalcommonv1.ServicePlan,
) auditlog.Details {
	details := eventbus.NewAuditLogDetails(eventbus.TierTypePremium)
	details[auditlog.KeyResourcePoolId] = resourcePoolID
	if servicePlan != globalcommonv1.ServicePlan_SERVICE_PLAN_UNSPECIFIED {
		details[auditlog.KeyServicePlan] = common.CvtOpenAPIServicePlan(servicePlan)
	}
	if pool == nil {
		return details
	}
	if pool.GetResourcePoolId() != "" {
		details[auditlog.KeyResourcePoolId] = pool.GetResourcePoolId()
	}
	details[auditlog.KeyDisplayName] = pool.GetDisplayName()
	cloudProvider, region := resourcePoolAuditProviderAndRegion(pool)
	details[auditlog.KeyCloudProvider] = cloudProvider
	details[auditlog.KeyRegion] = region
	details[auditlog.KeyVpcCidr] = pool.GetVpcCidr()
	details[auditlog.KeyHighAvailabilityType] = pool.GetHighAvailabilityType().String()
	details[auditlog.KeyVcpuLimit] = resourcePoolVcpuLimitAuditValue(pool.GetVcpuLimit())
	details[auditlog.KeyCapacity] = resourcePoolCapacityAuditValue(pool.GetCapacity())
	if pool.GetCreatedBy() != "" {
		details[auditlog.KeyCreatedBy] = pool.GetCreatedBy()
	}
	return details
}

func resourcePoolAuditProviderAndRegion(
	pool *openapingv1beta2.ResourcePool,
) (provider, region string) {
	if provider, region, ok := strings.Cut(pool.GetRegionId(), "-"); ok {
		return provider, region
	}
	return pool.GetCloudProvider().String(), pool.GetRegionId()
}

func resourcePoolVcpuLimitAuditValue(limit *openapingv1beta2.VcpuLimit) map[string]any {
	if limit == nil {
		return nil
	}
	switch policy := limit.GetPolicy().(type) {
	case *openapingv1beta2.VcpuLimit_LimitedVcpus:
		return map[string]any{"mode": "limited", "vcpus": policy.LimitedVcpus}
	case *openapingv1beta2.VcpuLimit_Unlimited:
		return map[string]any{"mode": "unlimited"}
	default:
		return nil
	}
}

func resourcePoolCapacityAuditValue(capacity *openapingv1beta2.ResourcePoolCapacity) map[string]any {
	if capacity == nil {
		return nil
	}
	result := make(map[string]any, 2)
	if capacity.GetTikv() != nil {
		result["tikv"] = resourcePoolComponentCapacityAuditValue(capacity.GetTikv())
	}
	if capacity.GetTiflash() != nil {
		result["tiflash"] = resourcePoolComponentCapacityAuditValue(capacity.GetTiflash())
	}
	return result
}

func resourcePoolComponentCapacityAuditValue(capacity *openapingv1beta2.ComponentCapacity) map[string]any {
	result := map[string]any{"node_count": capacity.GetNodeCount()}
	if capacity.GetNodeSpec() != nil {
		result["node_spec"] = map[string]any{
			"cpu":       capacity.GetNodeSpec().GetCpu(),
			"memory_gi": capacity.GetNodeSpec().GetMemoryGi(),
		}
	}
	if len(capacity.GetStorages()) > 0 {
		storages := make([]map[string]any, 0, len(capacity.GetStorages()))
		for _, storage := range capacity.GetStorages() {
			storages = append(storages, map[string]any{
				"name":    storage.GetName(),
				"size_gi": storage.GetSizeGi(),
			})
		}
		result["storages"] = storages
	}
	return result
}

func sortedResourcePoolTagKeys(tags map[string]string) []string {
	keys := make([]string, 0, len(tags))
	for key := range tags {
		keys = append(keys, key)
	}
	slices.Sort(keys)
	return keys
}

func sortedResourcePoolRemovedTagKeys(keys []string) []string {
	result := append([]string(nil), keys...)
	slices.Sort(result)
	return result
}
