package action

import (
	"fmt"

	"github.com/tidbcloud/fastflow/pkg/entity/run"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"google.golang.org/protobuf/encoding/protojson"

	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/adapter/clusterconfig"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/cluster"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/service/workflow/common"
	"github.com/tidbcloud/tidb-management-service/utilities/workflow"
)

const (
	GenerateConfiguration = "generate_configuration"
)

type GenerationConfigurationAction struct {
	clusterConfigCli clusterconfig.Client
	clusterRepo      cluster.Repo
}

func NewGenerationConfigurationAction(
	clusterRepo cluster.Repo,
	clusterConfigCli clusterconfig.Client,
) *GenerationConfigurationAction {
	return &GenerationConfigurationAction{
		clusterRepo:      clusterRepo,
		clusterConfigCli: clusterConfigCli,
	}
}

func (a *GenerationConfigurationAction) Name() string {
	return GenerateConfiguration
}

func (a *GenerationConfigurationAction) ParameterNew() any {
	return &common.ClusterIdParameter{}
}

func (a *GenerationConfigurationAction) Run(ctx run.ExecuteContext, params any) error {
	cctx, span := workflow.GenerateContext(ctx)

	clusterId, err := common.GetClusterId(params)
	if err != nil {
		tilog.Error(cctx, "failed to get cluster id", zap.Error(err))
		return err
	}

	tilog.Info(cctx, "start to generate configuration", zap.Uint64("cluster_id", clusterId))
	defer func() {
		tilog.Info(cctx, "end to generate configuration", zap.Uint64("cluster_id", clusterId))
		span.End()
	}()

	clusterEn, err := a.clusterRepo.GetAndLoadComponents(cctx, clusterId)
	if err != nil {
		tilog.Error(cctx, "failed to get cluster", zap.Error(err))
		return err
	}

	// version use default version from config center.
	// create cluster meta, including version, imagetag, configuration and init sql.
	if _, err := a.clusterConfigCli.CreateClusterMeta(cctx, clusterEn); err != nil {
		tilog.Error(cctx, "failed to create cluster meta", zap.Error(err))
		return err
	}

	cfg, ok := ctx.GetVar(common.KeyClusterConfiguration)
	// no need to set cluster config
	if !ok || cfg == "" {
		return nil
	}

	var overwriteTenantInitConfig bool
	if v, ok := ctx.GetVar(common.KeyOverwriteTenantInitConfig); ok && v == "true" {
		overwriteTenantInitConfig = true
	}
	tilog.Info(cctx, "overwriteTenantInitConfig", zap.Bool("overwrite_tenant_init_config", overwriteTenantInitConfig))

	var clusterConfiguration entity.ClusterConfiguration
	if err := protojson.Unmarshal([]byte(cfg), &clusterConfiguration); err != nil {
		return fmt.Errorf("unmarshal cluster configuration failed: %w", err)
	}

	if overwriteTenantInitConfig {
		err = a.clusterConfigCli.OverwriteClusterComponentConfigurations(cctx, clusterEn, &clusterConfiguration)
		if err != nil {
			tilog.Error(cctx, "failed to overwrite component custom params or set new image tag", zap.Error(err))
			return err
		}
	} else {
		err = a.clusterConfigCli.UpdateClusterComponentConfigurations(cctx, clusterEn, &clusterConfiguration)
		if err != nil {
			tilog.Error(cctx, "failed to upsert component custom params or set new image tag", zap.Error(err))
			return err
		}
	}

	return nil
}
