package experiment

import (
	"context"

	"github.com/tidbcloud/chaos-svc/idl/pbgen/proto/chaos"
	experiment_query "github.com/tidbcloud/chaos-svc/pkg/application/query/experiment"
	"github.com/tidbcloud/chaos-svc/pkg/domain/facade"
	"github.com/tidbcloud/chaos-svc/pkg/domain/repo"
	workflowSvc "github.com/tidbcloud/chaos-svc/pkg/domain/service/workflow"
	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/pkgv2/db"
	"github.com/tidbcloud/pkgv2/log"
)

type UpdateExperimentSvc struct {
	trans             db.TransProvider
	infraFacade       facade.Infra
	updateWorkflowSvc workflowSvc.UpdateWorkflowSvc
	specConverter     workflowSvc.SpecConverter
	workflowLockRepo  repo.WorkflowLockRepo
	backendRepo       repo.BackendRepo
}

func NewUpdateExperimentSvc(
	trans db.TransProvider,
	infraFacade facade.Infra,
	updateWorkflowSvc workflowSvc.UpdateWorkflowSvc,
	specConverter workflowSvc.SpecConverter,
	workflowLockRepo repo.WorkflowLockRepo,
	backendRepo repo.BackendRepo,
) *UpdateExperimentSvc {
	return &UpdateExperimentSvc{
		trans:             trans,
		infraFacade:       infraFacade,
		updateWorkflowSvc: updateWorkflowSvc,
		specConverter:     specConverter,
		workflowLockRepo:  workflowLockRepo,
		backendRepo:       backendRepo,
	}
}

func (svc *UpdateExperimentSvc) Execute(ctx context.Context, req *chaos.UpdateExperimentRequest) (*chaos.Experiment, bizerr.BizError) {
	logger := log.WithContextS(ctx).With("clusterID", req.ClusterId).With("projectID", req.ProjectId)
	logger.Info("update experiment")

	tidbCluster, err := svc.infraFacade.GetTiDBCluster(ctx, req.ProjectId, req.ClusterId)
	if err != nil {
		return nil, err
	}
	backend, err := svc.backendRepo.Load(ctx, tidbCluster.ProjectID, tidbCluster.NetworkName)
	if err != nil {
		return nil, err
	}
	if err := ValidateChaosBackendReady(backend); err != nil {
		return nil, err
	}

	var experimentPb *chaos.Experiment
	if err := svc.trans.Transaction(ctx, func(ctx context.Context) error {
		params, err := svc.buildUpdateWorkflowParams(req, *tidbCluster)
		if err != nil {
			return err
		}

		workflowEntity, err := svc.updateWorkflowSvc.Execute(ctx, params)
		if err != nil {
			return err
		}
		experimentPb, err = experiment_query.WorkflowEntity2Pb(svc.specConverter, workflowEntity)
		if err != nil {
			return err
		}
		return nil
	}); err != nil {
		return nil, bizerr.WrapError(err, "transaction")
	}
	return experimentPb, nil
}

func (svc *UpdateExperimentSvc) buildUpdateWorkflowParams(req *chaos.UpdateExperimentRequest, cluster facade.TiDBCluster) (*workflowSvc.UpdateWorkflowParam, bizerr.BizError) {
	return &workflowSvc.UpdateWorkflowParam{
		TiDBCluster: cluster,
		WorkflowID:  req.ExperimentId,
		Draft:       req.Draft,
		Summary:     req.Summary,
		ReportLink:  req.ReportLink,
	}, nil
}
