// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT

package k8sclient

import (
	"log"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var nodeArray = []interface{}{
	&v1.Node{
		ObjectMeta: metav1.ObjectMeta{
			Name:            "ip-192-168-200-63.eu-west-1.compute.internal",
			GenerateName:    "",
			Namespace:       "",
			SelfLink:        "/api/v1/nodes/ip-192-168-200-63.eu-west-1.compute.internal",
			UID:             "9e31e901-4c14-11e9-9bd4-02cf86190d00",
			ResourceVersion: "6505830",
			Generation:      0,
			CreationTimestamp: metav1.Time{
				Time: time.Now(),
			},
			Labels: map[string]string{
				"beta.kubernetes.io/arch":                  "amd64",
				"beta.kubernetes.io/instance-type":         "t3.medium",
				"beta.kubernetes.io/os":                    "linux",
				"failure-domain.beta.kubernetes.io/region": "eu-west-1",
				"failure-domain.beta.kubernetes.io/zone":   "eu-west-1c",
				"kubernetes.io/hostname":                   "ip-192-168-200-63.eu-west-1.compute.internal",
			},
			Annotations: map[string]string{
				"node.alpha.kubernetes.io/ttl":                           "0",
				"volumes.kubernetes.io/controller-managed-attach-detach": "true",
			},
		},
		Status: v1.NodeStatus{
			Conditions: []v1.NodeCondition{
				{
					Type:   "MemoryPressure",
					Status: "False",
					LastHeartbeatTime: metav1.Time{
						Time: time.Now(),
					},
					LastTransitionTime: metav1.Time{
						Time: time.Now(),
					},
					Reason:  "KubeletHasSufficientMemory",
					Message: "kubelet has sufficient memory available",
				},
				{
					Type:   "DiskPressure",
					Status: "False",
					LastHeartbeatTime: metav1.Time{
						Time: time.Now(),
					},
					LastTransitionTime: metav1.Time{
						Time: time.Now(),
					},
					Reason:  "KubeletHasNoDiskPressure",
					Message: "kubelet has no disk pressure",
				},
				{
					Type:   "PIDPressure",
					Status: "False",
					LastHeartbeatTime: metav1.Time{
						Time: time.Now(),
					},
					LastTransitionTime: metav1.Time{
						Time: time.Now(),
					},
					Reason:  "KubeletHasSufficientPID",
					Message: "kubelet has sufficient PID available",
				},
				{
					Type:   "Ready",
					Status: "True",
					LastHeartbeatTime: metav1.Time{
						Time: time.Now(),
					},
					LastTransitionTime: metav1.Time{
						Time: time.Now(),
					},
					Reason:  "KubeletReady",
					Message: "kubelet is posting ready status",
				},
			},
			NodeInfo: v1.NodeSystemInfo{
				MachineID:               "ec2bb261412a689dd19139d9a526407f",
				SystemUUID:              "EC2BB261-412A-689D-D191-39D9A526407F",
				BootID:                  "1d5db5f1-03e8-48f3-9c49-21781a9ba1ae",
				KernelVersion:           "4.14.97-90.72.amzn2.x86_64",
				OSImage:                 "Amazon Linux 2",
				ContainerRuntimeVersion: "docker://18.6.1",
				KubeletVersion:          "v1.11.5",
				KubeProxyVersion:        "v1.11.5",
				OperatingSystem:         "linux",
				Architecture:            "amd64",
			},
		},
	},
	&v1.Node{
		ObjectMeta: metav1.ObjectMeta{
			Name:            "ip-192-168-76-61.eu-west-1.compute.internal",
			GenerateName:    "",
			Namespace:       "",
			SelfLink:        "/api/v1/nodes/ip-192-168-76-61.eu-west-1.compute.internal",
			UID:             "9f9e79a7-4c14-11e9-b47e-066a7a20bac8",
			ResourceVersion: "6505829",
			Generation:      0,
			CreationTimestamp: metav1.Time{
				Time: time.Now(),
			},
			Labels: map[string]string{
				"beta.kubernetes.io/os":                    "linux",
				"failure-domain.beta.kubernetes.io/region": "eu-west-1",
				"failure-domain.beta.kubernetes.io/zone":   "eu-west-1a",
				"kubernetes.io/hostname":                   "ip-192-168-76-61.eu-west-1.compute.internal",
				"beta.kubernetes.io/arch":                  "amd64",
				"beta.kubernetes.io/instance-type":         "t3.medium",
			},
			Annotations: map[string]string{
				"node.alpha.kubernetes.io/ttl":                           "0",
				"volumes.kubernetes.io/controller-managed-attach-detach": "true",
			},
		},
		Status: v1.NodeStatus{
			Conditions: []v1.NodeCondition{
				{
					Type:   "MemoryPressure",
					Status: "False",
					LastHeartbeatTime: metav1.Time{
						Time: time.Now(),
					},
					LastTransitionTime: metav1.Time{
						Time: time.Now(),
					},
					Reason:  "KubeletHasSufficientMemory",
					Message: "kubelet has sufficient memory available",
				},
				{
					Type:   "DiskPressure",
					Status: "False",
					LastHeartbeatTime: metav1.Time{
						Time: time.Now(),
					},
					LastTransitionTime: metav1.Time{
						Time: time.Now(),
					},
					Reason:  "KubeletHasNoDiskPressure",
					Message: "kubelet has no disk pressure",
				},
				{
					Type:   "PIDPressure",
					Status: "False",
					LastHeartbeatTime: metav1.Time{
						Time: time.Now(),
					},
					LastTransitionTime: metav1.Time{
						Time: time.Now(),
					},
					Reason:  "KubeletHasSufficientPID",
					Message: "kubelet has sufficient PID available",
				},
				{
					Type:   "Ready",
					Status: "True",
					LastHeartbeatTime: metav1.Time{
						Time: time.Now(),
					},
					LastTransitionTime: metav1.Time{
						Time: time.Now(),
					},
					Reason:  "KubeletReady",
					Message: "kubelet is posting ready status",
				},
			},
			NodeInfo: v1.NodeSystemInfo{
				MachineID:               "ec275328a762912e9c1777bc59328231",
				SystemUUID:              "EC275328-A762-912E-9C17-77BC59328231",
				BootID:                  "02a66fbd-7030-4f7d-85c4-935a32b5d3e9",
				KernelVersion:           "4.14.97-90.72.amzn2.x86_64",
				OSImage:                 "Amazon Linux 2",
				ContainerRuntimeVersion: "docker://18.6.1",
				KubeletVersion:          "v1.11.5",
				KubeProxyVersion:        "v1.11.5",
				OperatingSystem:         "linux",
				Architecture:            "amd64",
			},
		},
	},
	&v1.Node{
		ObjectMeta: metav1.ObjectMeta{
			Name:            "ip-192-168-153-1.eu-west-1.compute.internal",
			GenerateName:    "",
			Namespace:       "",
			SelfLink:        "/api/v1/nodes/ip-192-168-153-1.eu-west-1.compute.internal",
			UID:             "9eb3a09d-4c14-11e9-b47e-066a7a20bac8",
			ResourceVersion: "6505831",
			Generation:      0,
			CreationTimestamp: metav1.Time{
				Time: time.Now(),
			},
			Labels: map[string]string{
				"beta.kubernetes.io/arch":                  "amd64",
				"beta.kubernetes.io/instance-type":         "t3.medium",
				"beta.kubernetes.io/os":                    "linux",
				"failure-domain.beta.kubernetes.io/region": "eu-west-1",
				"failure-domain.beta.kubernetes.io/zone":   "eu-west-1b",
				"kubernetes.io/hostname":                   "ip-192-168-153-1.eu-west-1.compute.internal",
			},
			Annotations: map[string]string{
				"node.alpha.kubernetes.io/ttl":                           "0",
				"volumes.kubernetes.io/controller-managed-attach-detach": "true",
			},
		},
		Status: v1.NodeStatus{
			Conditions: []v1.NodeCondition{
				{
					Type:   "MemoryPressure",
					Status: "False",
					LastHeartbeatTime: metav1.Time{
						Time: time.Now(),
					},
					LastTransitionTime: metav1.Time{
						Time: time.Now(),
					},
					Reason:  "KubeletHasSufficientMemory",
					Message: "kubelet has sufficient memory available",
				},
				{ //This entry shows failed node
					Type:   "DiskPressure",
					Status: "True",
					LastHeartbeatTime: metav1.Time{
						Time: time.Now(),
					},
					LastTransitionTime: metav1.Time{
						Time: time.Now(),
					},
					Reason:  "KubeletHasDiskPressure",
					Message: "kubelet has disk pressure",
				},
				{
					Type:   "PIDPressure",
					Status: "False",
					LastHeartbeatTime: metav1.Time{
						Time: time.Now(),
					},
					LastTransitionTime: metav1.Time{
						Time: time.Now(),
					},
					Reason:  "KubeletHasSufficientPID",
					Message: "kubelet has sufficient PID available",
				},
				{
					Type:   "Ready",
					Status: "True",
					LastHeartbeatTime: metav1.Time{
						Time: time.Now(),
					},
					LastTransitionTime: metav1.Time{
						Time: time.Now(),
					},
					Reason:  "KubeletReady",
					Message: "kubelet is posting ready status",
				},
			},
			NodeInfo: v1.NodeSystemInfo{
				MachineID:               "ec2eb21af60b929ba89f44fb5d86508f",
				SystemUUID:              "EC2EB21A-F60B-929B-A89F-44FB5D86508F",
				BootID:                  "3b67d19f-cfa6-4925-a728-ce3f3e28991b",
				KernelVersion:           "4.14.97-90.72.amzn2.x86_64",
				OSImage:                 "Amazon Linux 2",
				ContainerRuntimeVersion: "docker://18.6.1",
				KubeletVersion:          "v1.11.5",
				KubeProxyVersion:        "v1.11.5",
				OperatingSystem:         "linux",
				Architecture:            "amd64",
			},
		},
	},
}

func setUpNodeClient() (*nodeClient, chan struct{}) {
	stopChan := make(chan struct{})

	client := &nodeClient{
		stopChan: stopChan,
		store:    NewObjStore(transformFuncNode),
		inited:   true, //make it true to avoid further initialization invocation.
	}
	return client, stopChan
}

func TestNodeClient(t *testing.T) {
	client, stopChan := setUpNodeClient()
	defer close(stopChan)

	client.store.Replace(nodeArray, "")

	expectedClusterNodeCount := 3
	expectedClusterFailedNodeCount := 1
	clusterNodeCount := client.ClusterNodeCount()
	clusterFailedNodeCount := client.ClusterFailedNodeCount()
	log.Printf("clusterNodeCount: %v, clusterFailedNodeCount: %v", clusterNodeCount, clusterFailedNodeCount)

	assert.Equal(t, clusterNodeCount, expectedClusterNodeCount)
	assert.Equal(t, clusterFailedNodeCount, expectedClusterFailedNodeCount)
}