package dataplane

import (
	"context"
	"fmt"

	"github.com/mitchellh/mapstructure"
	"github.com/samber/lo"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/log"

	clusteragent "github.com/tidbcloud/eks-provider/controllers/utils/cluster_agent"
	"github.com/tidbcloud/eks-provider/controllers/workflow/action"
	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	apiv1 "github.com/tidbcloud/infra-provider/api/tidbcloud/cluster-agent/v1"
	"github.com/tidbcloud/infra-provider/pkg/consts"
)

// restartPod restarts a tc pod by calling cluster-agent's API
type restartPod struct {
	cli client.Client
}

type restartPodInputs struct {
	Namespace   string `mapstructure:"namespace" json:"namespace" validate:"required"`
	ClusterName string `mapstructure:"clusterName" json:"clusterName" validate:"required"`
	// PodName for example 'db-tidb-0'
	PodName    string `mapstructure:"podName" json:"podName" validate:"required"`
	Force      bool   `mapstructure:"force" json:"force" validate:"omitempty"`
	TargetType int64  `mapstructure:"targetType" json:"targetType" validate:"omitempty"`
}

var (
	_ action.Action = &restartPod{}
)

func NewRestartPod(cli client.Client) action.Action {
	return &restartPod{cli: cli}
}

func (p *restartPod) Name() cloudv1.WorkflowActionName {
	return consts.WfActionRestartPodAction
}

func (p *restartPod) PreCheck(ctx context.Context, wf *cloudv1.Workflow, step cloudv1.WorkflowStep) error {
	inputs := &restartPodInputs{}
	if err := mapstructure.Decode(step.Inputs.Object, inputs); err != nil {
		return fmt.Errorf("decode inputs: %w", err)
	}
	if err := validate.Struct(inputs); err != nil {
		return fmt.Errorf("validate inputs: %w", err)
	}
	return nil
}

func (p *restartPod) Handle(ctx context.Context, wf *cloudv1.Workflow, step cloudv1.WorkflowStep) error {
	logger := log.FromContext(ctx)
	inputs := &restartPodInputs{}
	lo.Must0(mapstructure.Decode(step.Inputs.Object, inputs))

	cluster := &cloudv1.Cluster{}
	if err := p.cli.Get(ctx, client.ObjectKey{Namespace: inputs.Namespace, Name: inputs.ClusterName}, cluster); err != nil {
		if apierrors.IsNotFound(err) {
			return cloudv1.NewWFNonRetriableError(fmt.Errorf("cluster %s not found", inputs.ClusterName))
		}
		return fmt.Errorf("get cluster: %w", err)
	}
	cli, err := clusteragent.NewClient(cluster)
	if err != nil {
		return fmt.Errorf("create cluster-agent client: %w", err)
	}

	targetType := apiv1.RestartTargetType_POD
	if inputs.TargetType != 0 {
		targetType = apiv1.RestartTargetType(inputs.TargetType)
	}

	req := &apiv1.RestartReq{
		// the format of idempotency key is same as operationId
		OperationId: wf.Labels[cloudv1.IdempotencyKey],
		ClusterId:   inputs.ClusterName,
		TargetType:  targetType,
		TargetName:  inputs.PodName,
		Force:       inputs.Force,
	}
	resp, err := cli.Restart(ctx, req)
	if err != nil {
		if resp != nil {
			logger.Info("restart pod", "message", resp.Message)
		}
		return fmt.Errorf("restart pod: %w", err)
	}

	return nil
}
