package service

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
	v1 "k8s.io/api/core/v1"
)

const tikvGracePeriodSeconds = 30

var (
	gcpProviderIDPattern = regexp.MustCompile(`^gce://([^/]+)/([^/]+)/([^/]+)$`)
)

func setAnnotationForComponent(tc *v1alpha1.TidbCluster, component, key, value string) (changed bool) {
	var annotations map[string]string
	switch component {
	case "pd":
		if tc.Spec.PD == nil {
			return false
		}
		if tc.Spec.PD.Annotations == nil {
			tc.Spec.PD.Annotations = make(map[string]string)
		}
		annotations = tc.Spec.PD.Annotations
	case "tikv":
		if tc.Spec.TiKV == nil {
			return false
		}
		if tc.Spec.TiKV.Annotations == nil {
			tc.Spec.TiKV.Annotations = make(map[string]string)
		}
		annotations = tc.Spec.TiKV.Annotations
	case "tidb":
		if tc.Spec.TiDB == nil {
			return false
		}
		if tc.Spec.TiDB.Annotations == nil {
			tc.Spec.TiDB.Annotations = make(map[string]string)
		}
		annotations = tc.Spec.TiDB.Annotations
	case "tiflash":
		if tc.Spec.TiFlash == nil {
			return false
		}
		if tc.Spec.TiFlash.Annotations == nil {
			tc.Spec.TiFlash.Annotations = make(map[string]string)
		}
		annotations = tc.Spec.TiFlash.Annotations
	case "tiproxy":
		if tc.Spec.TiProxy == nil {
			return false
		}
		if tc.Spec.TiProxy.Annotations == nil {
			tc.Spec.TiProxy.Annotations = make(map[string]string)
		}
		annotations = tc.Spec.TiProxy.Annotations
	default:
		return false
	}

	if v, ok := annotations[key]; !ok || v != value {
		annotations[key] = value
		return true
	}
	return false
}

func getCloudProviderFromNode(node *v1.Node) (string, error) {
	providerID := node.Spec.ProviderID
	if providerID == "" {
		return "", fmt.Errorf("providerID is empty")
	}
	if parts := strings.Split(providerID, "://"); len(parts) < 1 {
		return "", fmt.Errorf("providerID format is incorrect")
	} else {
		if parts[0] == "gce" {
			return "gcp", nil
		}
		return parts[0], nil
	}
}

// parseEKSNodeProviderID extracts EC2 instance id from the `spec.providerID` of a EKS node.
// The `providerID` looks like `aws:///us-west-2a/i-xxxxx`.
func parseEKSNodeProviderID(providerID string) (region string, instanceID string, err error) {
	parts := strings.Split(providerID, "/")

	if len(parts) == 5 {
		// Extract region (removing the last character to get rid of the availability zone)
		region = parts[3][:len(parts[3])-1]

		instanceID = parts[4]
		return region, instanceID, nil
	}

	return "", "", fmt.Errorf("invalid EKS node providerID: %s", providerID)
}

// parseGKENodeProviderID extracts GCP project id, zone and instance name from the `spec.providerID` of a GKE node.
// The `providerID` looks like `gce://<project-id>/<zone>/gke-xxx-node-po-xxx`.
func parseGKENodeProviderID(providerID string) (projectID string, zone string, instanceName string, err error) {
	matches := gcpProviderIDPattern.FindStringSubmatch(providerID)

	if len(matches) < 4 {
		return "", "", "", fmt.Errorf("invalid GKE node providerID: %s", providerID)
	}

	return matches[1], matches[2], matches[3], nil
}

// azure:///subscriptions/<subscription>/resourceGroups/<resource-group>/providers/Microsoft.Compute/virtualMachineScaleSets/<vmss>/virtualMachines/<instance-id>
func parseAKSNodeProviderID(providerID string) (subscription, resourceGroup, vmss, instanceID string, err error) {
	s := strings.TrimPrefix(providerID, "azure:///")
	parts := strings.Split(s, "/")
	if len(parts) != 10 {
		return "", "", "", "", fmt.Errorf("invalid AKS node providerID: %s", providerID)
	}
	return parts[1], parts[3], parts[7], parts[9], nil
}

// providerID format: kind://docker/kind/kind-control-plane
func parseKindNodeProviderID(providerID string) (string, error) {
	s := strings.Split(providerID, "/")
	if len(s) == 5 {
		return s[len(s)-1], nil
	}
	return "", fmt.Errorf("invalid kind node providerID: %s", providerID)
}
