package backend

import (
	"context"
	"strings"

	"github.com/tidbcloud/chaos-svc/idl/pbgen/proto/chaos"
	"github.com/tidbcloud/chaos-svc/pkg/domain/facade"
	"github.com/tidbcloud/chaos-svc/pkg/domain/repo"
	backendSvc "github.com/tidbcloud/chaos-svc/pkg/domain/service/backend"
	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/pkgv2/db"
	"github.com/tidbcloud/pkgv2/log"
)

type DisableBackendSvc struct {
	trans             db.TransProvider
	disableBackendSvc backendSvc.DisableBackendSvc
	infraFacade       facade.Infra
	workflowRepo      repo.WorkflowRepo
}

func NewDisableBackendSvc(
	trans db.TransProvider,
	disableBackendSvc backendSvc.DisableBackendSvc,
	infraFacade facade.Infra,
	workflowRepo repo.WorkflowRepo,
) *DisableBackendSvc {
	return &DisableBackendSvc{
		trans:             trans,
		disableBackendSvc: disableBackendSvc,
		infraFacade:       infraFacade,
		workflowRepo:      workflowRepo,
	}
}

func (svc *DisableBackendSvc) Execute(ctx context.Context, req *chaos.DisableChaosBackendRequest) bizerr.BizError {
	logger := log.WithContextS(ctx).With("clusterID", req.ClusterId).With("projectID", req.ProjectId)
	logger.Info("disable chaos backend")

	tidbCluster, err := svc.infraFacade.GetTiDBCluster(ctx, req.ProjectId, req.ClusterId)
	if err != nil {
		return err
	}
	if err := validateClusterType(tidbCluster); err != nil {
		return err
	}

	if err := svc.trans.Transaction(ctx, func(ctx context.Context) error {
		if err := svc.validateNoRunningOrDraftExperiment(ctx, tidbCluster); err != nil {
			return err.Chain("check")
		}
		params, err := buildDisableBackendParams(ctx, req.ProjectId, tidbCluster.NetworkName, req.UserId)
		if err != nil {
			return err
		}

		if err := svc.disableBackendSvc.Execute(ctx, params); err != nil {
			return err
		}
		return nil
	}); err != nil {
		return bizerr.WrapError(err, "transaction")
	}
	return nil
}

func validateClusterType(tidbCluster *facade.TiDBCluster) bizerr.BizError {
	if strings.Contains(tidbCluster.ProviderType, "free-tier") {
		return bizerr.New(bizerr.ErrCodeGeneralBusiness, "free-tier cluster is not supported")
	}
	if tidbCluster.ProviderType != "eks" {
		return bizerr.New(bizerr.ErrCodeGeneralBusiness, "only support new-architecture dedicated clusters deployed on eks")
	}
	return nil
}

// check if there is no running or draft experiments in this project/region
func (svc *DisableBackendSvc) validateNoRunningOrDraftExperiment(ctx context.Context, tidbCluster *facade.TiDBCluster) bizerr.BizError {
	clusters, err := svc.infraFacade.ListTiDBClusters(ctx, tidbCluster.ProjectID, &facade.TiDBClusterFilter{
		Region: tidbCluster.Region,
	})
	if err != nil {
		return err
	}
	clusterIDs := make([]uint64, 0, len(clusters))
	for _, cluster := range clusters {
		clusterIDs = append(clusterIDs, cluster.ClusterID)
	}

	count, err := svc.workflowRepo.Count(ctx, repo.WorkflowFilter{
		ClusterIDs: clusterIDs,
		Statuses: []chaos.ExperimentStatus{
			chaos.ExperimentStatus_EXPERIMENT_STATUS_RUNNING,
			chaos.ExperimentStatus_EXPERIMENT_STATUS_DRAFT,
		},
	})
	if err != nil {
		return err.Chain("check no running experiments")
	}
	if count > 0 {
		return bizerr.New(bizerr.ErrCodeGeneralBusiness, "there are running experiments in the project")
	}
	return nil
}

func buildDisableBackendParams(ctx context.Context, projectID uint64, networkName string, userID uint64) (*backendSvc.DisableBackendParam, bizerr.BizError) {
	return &backendSvc.DisableBackendParam{
		ProjectID:   projectID,
		NetworkName: networkName,
		UserID:      userID,
	}, nil
}
