// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

package orchestrator

import (
	"strings"

	"github.com/patrickmn/go-cache"

	"github.com/DataDog/datadog-agent/pkg/util/log"
)

// NodeType represents a kind of resource used by a container orchestrator.
type NodeType int

// CheckName is the cluster check name of the orchestrator check
const CheckName = "orchestrator"

// ExtraLogContext is used to add check name into log context
var ExtraLogContext = []interface{}{"check", CheckName}

// NoExpiration maps to go-cache corresponding value
const NoExpiration = cache.NoExpiration

// The values in these enums should match the values defined in the agent payload schema, defined here:
// https://github.com/DataDog/agent-payload/blob/master/proto/process/agent.proto (within enum OrchestratorResource)
// we do not utilize iota as these types are used in external systems, not just within the agent instance.
const (
	// K8sUnsetType represents a Kubernetes unset type
	K8sUnsetType NodeType = 0
	// K8sPod represents a Kubernetes Pod
	K8sPod = 1
	// K8sReplicaSet represents a Kubernetes ReplicaSet
	K8sReplicaSet = 2
	// K8sService represents a Kubernetes Service
	K8sService = 3
	// K8sNode represents a Kubernetes Node
	K8sNode = 4
	// K8sCluster represents a Kubernetes Cluster
	K8sCluster = 5
	// K8sJob represents a Kubernetes Job
	K8sJob = 6
	// K8sCronJob represents a Kubernetes CronJob
	K8sCronJob = 7
	// K8sDaemonSet represents a Kubernetes DaemonSet
	K8sDaemonSet = 8
	// K8sStatefulSet represents a Kubernetes StatefulSet
	K8sStatefulSet = 9
	// K8sPersistentVolume represents a Kubernetes PersistentVolume
	K8sPersistentVolume = 10
	// K8sPersistentVolumeClaim represents a Kubernetes PersistentVolumeClaim
	K8sPersistentVolumeClaim = 11
	// K8sRole represents a Kubernetes Role
	K8sRole = 12
	// K8sRoleBinding represents a Kubernetes RoleBinding
	K8sRoleBinding = 13
	// K8sClusterRole represents a Kubernetes ClusterRole
	K8sClusterRole = 14
	// K8sClusterRoleBinding represents a Kubernetes ClusterRoleBinding
	K8sClusterRoleBinding = 15
	// K8sServiceAccount represents a Kubernetes ServiceAccount
	K8sServiceAccount = 16
	// K8sIngress represents a Kubernetes Ingress
	K8sIngress = 17
	// K8sDeployment represents a Kubernetes Deployment
	K8sDeployment = 18
	// K8sNamespace represents a Kubernetes Namespace
	K8sNamespace = 19
	// K8sCRD represents a Kubernetes CRD
	K8sCRD = 20
	// K8sCR represents a Kubernetes CR
	K8sCR = 21
	// K8sVerticalPodAutoscaler represents a Kubernetes VerticalPod Autoscaler
	K8sVerticalPodAutoscaler = 22
	// K8sHorizontalPodAutoscaler represents a Kubernetes Horizontal Pod Autoscaler
	K8sHorizontalPodAutoscaler = 23
	// K8sNetworkPolicy represents a Kubernetes NetworkPolicy
	K8sNetworkPolicy = 24
	// K8sLimitRange represents a Kubernetes LimitRange
	K8sLimitRange = 25
	// K8sStorageClass represents a Kubernetes StorageClass
	K8sStorageClass = 26
	// K8sPodDisruptionBudget represents a Kubernetes PodDisruptionBudget
	K8sPodDisruptionBudget = 27
	// K8sEndpointSlice represents a Kubernetes EndpointSlice
	K8sEndpointSlice = 28
	// K8sKubeletConfig represents a Kubernetes KubeletConfiguration
	K8sKubeletConfig = 29
	// ECSTask represents an ECS Task
	ECSTask = 150
)

// NodeTypes returns the current existing NodesTypes as a slice to iterate over.
func NodeTypes() []NodeType {
	return []NodeType{
		ECSTask,
		K8sCR,
		K8sCRD,
		K8sCluster,
		K8sClusterRole,
		K8sClusterRoleBinding,
		K8sCronJob,
		K8sDaemonSet,
		K8sDeployment,
		K8sEndpointSlice,
		K8sKubeletConfig,
		K8sHorizontalPodAutoscaler,
		K8sIngress,
		K8sJob,
		K8sLimitRange,
		K8sNamespace,
		K8sNetworkPolicy,
		K8sNode,
		K8sPersistentVolume,
		K8sPersistentVolumeClaim,
		K8sPod,
		K8sPodDisruptionBudget,
		K8sReplicaSet,
		K8sRole,
		K8sRoleBinding,
		K8sService,
		K8sServiceAccount,
		K8sStatefulSet,
		K8sStorageClass,
		K8sVerticalPodAutoscaler,
	}
}

