package nodes

import (
	"fmt"

	corev1 "k8s.io/api/core/v1"

	"github.com/karmada-io/karmada/pkg/util"
)

// NodeInfo is the wrapper of a node and its resource.
type NodeInfo struct {
	Node *corev1.Node

	AllocatableResource *util.Resource
	IdleResource        *util.Resource
}

// NewNodeInfo returns an instance of NodeInfo. The initial IdleResource equals with AllocatableResource.
func NewNodeInfo(node *corev1.Node) *NodeInfo {
	allocatableResource := util.NewResource(node.Status.Allocatable)
	return &NodeInfo{
		Node:                node,
		AllocatableResource: allocatableResource,
		IdleResource:        allocatableResource,
	}
}

// AssignedPodRequest counts the effective request resource of pods for the node.
// IdleResource will be subtracted from the all pod request resources when this function is called.
func (ni *NodeInfo) AssignedPodRequest(pods []*corev1.Pod) error {
	occupiedResource := util.EmptyResource()
	for _, pod := range pods {
		occupiedResource.AddPodRequest(&pod.Spec)
	}
	// The pod request does not contain a pod resource, so we add it manually.
	occupiedResource.AddResourcePods(int64(len(pods)))

	// The occupied resource must be less than or equal with the node idle resource.
	if !occupiedResource.LessEqual(ni.IdleResource) {
		return fmt.Errorf("node %s does have enough idle resource to accommodate %d pods", ni.Node.Name, len(pods))
	}

	// subtract
	if err := ni.IdleResource.Sub(occupiedResource.ResourceList()); err != nil {
		return err
	}
	return nil
}

// MaxReplicaDivided returns how many replicas that the node can produce.
func (ni *NodeInfo) MaxReplicaDivided(rl corev1.ResourceList) int64 {
	return ni.IdleResource.MaxDivided(rl)
}
