package config

import (
	"fmt"
	"strings"
	"time"

	ccent "github.com/tidbcloud/devops/entity/cloudconfig"
	"github.com/tidbcloud/devops/pkg/infra/errs"
	"github.com/tidbcloud/devops/pkg/infra/utils/container/pair"
	"github.com/tidbcloud/devops/pkg/infra/utils/pointer"
	"github.com/tidbcloud/devops/pkg/service/cloudconfig/change/config/applier"
)

type actorApplying struct {
	*baseActor
}

func (a *actorApplying) CheckResult() error {
	ctx := a.Context

	// distribute
	candAppliers, err := a.candidateAppliers(a.Change)
	if err != nil {
		return err
	}
	inputPairs, err := a.distributeChange(candAppliers)
	if err != nil {
		return err
	}
	ctxPairs := pair.NewPairs[applier.Applier, *applier.CheckResultContext]()
	for apler, input := range inputPairs.Pairs() {
		ctxPairs.Append(apler, &applier.CheckResultContext{
			Input: input,
		})
	}

	// check result
	allApplied := true
	reasons := []string{}
	for applier, rctx := range ctxPairs.Pairs() {
		scope := rctx.Input.ChangedScope()
		if scope.Empty() {
			continue
		}

		applied, reason, err := applier.CheckResult(ctx, rctx)
		if err != nil {
			return fmt.Errorf("%s check result failed: %w", applier.Name(), err)
		}

		allApplied = allApplied && applied
		if reason != "" {
			reasons = append(reasons, reason)
		}
	}

	// update the change status
	if allApplied {
		return a.PatchConfigurationChange(ctx, func(current *ccent.ConfigurationChange) error {
			current.Phase = ccent.ConfigurationChangePhaseApplied
			current.AppliedAt = pointer.Pointer(time.Now().Unix())
			return nil
		})
	}

	return errs.RetriableErrorf("waiting for the change applied: %s", strings.Join(reasons, ", "))
}
