package dataplane

import (
	"context"
	"fmt"

	"github.com/mitchellh/mapstructure"
	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"
	apicommon "github.com/tidbcloud/infra-provider/api/tidbcloud/common"
	"github.com/tidbcloud/infra-provider/pkg/consts"
)

var _ action.Action = &deleteASTS{}

// deleteASTS deletes all ASTS of a tidb cluster.
type deleteASTS struct {
	cli client.Client
}

type deleteASTSInputs struct {
	Cluster   string `mapstructure:"cluster" json:"cluster" validate:"required"`
	Namespace string `mapstructure:"namespace" json:"namespace" validate:"required"`
}

func NewDeleteASTS(cli client.Client) action.Action {
	return &deleteASTS{cli: cli}
}

func (a *deleteASTS) Name() cloudv1.WorkflowActionName {
	return consts.WfActionDeleteASTSAction
}

func (a *deleteASTS) PreCheck(ctx context.Context, wf *cloudv1.Workflow, step cloudv1.WorkflowStep) error {
	inputs := &deleteASTSInputs{}
	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 (a *deleteASTS) Handle(ctx context.Context, wf *cloudv1.Workflow, step cloudv1.WorkflowStep) error {
	inputs := &deleteASTSInputs{}
	_ = mapstructure.Decode(step.Inputs.Object, inputs)
	logger := log.FromContext(ctx).WithValues("action", a.Name(), "cluster", inputs.Cluster, "namespace", inputs.Namespace)

	cluster := &cloudv1.Cluster{}
	if err := a.cli.Get(ctx, client.ObjectKey{Namespace: inputs.Namespace, Name: inputs.Cluster}, cluster); err != nil {
		if apierrors.IsNotFound(err) {
			return cloudv1.NewWFNonRetriableError(fmt.Errorf("cluster %s/%s not found", inputs.Namespace, inputs.Cluster))
		}
		return fmt.Errorf("failed to get cluster %s/%s: %w", inputs.Namespace, inputs.Cluster, err)
	}

	cli, err := clusteragent.NewClient(cluster)
	if err != nil {
		return fmt.Errorf("failed to create cluster-agent client: %w", err)
	}

	resp, err := cli.DeleteASTSAndResumeTC(ctx, &apiv1.DeleteASTSAndResumeTCReq{
		ClusterId: inputs.Cluster,
		ComponentTypes: []apicommon.ComponentType{
			apicommon.ComponentType_PD,
			apicommon.ComponentType_TIDB,
			apicommon.ComponentType_TIKV,
			apicommon.ComponentType_TIFLASH,
			apicommon.ComponentType_TIPROXY,
		},
		OnlyDeleteAsts: true,
	})
	if err != nil {
		return fmt.Errorf("failed to delete ASTS: %w", err)
	}
	logger.Info("deleted all ASTS", "deletedAstsNames", resp.DeletedAstsNames)
	return nil
}
