package action

import (
	"fmt"

	"github.com/tidbcloud/cluster-service-ng/config"
	"github.com/tidbcloud/cluster-service-ng/domain/workflow"
	"github.com/tidbcloud/cluster-service-ng/pkg/cert"
	"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"
)

type CreateClusterIssuerAction struct {
	dataplaneManager dataplane.Manager
	sharedPoolRepo   repo.SharedPoolRepo
	bizConfig        *config.BizConfig
}

const CreateClusterIssuerActionName = "create-sharedpool-issuer"

func NewCreateClusterIssuerAction(sharedPoolRepo repo.SharedPoolRepo, dataplaneManager dataplane.Manager, bizConfig *config.BizConfig) actions.Action {
	return &CreateClusterIssuerAction{
		sharedPoolRepo:   sharedPoolRepo,
		dataplaneManager: dataplaneManager,
		bizConfig:        bizConfig,
	}
}

func (a *CreateClusterIssuerAction) Name() string {
	return CreateClusterIssuerActionName
}

func (a *CreateClusterIssuerAction) Invoke(args actions.ActionArgs) error {
	// generate context
	ctx := utils.GenerateContext(args)
	tilog.Info(ctx, "create cluster issuer 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 {
		tilog.Error(ctx, "failed to load sharedPool", zap.String("sharedPoolId", sharedPoolId), zap.Error(err))
		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)
	}

	caCert := cert.SelfSignedComponentCACertificate(sharedPool.GetDataPlaneNamespace())
	if err := k8s.CreateIfNotExist(ctx, remoteCli, caCert); err != nil {
		tilog.Error(ctx, "failed to create self-signed CA cert", zap.String("namespace", sharedPool.GetDataPlaneNamespace()), zap.Error(err))
		return fmt.Errorf("apply self-signed CA cert in regional k8s: %w", err)
	}
	tilog.Info(ctx, "self-signed CA cert created", zap.String("name", caCert.GetName()), zap.String("namespace", sharedPool.GetDataPlaneNamespace()))

	issuer := cert.TiDBComponentsSelfSignedIssuer(sharedPool.GetDataPlaneNamespace())
	if err := k8s.CreateIfNotExist(ctx, remoteCli, issuer); err != nil {
		tilog.Error(ctx, "failed to create self-signed issuer", zap.String("namespace", sharedPool.GetDataPlaneNamespace()), zap.Error(err))
		return fmt.Errorf("apply  self-signed issuer in regional k8s: %w", err)
	}

	tilog.Info(ctx, "self-signed issuer created", zap.String("name", issuer.GetName()), zap.String("namespace", sharedPool.GetDataPlaneNamespace()))

	return nil
}
