package config

import (
	v1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	"strings"
	"wdd.io/agent-common/logger"
	"wdd.io/agent-common/utils"
)

var log = logger.Log

type CmiiInterfaceConvert interface {
}

type CmiiPodInterface struct {
	Name      string
	Namespace string
	//ReplicaSetName    string
	ContainerImageMap map[string]string
	PodIP             string
	HostIP            string
	NodeName          string
	RestartCountMap   map[string]int32
	RestartCount      int32
	ContainerName     string
	Image             string
	ImageTag          string
	GitBranch         string
	GitCommit         string
	PodStatus         bool
	PodPhase          corev1.PodPhase
}

type CmiiDeploymentInterface struct {
	Name              string
	Namespace         string
	AvailableReplicas int32
	Replicas          int32
	ContainerImageMap map[string]string
	ContainerName     string
	Image             string
	ImageTag          string
	GitBranch         string
	GitCommit         string
	StatusOk          bool
}

type CmiiNodeInterface struct {
	Name               string
	Hostname           string
	InternalIP         string
	KernelVersion      string
	OsImage            string
	Architecture       string
	KubeletVersion     string
	CpuCapacity        string
	MemoryCapacity     string
	PodCapacity        string
	StorageCapacity    string
	Labels             map[string]string
	Unschedulable      bool
	NodeStatus         bool
	MemoryPressure     bool
	DiskPressure       bool
	PIDPressure        bool
	NetworkUnavailable bool
}

func (deploy CmiiDeploymentInterface) Convert(deployment v1.Deployment) CmiiDeploymentInterface {

	containers := deployment.Spec.Template.Spec.Containers

	containerImageMap := make(map[string]string, len(containers))
	if len(containers) > 1 {
		log.WarnF("[CmiiDeploymentInterface Convert] - deployment [%s] [%s] container greater than one !", deployment.Namespace, deployment.Name)
	}
	for _, container := range containers {
		containerImageMap[container.Name] = container.Image
		deploy.Image = container.Image
		deploy.ContainerName = container.Name

		if strings.Contains(container.Image, ":8033") {
			deploy.ImageTag = strings.Split(container.Image, ":")[2]
		} else {
			deploy.ImageTag = strings.Split(container.Image, ":")[1]
		}

		for _, envVar := range container.Env {
			if strings.HasPrefix(envVar.Name, "GIT_BRANCH") {
				deploy.GitBranch = envVar.Value
			}

			if strings.HasPrefix(envVar.Name, "GIT_COMMIT") {
				deploy.GitCommit = envVar.Value
			}
		}
	}

	deploy.Name = deployment.Name
	deploy.Namespace = deployment.Namespace
	deploy.AvailableReplicas = deployment.Status.AvailableReplicas
	deploy.Replicas = *deployment.Spec.Replicas
	deploy.ContainerImageMap = containerImageMap
	deploy.StatusOk = deployment.Status.AvailableReplicas == *deployment.Spec.Replicas

	return deploy
}

func (deploy CmiiDeploymentInterface) ConvertFromStatefulSet(statefulSet v1.StatefulSet) CmiiDeploymentInterface {
	containers := statefulSet.Spec.Template.Spec.Containers

	containerImageMap := make(map[string]string, len(containers))
	if len(containers) > 1 {
		log.WarnF("[CmiiDeploymentInterface ConvertFromStatefulSet] - statefulSet [%s] [%s] container greater than one !", statefulSet.Namespace, statefulSet.Name)
	}
	for _, container := range containers {
		containerImageMap[container.Name] = container.Image
		deploy.Image = container.Image
		deploy.ContainerName = container.Name
		deploy.ImageTag = strings.Split(container.Image, ":")[1]

		for _, envVar := range container.Env {
			if strings.HasPrefix(envVar.Name, "GIT_BRANCH") {
				deploy.GitBranch = envVar.Value
			}

			if strings.HasPrefix(envVar.Name, "GIT_COMMIT") {
				deploy.GitCommit = envVar.Value
			}
		}
	}

	deploy.Name = statefulSet.Name
	deploy.Namespace = statefulSet.Namespace
	deploy.AvailableReplicas = statefulSet.Status.AvailableReplicas
	deploy.Replicas = *statefulSet.Spec.Replicas
	deploy.ContainerImageMap = containerImageMap
	deploy.StatusOk = statefulSet.Status.AvailableReplicas == *statefulSet.Spec.Replicas

	return deploy
}

