package tidbgroup

import (
	"fmt"
	"net"
	"regexp"
	"strings"

	"github.com/samber/lo"
	"go.opentelemetry.io/otel"

	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

var tracer = otel.Tracer("domain.tidbgroup")

var (
	genericNameRegexp = regexp.MustCompile(`^[A-Za-z0-9][-A-Za-z0-9]{2,62}[A-Za-z0-9]$`)
	genericNameErrMsg = "must be 4-64 characters that can only include numbers, letters, and hyphens, and the first and last character must be a letter or number"
)

func validatePublicTrafficFilterItems(
	publicEndpoint *globaldedicatedv1.TidbNodeGroup_Endpoint_PublicEndpoint,
) bizerr.BizError {
	if publicEndpoint == nil {
		return nil
	}

	existMap := map[string]bool{}
	for idx, trafficFilter := range publicEndpoint.GetIpAllowList() {
		if strings.Contains(trafficFilter.GetCidrBlock(), "/") {
			_, _, err := net.ParseCIDR(trafficFilter.GetCidrBlock())
			if err != nil {
				return bizerr.New(bizerr.ErrCodeParams, "invalid cidr: %s", trafficFilter.GetCidrBlock())
			}
			if val, ok := existMap[trafficFilter.GetCidrBlock()]; ok && val {
				return bizerr.New(bizerr.ErrCodeDuplicatedCIDR, "duplicate CIDR: %s", trafficFilter.GetCidrBlock())
			}
			existMap[trafficFilter.GetCidrBlock()] = true
		} else {
			ip := net.ParseIP(trafficFilter.GetCidrBlock())
			if ip == nil {
				return bizerr.New(bizerr.ErrCodeParams, "invalid IP address: %s", trafficFilter.GetCidrBlock())
			}
			publicEndpoint.GetIpAllowList()[idx].CidrBlock = fmt.Sprintf("%s/32", trafficFilter.GetCidrBlock())
			if val, ok := existMap[publicEndpoint.GetIpAllowList()[idx].GetCidrBlock()]; ok && val {
				return bizerr.New(bizerr.ErrCodeDuplicatedCIDR, "duplicate IP: %s", trafficFilter.GetCidrBlock())
			}
			existMap[publicEndpoint.GetIpAllowList()[idx].GetCidrBlock()] = true
		}
	}

	return nil
}

func checkTiProxySpu(
	cluster *entity.Cluster,
	tiproxySetting *globaldedicatedv1.TidbNodeGroup_TiProxySetting,
) bizerr.BizError {
	tiproxyComp, ok := lo.Find(
		cluster.GetClusterComponents(),
		func(i *globaldedicatedv1.Cluster_ClusterComponent) bool {
			return i.GetComponentType() == globaldedicatedv1.ComponentType_TIPROXY && i.GetSpuName() != "" &&
				i.GetCount() > 0
		},
	)
	if ok {
		if tiproxySetting.GetSpuName() != tiproxyComp.GetSpuName() {
			return bizerr.New(
				bizerr.ErrCodeParams,
				"TiProxy node type must remain consistent if there are existing TiProxy nodes running",
			)
		}
	}
	return nil
}
