package cluster

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/pingcap/errors"
	"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
	"golang.org/x/mod/semver"
	corev1 "k8s.io/api/core/v1"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-api/pkg/consts"
	"github.com/tidbcloud/infra-api/pkg/o11y/metric_agent"
)

func setHealthCheckPortAnnotation(svc *corev1.Service) error {
	healthCheckPort := int32(0)
	for _, port := range svc.Spec.Ports {
		if port.Name == "health-checker" {
			healthCheckPort = port.NodePort
		}
	}
	if healthCheckPort == 0 {
		return errors.Errorf("health check port is not yet set on service %s/%s", svc.Namespace, svc.Name)
	}
	if svc.Annotations == nil {
		svc.Annotations = map[string]string{}
	}
	svc.Annotations[consts.AnnHealthCheckPort] = strconv.FormatInt(int64(healthCheckPort), 10)
	return nil
}

func gardenerStatusFromCluster(c *cloudv1.Cluster) (*cloudv1.GardenerStatus, error) {
	switch c.GetProviderType() {
	case consts.ProviderTypeAWS:
		awsStatus, err := cloudv1.AwsClusterStatusFromCluster(c)
		if err != nil {
			return nil, err
		}
		return &awsStatus.Gardener, nil
	case consts.ProviderTypeGCP:
		gcpStatus, err := cloudv1.GcpClusterStatusFromCluster(c)
		if err != nil {
			return nil, err
		}
		return &gcpStatus.Gardener, nil
	default:
		return nil, fmt.Errorf("unsupported provider type %s", c.GetProviderType())
	}
}

func setGardenerStatusToCluster(c *cloudv1.Cluster, g cloudv1.GardenerStatus) error {
	switch c.GetProviderType() {
	case consts.ProviderTypeAWS:
		if err := cloudv1.MutateAwsClusterStatus(c, func(status *cloudv1.AwsClusterStatus) error {
			status.Gardener = g
			return nil
		}); err != nil {
			return err
		}
	case consts.ProviderTypeGCP:
		if err := cloudv1.MutateGcpClusterStatus(c, func(status *cloudv1.GcpClusterStatus) error {
			status.Gardener = g
			return nil
		}); err != nil {
			return err
		}
	default:
		return fmt.Errorf("unsupported provider %s", c.GetProviderType())
	}
	return nil
}

func semSupported(version string) bool {
	return semver.Compare(version, minSemSupportedVersion) >= 0
}

// https://jira.tidbcloud.com/browse/TCOC-179
func analyzeSupported(tc *v1alpha1.TidbCluster) bool {
	return semver.Compare(tc.Spec.Version, minAnalyzeVersion) >= 0
}

/* Naming rules ported from tidbcloud/dbaas, should be visible to cluster controller only */

/* Admin TiDB related */
func getExtraAccessName(tcName string) string {
	return fmt.Sprintf("%s-tidb-extra-access", tcName)
}

func getExtraAccessSvcName(tcName string) string {
	return fmt.Sprintf("%s-tidb-extra-service", tcName)
}

func getExtraAccessSecretName(tcName string) string {
	return fmt.Sprintf("%s-tidb-extra-secret", tcName)
}

func getExtraAccessCMName(tcName string) string {
	return fmt.Sprintf("%s-tidb-extra-config-map", tcName)
}

func getPrivilegedTiDBAddress(tcName, ns string) string {
	return fmt.Sprintf("%s.%s", getExtraAccessSvcName(tcName), ns)
}

func getPrivateEndpointAddress(publicAddress string) string {
	return fmt.Sprintf("private-%s", publicAddress)
}

func getPrivateLinkEndpointAddress(publicAddress string) string {
	return fmt.Sprintf("privatelink-%s", publicAddress)
}

/* TLS related */
func tidbServerSecretName(clusterName string) string {
	return fmt.Sprintf("%s-tidb-server-secret", clusterName)
}

func tidbClusterComponentSecretName(clusterName string, comp cloudv1.ComponentType) string {
	return fmt.Sprintf("%s-%s-cluster-secret", clusterName, comp)
}

func clusterClientSecretName(clusterName string) string {
	return fmt.Sprintf("%s-cluster-client-secret", clusterName)
}

/* Endpoints related */
func getPrivateServiceName(clusterName string) string {
	return fmt.Sprintf("%s-tidb-private", clusterName)
}

func getInitializerName(clusterName string) string {
	return fmt.Sprintf("%s-tidb-init", clusterName)
}

func getAwsElbName(elbDNSName string) (string, error) {
	// the format of elbDNSName is like this: a097b3f8881694b90aa2599d4dd12b00-843587f16d327b53.elb.us-west-2.amazonaws.com
	elbDNSNameSlice := strings.Split(elbDNSName, "-")
	if len(elbDNSNameSlice) == 0 {
		return "", fmt.Errorf("tidb ELB DNS name cannot be empty")
	}

	return elbDNSNameSlice[0], nil
}

func GetIDs(cc *ClusterContext) (tenantID, projectID, clusterID uint64) {
	tenantIDStr, projectIDStr, clusterIDStr := metric_agent.GetIDsFromClusterCRD(cc.Target, cc.Network.Name)
	tenantID, _ = strconv.ParseUint(tenantIDStr, 10, 64)
	projectID, _ = strconv.ParseUint(projectIDStr, 10, 64)
	clusterID, _ = strconv.ParseUint(clusterIDStr, 10, 64)
	return
}
