package br

import (
	"fmt"

	"github.com/tidbcloud/cluster-service-ng/pkg/clustergateway"
	"github.com/tidbcloud/cluster-service-ng/pkg/dataplane"
	"github.com/tidbcloud/cluster-service-ng/pkg/runtime/actions"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/mysql/schema"
	"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"
)

const ExecuteKernelManualBackupActionName = "execute-kernel-manual-backup"

type executeKernelManualBackupAction struct {
	manualBackupRepo  repo.ManualBackupRepo
	sharedPoolRepo    repo.SharedPoolRepo
	clustergatewayCli clustergateway.Client
	dataplaneManager  dataplane.Manager
}

func NewExecuteKernelManualBackupAction(
	manualBackupRepo repo.ManualBackupRepo,
	sharedPoolRepo repo.SharedPoolRepo,
	clustergatewayCli clustergateway.Client,
	dataplaneManager dataplane.Manager,
) actions.Action {
	return &executeKernelManualBackupAction{
		manualBackupRepo:  manualBackupRepo,
		sharedPoolRepo:    sharedPoolRepo,
		clustergatewayCli: clustergatewayCli,
		dataplaneManager:  dataplaneManager,
	}
}

func (a *executeKernelManualBackupAction) Name() string {
	return ExecuteKernelManualBackupActionName
}

func (a *executeKernelManualBackupAction) Invoke(args actions.ActionArgs) error {
	ctx := utils.GenerateContext(args)
	tilog.Info(ctx, fmt.Sprintf("start %s action...", a.Name()))

	sharedPool, _, manualBackup, err := getManualBackupActionContextInfos(ctx, args, a.manualBackupRepo, a.sharedPoolRepo, a.dataplaneManager)
	if err != nil {
		if skip, skipErr := skipManualBackupActionForMissingSharedPool(ctx, a.manualBackupRepo, manualBackup, err, a.Name()); skip {
			return skipErr
		}
		return fmt.Errorf("failed to get manual backup action context infos: %w", err)
	}

	if err := AddActionForManualBackup(ctx, a.manualBackupRepo, manualBackup, schema.ManualBackupActionExecBackup); err != nil {
		return fmt.Errorf("failed to add action to manual backup: %w", err)
	}

	// if not empty, already done
	if manualBackup.KernelBackupName != "" {
		tilog.Info(ctx, "already have kernel backup name, no need to create backup in kernel", zap.String("backupId", manualBackup.BackupID))
		return nil
	}

	tibrName := tioperator.TiBRCRNameOfManualBackup(manualBackup.ClusterID, manualBackup.BackupID)
	resp, err := a.clustergatewayCli.CreateLightweightBackup(ctx, sharedPool, tibrName)
	if err != nil {
		tilog.Error(ctx, "failed to create lightweight backup in kernel", zap.Error(err))
		return fmt.Errorf("failed to create lightweight backup in kernel: %w", err)
	}

	if resp == nil || resp.BackupName == "" {
		tilog.Error(ctx, "failed to create lightweight backup in kernel, response is nil or backup name is empty", zap.Any("response", utils.JSON(resp)))
		return fmt.Errorf("failed to create lightweight backup in kernel, response is nil or backup name is empty")
	}

	manualBackup.KernelBackupName = resp.BackupName
	if err := a.manualBackupRepo.Save(ctx, manualBackup); err != nil {
		tilog.Error(ctx, "failed to save manual backup with kernel backup name", zap.Error(err))
		return fmt.Errorf("failed to save manual backup with kernel backup name: %w", err)
	}

	tilog.Info(ctx, "successfully create lightweight backup in kernel", zap.Any("manualBackup", manualBackup))
	return nil
}
