package br

import (
	"fmt"
	"time"

	v1alpha1br "github.com/pingcap/tidb-operator/api/v2/br/v1alpha1"
	"github.com/tidbcloud/cluster-service-ng/domain/workflow"
	"github.com/tidbcloud/cluster-service-ng/pkg/dataplane"
	"github.com/tidbcloud/cluster-service-ng/pkg/k8s"
	"github.com/tidbcloud/cluster-service-ng/pkg/runtime/actions"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/repo"
	"github.com/tidbcloud/cluster-service-ng/pkg/tioperator"
	"github.com/tidbcloud/cluster-service-ng/pkg/utils"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
)

const DeleteTiBRAutoBackupActionName = "delete-tibr-auto-backup"

type deleteTiBRAutoBackupAction struct {
	dataplaneManager dataplane.Manager
	sharedPoolRepo   repo.SharedPoolRepo
}

func NewDeleteTiBRAutoBackupAction(
	sharedPoolRepo repo.SharedPoolRepo,
	dataplaneManager dataplane.Manager) actions.Action {
	return actions.CreateActionFromAsync(&deleteTiBRAutoBackupAction{
		sharedPoolRepo:   sharedPoolRepo,
		dataplaneManager: dataplaneManager,
	}, 1*time.Minute, 30*time.Minute)
}

func (a *deleteTiBRAutoBackupAction) Name() string {
	return DeleteTiBRAutoBackupActionName
}

func (a *deleteTiBRAutoBackupAction) Start(args actions.ActionArgs) error {
	ctx := utils.GenerateContext(args)
	tilog.Info(ctx, "delete tibr auto backup...")

	sharedPoolId, err := args.GetString(workflow.SharedPoolIdKey)
	if err != nil {
		return fmt.Errorf("failed to get sharedPool id: %w", err)
	}
	sharedPool, err := a.sharedPoolRepo.Load(ctx, sharedPoolId, true)
	if err != nil {
		return fmt.Errorf("failed to load sharedPool: %w", err)
	}

	remoteCli, err := a.dataplaneManager.GetClient(sharedPool.GetDataplaneName())
	if err != nil {
		tilog.Error(ctx, "failed to get remote client for shared pool", zap.String("dataplaneName", sharedPool.GetDataplaneName()), zap.Error(err))
		return fmt.Errorf("failed to get remote client for shared pool: %w", err)
	}

	namespace := sharedPool.GetDataPlaneNamespace()

	// delete tibr
	err = k8s.DeleteObject(ctx, remoteCli, &v1alpha1br.TiBR{
		ObjectMeta: metav1.ObjectMeta{
			Name:      tioperator.DefaultTiBRName,
			Namespace: namespace,
		},
	})
	if err != nil {
		tilog.Error(ctx, "failed to delete tibr", zap.String("tibrName", tioperator.DefaultTiBRName), zap.String("namespace", namespace), zap.Error(err))
		return fmt.Errorf("failed to delete tibr %s: %w", tioperator.DefaultTiBRName, err)
	}

	tilog.Info(ctx, "tibr deleted", zap.String("tibrName", tioperator.DefaultTiBRName), zap.String("namespace", namespace))

	return nil
}

func (a *deleteTiBRAutoBackupAction) CheckFinish(args actions.ActionArgs) (bool, error) {
	ctx := utils.GenerateContext(args)
	tilog.Info(ctx, "check delete tibr restore finished...")

	sharedPoolId, err := args.GetString(workflow.SharedPoolIdKey)
	if err != nil {
		return false, fmt.Errorf("failed to get sharedPool id: %w", err)
	}
	sharedPool, err := a.sharedPoolRepo.Load(ctx, sharedPoolId, true)
	if err != nil {
		return false, fmt.Errorf("failed to load sharedPool: %w", err)
	}

	remoteCli, err := a.dataplaneManager.GetClient(sharedPool.GetDataplaneName())
	if err != nil {
		tilog.Error(ctx, "failed to get remote client for shared pool", zap.String("dataplaneName", sharedPool.GetDataplaneName()), zap.Error(err))
		return false, fmt.Errorf("failed to get remote client for shared pool: %w", err)
	}

	namespace := sharedPool.GetDataPlaneNamespace()

	// check tibr deleted
	tibr := &v1alpha1br.TiBR{}
	err = remoteCli.Get(ctx, types.NamespacedName{
		Name:      tioperator.DefaultTiBRName,
		Namespace: namespace,
	}, tibr)
	if err != nil && !apierrors.IsNotFound(err) {
		return false, fmt.Errorf("failed to get tibr %s: %w", tioperator.DefaultTiBRName, err)
	}

	if err != nil && apierrors.IsNotFound(err) {
		tilog.Info(ctx, "tibr deletion finished", zap.String("tibrName", tioperator.DefaultTiBRName), zap.String("namespace", namespace))
		return true, nil
	}

	tilog.Info(ctx, "tibr deletion not finished...", zap.String("tibrName", tioperator.DefaultTiBRName), zap.String("namespace", namespace))
	return false, nil
}
