package action

import (
	"fmt"

	"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"
	"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 CreateServiceAccountAction struct {
	dataplaneManager dataplane.Manager

	sharedPoolRepo repo.SharedPoolRepo
}

const (
	CreateServiceAccountActionName = "create-service-account"

	placeholderServiceAccountName = "placeholder"
	clusterServiceAccountName     = "tidb-cluster"
)

func NewCreateServiceAccountAction(sharedPoolRepo repo.SharedPoolRepo, dataplaneManager dataplane.Manager) actions.Action {
	return &CreateServiceAccountAction{
		sharedPoolRepo:   sharedPoolRepo,
		dataplaneManager: dataplaneManager,
	}
}

func (a *CreateServiceAccountAction) Name() string {
	return CreateServiceAccountActionName
}

// Invoke create service account for the shared pool
func (a *CreateServiceAccountAction) Invoke(args actions.ActionArgs) error {
	// generate context
	ctx := utils.GenerateContext(args)
	tilog.Info(ctx, "create service account action invoked")

	// get sharedPool id
	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)
	}

	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)
	}

	// for cluster pods
	{
		pc := &corev1.ServiceAccount{
			ObjectMeta: metav1.ObjectMeta{
				Name:      clusterServiceAccountName,
				Namespace: entity.GetDataPlaneNamespace(sharedPoolId),
			},
		}

		if err := k8s.CreateIfNotExist(ctx, remoteCli, pc); err != nil {
			return fmt.Errorf("failed to create sharedpool service account: %w", err)
		}
	}

	// for placeholder pods
	{

		sa := &corev1.ServiceAccount{
			ObjectMeta: metav1.ObjectMeta{
				Name:      placeholderServiceAccountName,
				Namespace: "default",
			},
		}

		err = k8s.CreateIfNotExist(ctx, remoteCli, sa)
		if err != nil {
			return fmt.Errorf("failed to create placeholder service account: %w", err)
		}
	}

	return nil
}
