package applier

import (
	"context"

	ccent "github.com/tidbcloud/devops/entity/cloudconfig"
	"github.com/tidbcloud/devops/pkg/service/cloudconfig/api"
)

type Input struct {
	// Change is the change to apply
	// NOTE: use the following changes that are accepted by this applier, do not use `Change.Scope`.
	Change *ccent.ConfigurationChange // the change

	// Version is the version change for this applier
	Version *ccent.ConfigurationVersionChange
	// Components are component changes for this applier
	// It may be a part of all component changes.
	Components []*ccent.ConfigurationComponentChange
	// Files are file changes for this applier
	// It may be a part of all file changes.
	Files []*ccent.ConfigurationFileChange
}

func NewInput(chg *ccent.ConfigurationChange) *Input {
	return &Input{
		Change: chg,
	}
}

func (c *Input) ChangedScope() *ccent.ChangedScope {
	scope := ccent.NewChangedScope()
	if c.Version != nil {
		scope.Merge(c.Version.ChangedScope())
	}
	for _, file := range c.Files {
		scope.Merge(file.ChangedScope())
	}
	for _, comp := range c.Components {
		scope.Merge(comp.ChangedScope())
	}

	return scope
}

type ApplyContext struct {
	Input *Input
	// ApplyMode means applier use a special mode to apply changes.
	ApplyMode *api.ApplyMode

	// context
	MgmtServiceApplierRequest             *MgmtServiceApplierRequest
	DataflowApplierRequest                *DataflowApplierRequest
	DataflowNgApplierRequest              *DataflowNgApplierRequest
	ServerlessChangefeedApplierRequest    *ServerlessChangefeedApplierRequest
	ConfigCenterTenantInitApplierRequest  *ConfigCenterTenantInitApplierRequest
	OnlyConfigCenterClusterApplierRequest *OnlyConfigCenterClusterApplierRequest
	ConfigurationTemplateApplierRequest   *ConfigurationTemplateApplierRequest
	NextGenApplierRequest                 *NextGenApplierRequest
	ResourcePoolApplierRequest            *ResourcePoolApplierRequest
}

type CheckResultContext struct {
	Input *Input
}

type Applier interface {
	// Name is the identifier of the applier.
	Name() string
	// AcceptVersionChange return true if the applier need to apply version change.
	AcceptVersionChange(verChg *ccent.ConfigurationVersionChange, changeType string) bool
	// AcceptComponentChange return true if the applier need to apply component change.
	AcceptComponentChange(compChg *ccent.ConfigurationComponentChange) bool
	// AcceptFileChange return true if the applier need to apply file change.
	AcceptFileChange(fileChg *ccent.ConfigurationFileChange) bool
	// Prepare check all changes and prepare the apply request.
	Prepare(ctx context.Context, actx *ApplyContext) error
	// Apply apply the change to the target service
	Apply(ctx context.Context, actx *ApplyContext) error
	// CheckResult check the result of the apply request.
	CheckResult(ctx context.Context, rctx *CheckResultContext) (bool, string, error)
}