func (pod CmiiPodInterface) Convert(podDetail corev1.Pod) CmiiPodInterface {

	containers := podDetail.Spec.Containers
	containerImageMap := make(map[string]string, len(containers))
	if len(containers) > 1 {
		log.WarnF("[CmiiDeploymentInterface Convert] - pod [%s] [%s] container greater than one !", podDetail.Namespace, podDetail.Name)
	}
	for _, container := range containers {
		containerImageMap[container.Name] = container.Image

		pod.Image = container.Image
		pod.ContainerName = container.Name
		if strings.Contains(container.Image, ":") {
			pod.ImageTag = strings.Split(container.Image, ":")[1]
		}

		for _, envVar := range container.Env {
			if strings.HasPrefix(envVar.Name, "GIT_BRANCH") {
				pod.GitBranch = envVar.Value
			}

			if strings.HasPrefix(envVar.Name, "GIT_COMMIT") {
				pod.GitCommit = envVar.Value
			}
		}
	}

	containerStatuses := podDetail.Status.ContainerStatuses
	containerStatusMap := make(map[string]int32, len(containerStatuses))

	for _, containerStatus := range containerStatuses {
		containerStatusMap[containerStatus.Name] = containerStatus.RestartCount
		pod.RestartCount = utils.MaxInt32(pod.RestartCount, containerStatus.RestartCount)
	}

	pod.Name = podDetail.Name
	pod.Namespace = podDetail.Namespace
	pod.ContainerImageMap = containerImageMap
	pod.RestartCountMap = containerStatusMap
	pod.PodIP = podDetail.Status.PodIP
	pod.HostIP = podDetail.Status.HostIP
	pod.NodeName = podDetail.Spec.NodeName
	pod.PodPhase = podDetail.Status.Phase

	switch podDetail.Status.Phase {
	case corev1.PodFailed:
		pod.PodStatus = false
		break
	case corev1.PodPending:
		pod.PodStatus = false
		break
	case corev1.PodReasonUnschedulable:
		pod.PodStatus = false
		break
	default:
		pod.PodStatus = true
		break
	}

	return pod
}

func (node CmiiNodeInterface) Convert(sourceNode corev1.Node) CmiiNodeInterface {

	node.Name = sourceNode.Name
	for _, nodeAddress := range sourceNode.Status.Addresses {
		if nodeAddress.Type == corev1.NodeInternalIP {
			node.InternalIP = nodeAddress.Address
		}
		if nodeAddress.Type == corev1.NodeHostName {
			node.Hostname = nodeAddress.Address
		}
	}

	node.KernelVersion = sourceNode.Status.NodeInfo.KernelVersion
	node.OsImage = sourceNode.Status.NodeInfo.OSImage
	node.KubeletVersion = sourceNode.Status.NodeInfo.KubeletVersion
	node.Architecture = sourceNode.Status.NodeInfo.Architecture

	node.CpuCapacity = sourceNode.Status.Capacity.Cpu().String()
	node.MemoryCapacity = sourceNode.Status.Capacity.Memory().String()
	node.PodCapacity = sourceNode.Status.Capacity.Pods().String()
	node.StorageCapacity = sourceNode.Status.Capacity.Storage().String()

	node.Labels = sourceNode.Labels

	for _, nodeCondition := range sourceNode.Status.Conditions {
		switch nodeCondition.Type {
		case corev1.NodeReady:
			node.NodeStatus = uniformNodeConditionStatus(nodeCondition.Status)
			break
		case corev1.NodeMemoryPressure:
			node.MemoryPressure = uniformNodeConditionStatus(nodeCondition.Status)
			break
		case corev1.NodeDiskPressure:
			node.DiskPressure = uniformNodeConditionStatus(nodeCondition.Status)
			break
		case corev1.NodePIDPressure:
			node.PIDPressure = uniformNodeConditionStatus(nodeCondition.Status)
			break
		case corev1.NodeNetworkUnavailable:
			node.NetworkUnavailable = uniformNodeConditionStatus(nodeCondition.Status)
			break
		}
	}

	node.Unschedulable = sourceNode.Spec.Unschedulable

	return node
}

func uniformNodeConditionStatus(conditionType corev1.ConditionStatus) bool {
	if conditionType == corev1.ConditionTrue {
		return true
	} else {
		return false
	}
}
