package server

import (
	"context"
	"errors"

	apiv1 "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	"github.com/tidbcloud/cluster-service-ng/domain/app/workernodeusage"
	"github.com/tidbcloud/cluster-service-ng/pkg/dataplane"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/timestamppb"
	corev1 "k8s.io/api/core/v1"
)

func (s *serverImpl) GetSharedPoolVcpusUsage(ctx context.Context, sharedPoolId string) (*apiv1.GetSharedPoolVcpusUsageResponse, error) {
	if sharedPoolId == "" {
		return nil, status.Errorf(codes.InvalidArgument, "sharedPoolId is empty")
	}

	dataplaneName, err := s.sharedPoolRepo.LoadDataplaneName(ctx, sharedPoolId, false)
	if err != nil {
		if status.Code(err) == codes.NotFound {
			return nil, status.Errorf(codes.NotFound, "sharedPool %s not found", sharedPoolId)
		}
		return nil, status.Errorf(codes.Internal, "failed to load sharedPool: %s", err)
	}

	k8sClient, err := s.dataplaneManager.GetClient(dataplaneName)
	if err != nil {
		if errors.Is(err, dataplane.ErrDataplaneProviderNotReady) {
			return nil, status.Errorf(codes.FailedPrecondition, "dataplane %s is not ready, please try again later", dataplaneName)
		}
		return nil, status.Errorf(codes.Internal, "failed to get dataplane client: %s", err)
	}

	var nodeList corev1.NodeList
	if err := k8sClient.List(ctx, &nodeList); err != nil {
		return nil, status.Errorf(codes.Internal, "failed to list dataplane nodes: %s", err)
	}

	var podList corev1.PodList
	if err := k8sClient.List(ctx, &podList); err != nil {
		return nil, status.Errorf(codes.Internal, "failed to list dataplane pods: %s", err)
	}

	workerNodes := make(map[string]*corev1.Node)
	for i := range nodeList.Items {
		node := &nodeList.Items[i]
		if workernodeusage.IsWorkerNode(node) {
			workerNodes[node.Name] = node
		}
	}

	pods := make([]*corev1.Pod, 0, len(podList.Items))
	for i := range podList.Items {
		pod := &podList.Items[i]
		if !workernodeusage.IsNonDaemonSetPod(pod) {
			continue
		}
		pods = append(pods, pod)
	}

	snapshot := workernodeusage.BuildSnapshot(ctx, workerNodes, pods)
	summary := snapshot.SummaryForSharedPool(sharedPoolId)

	return &apiv1.GetSharedPoolVcpusUsageResponse{
		SharedPoolId: sharedPoolId,
		VcpusUsed:    summary.VCPUsUsed,
		CountedNodes: summary.CountedNodes,
		DroppedNodes: summary.DroppedNodes,
		DropReasons: &apiv1.SharedPoolVcpusUsageDropReasons{
			ConflictingOwnership: summary.DropReasons[workernodeusage.DropReasonConflictingOwnership],
			MissingIdentity:      summary.DropReasons[workernodeusage.DropReasonMissingIdentity],
			InvalidNodeSpec:      summary.DropReasons[workernodeusage.DropReasonInvalidNodeSpec],
		},
		SampleTime: timestamppb.Now(),
	}, nil
}
