package resourcepool

import (
	"strconv"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	openapicommonv1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/common/v1beta1"
	openapingv1beta2 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/nextgen/v1beta2"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/pkg/common"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

func cidrValidationFindingsToOpenAPI(
	findings []*globalngv1.ResourcePoolCidrValidationFinding,
) ([]*openapingv1beta2.ResourcePoolCidrValidationFinding, error) {
	result := make([]*openapingv1beta2.ResourcePoolCidrValidationFinding, 0, len(findings))
	for _, finding := range findings {
		if finding == nil {
			return nil, bizerr.New(bizerr.ErrCodeUnknown, "CIDR validation finding is missing")
		}
		code, err := createCIDRFindingCodeToOpenAPI(finding.GetCode())
		if err != nil {
			return nil, err
		}
		converted := &openapingv1beta2.ResourcePoolCidrValidationFinding{Code: code, Message: finding.GetMessage()}
		requiresConflict := createCIDRIsOverlapFinding(finding.GetCode())
		if requiresConflict != (finding.ConflictingExclusion != nil) {
			return nil, bizerr.New(bizerr.ErrCodeUnknown, "CIDR validation finding conflict details are inconsistent")
		}
		if requiresConflict {
			converted.ConflictingExclusion, err = createCIDRExclusionToOpenAPI(finding.GetConflictingExclusion())
			if err != nil {
				return nil, err
			}
		}
		result = append(result, converted)
	}
	return result, nil
}

func createCIDRExclusionToOpenAPI(
	exclusion *globalngv1.ResourcePoolCidrExclusion,
) (*openapingv1beta2.ResourcePoolCidrExclusion, error) {
	if exclusion == nil || exclusion.GetType() == globalngv1.ResourcePoolCidrExclusion_TYPE_UNSPECIFIED {
		return nil, bizerr.New(bizerr.ErrCodeUnknown, "CIDR exclusion is invalid")
	}
	converted := &openapingv1beta2.ResourcePoolCidrExclusion{Cidr: exclusion.GetCidr()}
	switch exclusion.GetType() {
	case globalngv1.ResourcePoolCidrExclusion_RESOURCE_POOL:
		if exclusion.ResourcePoolId == nil || exclusion.ResourcePoolDisplayName == nil ||
			exclusion.RegionId == nil || exclusion.CloudProviderId == nil {
			return nil, bizerr.New(bizerr.ErrCodeUnknown, "resource pool CIDR exclusion is incomplete")
		}
		converted.Type = openapingv1beta2.ResourcePoolCidrExclusion_RESOURCE_POOL
		resourcePoolID := strconv.FormatUint(exclusion.GetResourcePoolId(), 10)
		converted.ResourcePoolId = &resourcePoolID
		displayName := exclusion.GetResourcePoolDisplayName()
		converted.ResourcePoolDisplayName = &displayName
		_, regionID, provider, err := common.BuildOpenApiRegionName(
			exclusion.GetCloudProviderId().String(),
			exclusion.GetRegionId().String(),
		)
		expectedProvider, providerErr := createCIDRCloudProviderToOpenAPI(exclusion.GetCloudProviderId())
		if err != nil || providerErr != nil || regionID == "" || provider != expectedProvider {
			return nil, bizerr.New(bizerr.ErrCodeUnknown, "resource pool CIDR exclusion region is invalid")
		}
		converted.RegionId = &regionID
		converted.CloudProvider = &provider
	case globalngv1.ResourcePoolCidrExclusion_O11Y_CLUSTER:
		if createCIDRHasResourcePoolExclusionDetails(exclusion) {
			return nil, bizerr.New(bizerr.ErrCodeUnknown, "O11Y CIDR exclusion contains resource pool details")
		}
		converted.Type = openapingv1beta2.ResourcePoolCidrExclusion_O11Y_CLUSTER
	case globalngv1.ResourcePoolCidrExclusion_TIDB_CLOUD_RESERVED:
		if createCIDRHasResourcePoolExclusionDetails(exclusion) {
			return nil, bizerr.New(bizerr.ErrCodeUnknown, "reserved CIDR exclusion contains resource pool details")
		}
		converted.Type = openapingv1beta2.ResourcePoolCidrExclusion_TIDB_CLOUD_RESERVED
	default:
		return nil, bizerr.New(bizerr.ErrCodeUnknown, "unknown CIDR exclusion type")
	}
	return converted, nil
}

func createCIDRCloudProviderToOpenAPI(
	provider globalcommonv1.CloudProvider_Name,
) (openapicommonv1beta1.Region_CloudProvider, error) {
	switch provider {
	case globalcommonv1.CloudProvider_AWS:
		return openapicommonv1beta1.Region_aws, nil
	case globalcommonv1.CloudProvider_GCP:
		return openapicommonv1beta1.Region_gcp, nil
	case globalcommonv1.CloudProvider_AZURE:
		return openapicommonv1beta1.Region_azure, nil
	case globalcommonv1.CloudProvider_ALICLOUD:
		return openapicommonv1beta1.Region_alicloud, nil
	default:
		return openapicommonv1beta1.Region_CLOUD_PROVIDER_UNSPECIFIED,
			bizerr.New(bizerr.ErrCodeUnknown, "resource pool CIDR exclusion provider is invalid")
	}
}

func createCIDRHasResourcePoolExclusionDetails(exclusion *globalngv1.ResourcePoolCidrExclusion) bool {
	return exclusion.ResourcePoolId != nil || exclusion.ResourcePoolDisplayName != nil ||
		exclusion.RegionId != nil || exclusion.CloudProviderId != nil
}

func createCIDRIsOverlapFinding(code globalngv1.ResourcePoolCidrValidationFinding_Code) bool {
	switch code {
	case globalngv1.ResourcePoolCidrValidationFinding_CIDR_OVERLAPS_RESOURCE_POOL,
		globalngv1.ResourcePoolCidrValidationFinding_CIDR_OVERLAPS_O11Y,
		globalngv1.ResourcePoolCidrValidationFinding_CIDR_OVERLAPS_RESERVED_NETWORK:
		return true
	default:
		return false
	}
}

func createCIDRFindingCodeToOpenAPI(
	code globalngv1.ResourcePoolCidrValidationFinding_Code,
) (openapingv1beta2.ResourcePoolCidrValidationFinding_Code, error) {
	switch code {
	case globalngv1.ResourcePoolCidrValidationFinding_INVALID_CIDR:
		return openapingv1beta2.ResourcePoolCidrValidationFinding_INVALID_CIDR, nil
	case globalngv1.ResourcePoolCidrValidationFinding_CIDR_NOT_PRIVATE:
		return openapingv1beta2.ResourcePoolCidrValidationFinding_CIDR_NOT_PRIVATE, nil
	case globalngv1.ResourcePoolCidrValidationFinding_CIDR_PREFIX_UNSUPPORTED:
		return openapingv1beta2.ResourcePoolCidrValidationFinding_CIDR_PREFIX_UNSUPPORTED, nil
	case globalngv1.ResourcePoolCidrValidationFinding_CIDR_OVERLAPS_RESOURCE_POOL:
		return openapingv1beta2.ResourcePoolCidrValidationFinding_CIDR_OVERLAPS_RESOURCE_POOL, nil
	case globalngv1.ResourcePoolCidrValidationFinding_CIDR_OVERLAPS_O11Y:
		return openapingv1beta2.ResourcePoolCidrValidationFinding_CIDR_OVERLAPS_O11Y, nil
	case globalngv1.ResourcePoolCidrValidationFinding_CIDR_OVERLAPS_RESERVED_NETWORK:
		return openapingv1beta2.ResourcePoolCidrValidationFinding_CIDR_OVERLAPS_RESERVED_NETWORK, nil
	default:
		return openapingv1beta2.ResourcePoolCidrValidationFinding_CODE_UNSPECIFIED,
			bizerr.New(bizerr.ErrCodeUnknown, "unknown CIDR validation finding code")
	}
}
