package action

import (
	"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/pkg/clusterconfig"
	"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"
	"github.com/tidbcloud/cluster-service-ng/pkg/utils"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type BootstrapTiDBGroupAction struct {
	dataplaneManager    dataplane.Manager
	sharedPoolRepo      repo.SharedPoolRepo
	clusterConfigClient clusterconfig.Client
}

const (
	BootstrapTiDBGroupActionName = "bootstrap-tidbgroup"

	initSQLConfigMapName = "db-bootstrap-sql"
)

func (a *BootstrapTiDBGroupAction) Name() string {
	return BootstrapTiDBGroupActionName
}

func NewBootstrapTiDBGroupAction(
	sharedPoolRepo repo.SharedPoolRepo,
	dataplaneManager dataplane.Manager,
	clusterConfigClient clusterconfig.Client,
) actions.Action {
	return &BootstrapTiDBGroupAction{
		sharedPoolRepo:      sharedPoolRepo,
		dataplaneManager:    dataplaneManager,
		clusterConfigClient: clusterConfigClient,
	}
}

func (a *BootstrapTiDBGroupAction) Invoke(args actions.ActionArgs) error {
	// generate context
	ctx := utils.GenerateContext(args)
	tilog.Info(ctx, "bootstrap tidb group 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)
	}

	// sql statements
	initSqlStatements, err := a.clusterConfigClient.GetClusterInitSqlStmts(ctx, sharedPoolId)
	if err != nil {
		return fmt.Errorf("failed to get init sql statements: %w", err)
	}

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

	globalSortPath, err := sharedPool.GetDXFGlobalSortPath()
	if err != nil {
		return fmt.Errorf("failed to get global sort path: %w", err)
	}

	var initSQL string
	switch sharedPool.GetProvider().GetType() {
	case apiv1.ProviderType_AWS, apiv1.ProviderType_ALICLOUD, apiv1.ProviderType_AZURE:
		initSQL = fmt.Sprintf("%s\nSET GLOBAL tidb_cloud_storage_uri = '%s'",
			initSqlStatements,
			globalSortPath,
		)
	case apiv1.ProviderType_GCP:
		return fmt.Errorf("unsupported provider type: %s", sharedPool.GetProvider())
	default:
		initSQL = initSqlStatements
	}

	cm := &corev1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Name:      initSQLConfigMapName,
			Namespace: sharedPool.GetDataPlaneNamespace(),
		},
		Data: map[string]string{
			"bootstrap-sql": initSQL,
		},
	}
	if err := k8s.CreateIfNotExist(ctx, remoteCli, cm); err != nil {
		tilog.Error(ctx, "failed to create bootstrap sql config map", zap.Error(err))
		return err
	}

	return nil
}