func (n NodeType) String() string {
	switch n {
	case K8sCluster:
		return "Cluster"
	case K8sCronJob:
		return "CronJob"
	case K8sDeployment:
		return "Deployment"
	case K8sDaemonSet:
		return "DaemonSet"
	case K8sJob:
		return "Job"
	case K8sNode:
		return "Node"
	case K8sPod:
		return "Pod"
	case K8sReplicaSet:
		return "ReplicaSet"
	case K8sService:
		return "Service"
	case K8sStatefulSet:
		return "StatefulSet"
	case K8sPersistentVolume:
		return "PersistentVolume"
	case K8sPersistentVolumeClaim:
		return "PersistentVolumeClaim"
	case K8sRole:
		return "Role"
	case K8sRoleBinding:
		return "RoleBinding"
	case K8sClusterRole:
		return "ClusterRole"
	case K8sClusterRoleBinding:
		return "ClusterRoleBinding"
	case K8sServiceAccount:
		return "ServiceAccount"
	case K8sIngress:
		return "Ingress"
	case K8sNamespace:
		return "Namespace"
	case K8sCRD:
		return "CustomResourceDefinition"
	case K8sCR:
		return "CustomResource"
	case K8sVerticalPodAutoscaler:
		return "VerticalPodAutoscaler"
	case K8sHorizontalPodAutoscaler:
		return "HorizontalPodAutoscaler"
	case K8sNetworkPolicy:
		return "NetworkPolicy"
	case K8sLimitRange:
		return "LimitRange"
	case K8sStorageClass:
		return "StorageClass"
	case K8sUnsetType:
		return "UnsetType"
	case ECSTask:
		return "ECSTask"
	case K8sPodDisruptionBudget:
		return "PodDisruptionBudget"
	case K8sEndpointSlice:
		return "EndpointSlice"
	case K8sKubeletConfig:
		return "KubeletConfiguration"
	default:
		_ = log.Errorf("Trying to convert unknown NodeType iota: %d", n)
		return "Unknown"
	}
}

// Orchestrator returns the orchestrator name for a node type.
func (n NodeType) Orchestrator() string {
	switch n {
	case K8sCR,
		K8sCRD,
		K8sCluster,
		K8sClusterRole,
		K8sClusterRoleBinding,
		K8sCronJob,
		K8sDaemonSet,
		K8sDeployment,
		K8sEndpointSlice,
		K8sKubeletConfig,
		K8sHorizontalPodAutoscaler,
		K8sIngress,
		K8sJob,
		K8sLimitRange,
		K8sNamespace,
		K8sNetworkPolicy,
		K8sNode,
		K8sPersistentVolume,
		K8sPersistentVolumeClaim,
		K8sPod,
		K8sPodDisruptionBudget,
		K8sReplicaSet,
		K8sRole,
		K8sRoleBinding,
		K8sService,
		K8sServiceAccount,
		K8sStatefulSet,
		K8sStorageClass,
		K8sUnsetType,
		K8sVerticalPodAutoscaler:
		return "k8s"
	case ECSTask:
		return "ecs"
	default:
		log.Errorf("Unknown NodeType %v", n)
		return ""
	}
}

// TelemetryTags return tags used for telemetry.
func (n NodeType) TelemetryTags() []string {
	if n.String() == "" {
		log.Errorf("Unknown NodeType %v", n)
		return []string{"unknown", "unknown"}
	}
	tags := getTelemetryTags(n)
	return tags
}

func getTelemetryTags(n NodeType) []string {
	return []string{
		n.Orchestrator(),
		strings.ToLower(n.String()),
	}
}

// SetCacheStats sets the cache stats for each resource
func SetCacheStats(resourceListLen int, resourceMsgLen int, nodeType NodeType, ca *cache.Cache) {
	stats := CheckStats{
		CacheHits: resourceListLen - resourceMsgLen,
		CacheMiss: resourceMsgLen,
		NodeType:  nodeType,
	}
	ca.Set(BuildStatsKey(nodeType), stats, NoExpiration)
}
