package framework

import (
	"fmt"
)

type ClusterScalerCreator func(
	eventReceiver EventReceiver,
	clusterId ClusterId,
	scalerConfig ConfigData,
	pInputInterface, pOutputInterface any) (pScaler any, err error)

type ClusterScalerCreatorUniqId string

type ClusterScalerManager struct {
	eventReceiver                EventReceiver
	inputProviderManager         *InputProviderManager
	outputManager                *OutputManager
	phasedComponentScalerManager *PhasedComponentScalerManager
	creators                     map[ClusterScalerCreatorUniqId]ClusterScalerCreator
}

func NewClusterScalerManager(
	eventReceiver EventReceiver,
	inputProviderManager *InputProviderManager,
	outputManager *OutputManager) (*ClusterScalerManager, error) {

	phasedComponentScalerManager, err := NewDefaultPhasedComponentScalerManager(eventReceiver)
	if err != nil {
		return nil, err
	}
	return &ClusterScalerManager{
		eventReceiver:                eventReceiver,
		inputProviderManager:         inputProviderManager,
		outputManager:                outputManager,
		phasedComponentScalerManager: phasedComponentScalerManager,
		creators:                     make(map[ClusterScalerCreatorUniqId]ClusterScalerCreator),
	}, nil
}

func (this *ClusterScalerManager) Register(meta *ClusterScalerMeta, creator ClusterScalerCreator) error {
	creatorUniqId := getClusterScalerCreatorUniqId(meta)
	_, ok := this.creators[creatorUniqId]
	if ok {
		this.eventReceiver.OnEvent(
			EventClusterScalerManagerRegisterCreatorDuplicated,
			EventDataWithName("CreatorUniqId", CloneableString(creatorUniqId)))
		return fmt.Errorf("[ClusterScalerManager.Register] meta `%s` already exists", creatorUniqId)
	}
	this.creators[creatorUniqId] = creator
	this.eventReceiver.OnEvent(
		EventClusterScalerManagerRegisterCreatorOk,
		EventDataWithName("CreateUniqId", CloneableString(creatorUniqId)))
	return nil
}

func getClusterScalerCreatorUniqId(meta *ClusterScalerMeta) ClusterScalerCreatorUniqId {
	id := fmt.Sprintf("Type:%s;InterfaceVersion:%s;InputInterfaceVersion:%s;OutputInterfaceVersion:%s",
		meta.ScalerMeta.Type,
		meta.ScalerMeta.InterfaceVersion,
		meta.InputMeta.InterfaceVersion,
		meta.OutputMeta.InterfaceVersion)
	return ClusterScalerCreatorUniqId(id)
}

func (this *ClusterScalerManager) NewClusterScaler(
	meta *ClusterScalerMeta,
	clusterId ClusterId,
	scalerConfig, inputConfig, outputConfig ConfigData) (pScaler, pInput, pOutput any, err error) {

	creatorUniqId := getClusterScalerCreatorUniqId(meta)
	creator, ok := this.creators[creatorUniqId]
	if !ok {
		this.eventReceiver.OnEvent(
			EventClusterScalerManagerCreatorNotFound,
			EventDataWithName("CreatorUniqId", CloneableString(creatorUniqId)))
		return nil, nil, nil, fmt.Errorf("[ClusterScalerManager.NewClusterScaler] meta `%s` does not exist", creatorUniqId)
	}

	pInputInterface, err := this.inputProviderManager.NewInput(meta.InputMeta, inputConfig)
	if err != nil {
		return nil, nil, nil, err
	}

	pOutputInterface, err := this.outputManager.NewOutput(meta.OutputMeta, outputConfig)
	if err != nil {
		return nil, nil, nil, err
	}

	scaler, err := creator(this.eventReceiver, clusterId, scalerConfig, pInputInterface, pOutputInterface)
	if err != nil {
		return nil, nil, nil, err
	}
	this.eventReceiver.OnEvent(
		EventClusterScalerManagerCreatedClusterScaler,
		EventDataWithName("CreatorUniqId", CloneableString(creatorUniqId)))
	return scaler, pInputInterface, pOutputInterface, nil
}

func (this *ClusterScalerManager) GetInputProviderManager() *InputProviderManager {
	return this.inputProviderManager
}

func (this *ClusterScalerManager) GetOutputManager() *OutputManager {
	return this.outputManager
}

func (this *ClusterScalerManager) GetPhasedComponentScalerManager() *PhasedComponentScalerManager {
	return this.phasedComponentScalerManager
}

func NewDefaultClusterScalerManager(eventReceiver EventReceiver) (*ClusterScalerManager, error) {
	inputProviderManager, err := NewDefaultInputProviderManager(eventReceiver)
	if err != nil {
		return nil, err
	}
	outputManager, err := NewDefaultOutputManager(eventReceiver)
	if err != nil {
		return nil, err
	}

	manager, err := NewClusterScalerManager(eventReceiver, inputProviderManager, outputManager)
	if err != nil {
		return nil, err
	}

	err = manager.Register(
		ClusterScalerMockEmptyMeta,
		func(eventReceiver EventReceiver,
			clusterId ClusterId,
			_ ConfigData,
			pInputInterface,
			pOutputInterface any) (any, error) {

			input, ok := pInputInterface.(InputInterfaceV1)
			if !ok {
				return nil, fmt.Errorf("[ClusterScalerMockEmptyMeta.Creator] input interface unmatched")
			}
			output, ok := pOutputInterface.(OutputInterfaceV1)
			if !ok {
				return nil, fmt.Errorf("[ClusterScalerMockEmptyMeta.Creator] output interface unmatched")
			}
			scaler := NewClusterScalerMockEmpty(eventReceiver, clusterId, input, output)
			return scaler, nil
		},
	)
	if err != nil {
		return nil, err
	}

	err = manager.Register(
		ClusterScalerPhasedComponentScalerMockV1Meta,
		func(eventReceiver EventReceiver,
			clusterId ClusterId,
			config ConfigData,
			pInputInterface,
			pOutputInterface any) (any, error) {

			input, ok := pInputInterface.(InputInterfaceV1)
			if !ok {
				return nil, fmt.Errorf("[ClusterScalerPhasedComponentScalerMockEmptyMeta.Creator] input interface unmatched")
			}
			output, ok := pOutputInterface.(OutputInterfaceV1)
			if !ok {
				return nil, fmt.Errorf("[ClusterScalerPhasedComponentScalerMockEmptyMeta.Creator] output interface unmatched")
			}
			if config == nil {
				return nil, fmt.Errorf("[ClusterScalerPhasedComponentScalerMockEmptyMeta.Creator] cluster scaler config is empty")
			}
			clusterScalerConfig, ok := config.(*ClusterScalerPhasedComponentScalerV1Config)
			if !ok {
				return nil, fmt.Errorf("[ClusterScalerPhasedComponentScalerMockEmptyMeta.Creator] cluster scaler config type unmatched")
			}
			componentScaler, err := manager.phasedComponentScalerManager.NewPhasedComponentScaler(
				PhasedComponentScalerMockEmptyIdStr,
				clusterId,
				clusterScalerConfig.ComponentName,
				clusterScalerConfig.ComponentScalerConfig,
				pInputInterface,
				pOutputInterface)
			if err != nil {
				return nil, err
			}
			scaler := NewClusterScalerPhasedComponentScalerV1(
				eventReceiver,
				clusterId,
				input,
				output,
				componentScaler.(PhasedComponentScalerV1),
				clusterScalerConfig.ComponentName)
			return scaler, nil
		},
	)
	if err != nil {
		return nil, err
	}

	return manager, nil
}
