package action

import (
	"fmt"
	"strconv"

	csngapiv1beat "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/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"
	templateversion "github.com/tidbcloud/cluster-service-ng/pkg/template/version"
	"github.com/tidbcloud/cluster-service-ng/pkg/utils"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

type ApplySharedPoolAction struct {
	dataplaneManager dataplane.Manager

	sharedPoolRepo     repo.SharedPoolRepo
	templateVersionSvc *templateversion.Service
}

const ApplyClusterActionName = "apply-cluster"

func NewApplySharedPoolAction(
	sharedPoolRepo repo.SharedPoolRepo,
	dataplaneManager dataplane.Manager,
	templateVersionSvc *templateversion.Service,
) actions.Action {
	return &ApplySharedPoolAction{
		sharedPoolRepo:     sharedPoolRepo,
		dataplaneManager:   dataplaneManager,
		templateVersionSvc: templateVersionSvc,
	}
}

func (a *ApplySharedPoolAction) Name() string {
	return ApplyClusterActionName
}

// Invoke creates tidb cluster cr in dataplane
func (a *ApplySharedPoolAction) Invoke(args actions.ActionArgs) error {
	// generate context
	ctx := utils.GenerateContext(args)
	tilog.Info(ctx, "create cluster action invoked")

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

	// load sharedPool
	sharedPool, err := a.sharedPoolRepo.Load(ctx, sharedPoolId, false)
	if err != nil {
		return fmt.Errorf("failed to load sharedPool: %w", err)
	}
	if err := entity.ValidatePauseResumeCombination(sharedPool); err != nil {
		return fmt.Errorf("invalid sharedPool pause/resume state: %w", err)
	}

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

	// create namespace
	err = k8s.EnsureNamespace(ctx, remoteCli, sharedPool.GetDataPlaneNamespace(), "")
	if err != nil {
		return fmt.Errorf("failed to ensure dataplane namespace %s: %w", sharedPool.GetDataPlaneNamespace(), err)
	}

	if err := k8s.EnsureSharedPoolLimitRange(ctx, remoteCli, sharedPool.GetDataPlaneNamespace(), sharedPool.GetVcpusLimit()); err != nil {
		return fmt.Errorf("failed to ensure limit range for namespace %s: %w", sharedPool.GetDataPlaneNamespace(), err)
	}

	err = k8s.EnsureResourceQuota(ctx, remoteCli, sharedPool.GetDataPlaneNamespace(), sharedPool.GetVcpusLimit())
	if err != nil {
		return fmt.Errorf("failed to ensure resource quota for namespace %s: %w", sharedPool.GetDataPlaneNamespace(), err)
	}

	templArgs := map[string]string{
		"ClusterId":      sharedPool.GetId(),
		"SharedPoolId":   sharedPool.GetId(),
		"BootStrapSQL":   initSQLConfigMapName,
		"ClusterType":    "private",
		"SuspendCompute": strconv.FormatBool(sharedPool.SuspendComputeDesired),
	}
	if sharedPool.GetProvider().GetType() == csngapiv1beat.ProviderType_AWS ||
		sharedPool.GetProvider().GetType() == csngapiv1beat.ProviderType_ALICLOUD ||
		sharedPool.GetProvider().GetType() == csngapiv1beat.ProviderType_AZURE {
		templArgs["EnableFeatureGateVolumeAttributesClass"] = "true"
	}

	err = templateExecuteWithVersionResolver(ctx, remoteCli, "tidb_cluster", "", templArgs, false, a.templateVersionSvc)
	if err != nil {
		tilog.Error(ctx, "failed to apply sharedPool", zap.Error(err))
		return fmt.Errorf("failed to apply sharedPool: %w", err)
	}
	tilog.Info(ctx, "apply sharedPool success", zap.String("sharedPool id", sharedPool.GetId()))
	return nil
}
