package server

import (
	"context"
	"errors"
	"fmt"

	apiv1 "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	"github.com/tidbcloud/cluster-service-ng/domain/workflow"
	"github.com/tidbcloud/cluster-service-ng/domain/workflow/dag"
	"github.com/tidbcloud/cluster-service-ng/pkg/runtime/actions"
	"github.com/tidbcloud/cluster-service-ng/pkg/runtime/executorext"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/mysql/schema"
	"github.com/tidbcloud/cluster-service-ng/pkg/utils"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

type DeleteManualBackupRestoreInput struct {
	*apiv1.DeleteManualBackupRestoreRequest
}

func (s *serverImpl) DeleteManualBackupRestore(ctx context.Context, input *DeleteManualBackupRestoreInput) error {
	restore, err := s.manualBackupRestoreRepo.Load(ctx, input.RestoreId, true)
	if err != nil {
		return fmt.Errorf("failed to load manual backup restore: %w", err)
	}
	if restore.DeletedAt.Valid {
		tilog.Info(ctx, "manual backup restore is already deleted, no need to create deletion dag", zap.String("restoreId", input.RestoreId))
		return nil
	}
	if err := s.guardSharedPoolMutationByID(ctx, restore.TargetSharedPoolID, sharedPoolMutationBlockedWhilePaused); err != nil {
		return err
	}

	isCrossSharedPool := restore.SourceSharedPoolID != restore.TargetSharedPoolID

	filters := []executorext.DagInsFilter{
		{
			ArgKey:    workflow.ManualRestoreIdKey,
			ArgValues: []string{input.RestoreId},
		},
	}

	creationPlan := dag.PlanCreateManualBackupRestore
	if isCrossSharedPool {
		creationPlan = dag.PlanCreateManualBackupXRestore
	}

	// Cancel create dag if exist
	creationDagIns, err := s.dagInstanceFinder.GetRunningDagInstance(ctx, creationPlan, filters...)
	if err != nil && !errors.Is(err, executorext.DagInsNotFoundErr) {
		return err
	}
	if creationDagIns != nil {
		err = s.transactioner.Transaction(ctx, func(ctx context.Context) error {
			ierr := s.dagExecutor.Abort(ctx, creationDagIns.ExecTask.ExecId)
			if ierr != nil {
				tilog.Error(ctx, fmt.Sprintf("abort existed %s dag failed", creationPlan),
					zap.Error(ierr),
					zap.String("restoreId", input.RestoreId),
					zap.Any("dag ins", utils.JSON(creationDagIns)))
				return fmt.Errorf("abort existed %s dag failed: %w", creationPlan, ierr)
			}
			ierr = s.manualBackupRestoreRepo.SetStatus(ctx, input.RestoreId, schema.ManualBackupRestoreStatusCancelled)
			if ierr != nil {
				tilog.Error(ctx, "mark db manual backup restore record as cancelled failed",
					zap.Error(ierr),
					zap.String("restoreId", input.RestoreId))
				return fmt.Errorf("mark db manual backup restore record as cancelled: %w", ierr)
			}
			return nil
		})
		if err != nil {
			return fmt.Errorf("abort existed %s dag failed: %s", creationPlan, err.Error())
		}

		tilog.Info(ctx, fmt.Sprintf("abort existed %s dag success", creationPlan),
			zap.String("restoreId", input.RestoreId),
			zap.Any("dag ins", utils.JSON(creationDagIns)))
	}

	deletionPlan := dag.PlanDeleteManualBackupRestore
	if isCrossSharedPool {
		deletionPlan = dag.PlanDeleteManualBackupXRestore
	}

	// query existed delete dag ins
	deletionDagIns, err := s.dagInstanceFinder.GetRunningDagInstance(ctx, deletionPlan, filters...)
	if err != nil && !errors.Is(err, executorext.DagInsNotFoundErr) {
		tilog.Error(ctx, fmt.Sprintf("failed to get running %s dag", deletionPlan),
			zap.Error(err),
			zap.String("restoreId", input.RestoreId))
		return err
	}
	if deletionDagIns != nil {
		tilog.Info(ctx, fmt.Sprintf("there is a running %s dag, no need to create new one", deletionPlan),
			zap.String("restoreId", input.RestoreId),
			zap.Any("dag ins", utils.JSON(deletionDagIns)))
		return nil
	}

	// create deletion dag
	tilog.Info(ctx, fmt.Sprintf("create a %s dag ins ...", deletionPlan),
		zap.Any("filters", filters))
	args := actions.ActionArgs{
		utils.TraceIdKey:               utils.GetTraceId(ctx),
		workflow.ManualRestoreIdKey:    actions.ActionArgString(input.RestoreId),
		workflow.SharedPoolIdKey:       actions.ActionArgString(restore.TargetSharedPoolID),
		workflow.SourceSharedPoolIdKey: actions.ActionArgString(restore.SourceSharedPoolID),
	}

	dagExecId := fmt.Sprintf("delete-manual-backup-restore-%s", input.RestoreId)
	if isCrossSharedPool {
		dagExecId = fmt.Sprintf("delete-manual-backup-x-restore-%s", input.RestoreId)
	}

	err = s.dagExecutor.Submit(ctx, deletionPlan, args, dagExecId)
	if err != nil {
		return fmt.Errorf("failed to submit %s dag: %s", deletionPlan, err.Error())
	}
	tilog.Info(ctx, fmt.Sprintf("submit %s dag success", deletionPlan),
		zap.String("restoreId", input.RestoreId))
	return nil
}
