package service

import (
	"bytes"
	"context"
	"fmt"

	apiv1 "github.com/tidbcloud/cluster-service-ng/api/tidbcloud/cluster-agent/v1"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/emptypb"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/remotecommand"
)

const tiProxyContainerName = "tiproxy"

func (s *AgentService) MarkTiProxyPodUnhealthyLegacy(ctx context.Context, req *apiv1.MarkTiProxyPodUnhealthyLegacyReq) (*emptypb.Empty, error) {
	logger := tilog.WithContextS(ctx).With("request", req)

	pod, err := s.getTiProxyPod(ctx, req.ClusterId, req.PodName)
	if err != nil {
		if apierrors.IsNotFound(err) {
			return nil, status.Errorf(codes.NotFound, "tiproxy pod %s not found in cluster %s", req.GetPodName(), req.GetClusterId())
		}
		return nil, err
	}

	if err := sendSIGTERMToTiProxyPod(ctx, pod); err != nil {
		return nil, status.Errorf(codes.Internal, "mark tiproxy pod unhealthy legacy %s/%s: %s", pod.Namespace, pod.Name, err)
	}

	logger.Infof("marked tiproxy pod unhealthy by legacy signal")
	return &emptypb.Empty{}, nil
}

func sendSIGTERMToTiProxyPod(ctx context.Context, pod *corev1.Pod) error {
	cfg, err := rest.InClusterConfig()
	if err != nil {
		return err
	}
	cfg.APIPath = "/api"
	cfg.GroupVersion = &corev1.SchemeGroupVersion
	cfg.NegotiatedSerializer = scheme.Codecs.WithoutConversion()

	restClient, err := rest.RESTClientFor(cfg)
	if err != nil {
		return err
	}

	req := restClient.Post().
		Resource("pods").
		Namespace(pod.Namespace).
		Name(pod.Name).
		SubResource("exec").
		VersionedParams(&corev1.PodExecOptions{
			Container: tiProxyContainerName,
			Command:   []string{"/bin/sh", "-c", "kill -TERM 1"},
			Stderr:    true,
		}, scheme.ParameterCodec)

	exec, err := remotecommand.NewSPDYExecutor(cfg, "POST", req.URL())
	if err != nil {
		return err
	}

	var stderr bytes.Buffer
	if err := exec.StreamWithContext(ctx, remotecommand.StreamOptions{Stderr: &stderr}); err != nil {
		if stderr.Len() > 0 {
			return fmt.Errorf("%w: %s", err, stderr.String())
		}
		return err
	}
	return nil
}
