package server

import (
	"context"
	"fmt"

	apiv1 "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	"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/utils"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

func (s *serverImpl) SetBrComponentConfigOrVersion(
	ctx context.Context,
	sharedPoolId string,
	targetComponents []*entity.TargetComponent,
	scope entity.SetVersionConfigScope,
) error {
	if err := s.guardSharedPoolMutationByID(ctx, sharedPoolId, sharedPoolMutationBlockedWhilePaused); err != nil {
		return err
	}
	tibrgcApplied := false

	for _, component := range targetComponents {
		switch component.GetComponentType() {
		case apiv1.ComponentType_TIBR_AUTOBACKUP:
			err := s.applyTiBRAutoBackup(ctx, sharedPoolId)
			if err != nil {
				return status.Errorf(codes.Internal, "failed to apply tibr auto backup: %s", err.Error())
			}
		case apiv1.ComponentType_TIBRGC_T2_DFSGC, apiv1.ComponentType_TIBRGC_T3_ARCHIVE:
			if tibrgcApplied {
				continue
			}

			err := s.applyTiBRGC(ctx, sharedPoolId)
			if err != nil {
				return status.Errorf(codes.Internal, "failed to apply tibrgc: %s", err.Error())
			}
			tibrgcApplied = true
		case apiv1.ComponentType_TIBR_RESTORE, apiv1.ComponentType_CLASSIC_RESTORE:
			tilog.Info(ctx, "skip set br component config or version since it's one time job", zap.String("componentType", component.GetComponentType().String()))
			continue
		default:
			return status.Errorf(codes.InvalidArgument, "invalid component type: %s", component.GetComponentType())
		}
	}
	return nil
}

func (s *serverImpl) applyTiBRAutoBackup(ctx context.Context, sharedPoolId string) error {
	dagIns, err := s.getRunningDagIns(ctx, []string{dag.PlanUpgradeTiBRAutoBackup}, []string{sharedPoolId}, nil, nil, nil)
	if err != nil {
		return fmt.Errorf("failed to get running upgrade tibr auto backup dag instance, shared pool id: %s, err: %w", sharedPoolId, err)
	}
	if len(dagIns) > 0 {
		return status.Errorf(codes.AlreadyExists, "exist tibr auto backup upgrading dag instance not finished, please try again later")
	}

	err = s.dagExecutor.Submit(ctx,
		dag.PlanUpgradeTiBRAutoBackup,
		actions.ActionArgs{workflow.SharedPoolIdKey: actions.ActionArgString(sharedPoolId)},
		utils.GeneratedDagExecId(ctx, fmt.Sprintf("upgrade-tibr-auto-backup-%s", sharedPoolId)))
	if err != nil {
		return status.Errorf(codes.Internal, "failed to submit upgrade tibr auto backup dag, shared pool id: %s, err: %s", sharedPoolId, err.Error())
	}

	return nil
}

func (s *serverImpl) applyTiBRGC(ctx context.Context, sharedPoolId string) error {
	dagIns, err := s.getRunningDagIns(ctx, []string{dag.PlanUpgradeTiBRGC}, []string{sharedPoolId}, nil, nil, nil)
	if err != nil {
		return status.Errorf(codes.Internal, "failed to get running upgrade tibrgc dag instance, shared pool id: %s, err: %s", sharedPoolId, err.Error())
	}
	if len(dagIns) > 0 {
		return status.Errorf(codes.AlreadyExists, "exist tibrgc upgrading dag instance not finished, please try again later")
	}

	err = s.dagExecutor.Submit(ctx,
		dag.PlanUpgradeTiBRGC,
		actions.ActionArgs{workflow.SharedPoolIdKey: actions.ActionArgString(sharedPoolId)},
		utils.GeneratedDagExecId(ctx, fmt.Sprintf("upgrade-tibrgc-%s", sharedPoolId)))
	if err != nil {
		return status.Errorf(codes.Internal, "failed to submit upgrade tibrgc dag, shared pool id: %s, err: %s", sharedPoolId, err.Error())
	}
	return nil
}
