package framework

import (
	"fmt"
)

type ScalingTaskExecutorCreator func(
	eventReceiver EventReceiver,
	clusterId ClusterId,
	pScaler any,
	pInput any,
	pOutput any,
	executorConfig ConfigData) (ScalingTaskExecutor, error)

type ScalingTaskExecutorManager struct {
	eventReceiver EventReceiver
	creators      map[ScalingTaskExecutorMeta]ScalingTaskExecutorCreator
}

func NewScalingTaskExecutorManager(eventReceiver EventReceiver) *ScalingTaskExecutorManager {
	return &ScalingTaskExecutorManager{
		eventReceiver: eventReceiver,
		creators:      make(map[ScalingTaskExecutorMeta]ScalingTaskExecutorCreator),
	}
}

func (this *ScalingTaskExecutorManager) Register(
	meta *ScalingTaskExecutorMeta,
	creator ScalingTaskExecutorCreator) error {

	_, ok := this.creators[*meta]
	if ok {
		this.eventReceiver.OnEvent(
			EventScalingTaskExecutorManagerRegisterCreatorDuplicated,
			EventDataWithName("ScalingTaskExecutorMeta", CloneableString(meta.String())))
		return fmt.Errorf("[ScalingTaskExecutorManager.Register] executor `%s` already exists", meta.String())
	}
	this.creators[*meta] = creator
	this.eventReceiver.OnEvent(
		EventScalingTaskExecutorManagerRegisterCreatorOk,
		EventDataWithName("ScalingTaskExecutorMeta", CloneableString(meta.String())))
	return nil
}

func (this *ScalingTaskExecutorManager) NewExecutor(
	meta *ScalingTaskExecutorMeta,
	clusterId ClusterId,
	pScaler any,
	pInput any,
	pOutput any,
	executorConfig ConfigData) (ScalingTaskExecutor, error) {

	creator, ok := this.creators[*meta]
	if !ok {
		this.eventReceiver.OnEvent(
			EventScalingTaskExecutorManagerCreatorNotFound,
			EventDataWithName("ScalingTaskExecutorMeta", CloneableString(meta.String())))
		return nil, fmt.Errorf("[ScalingTaskExecutorManager.NewExecutor] executor `%s` does not exist", meta.String())
	}
	executor, err := creator(this.eventReceiver, clusterId, pScaler, pInput, pOutput, executorConfig)
	if err != nil {
		return nil, err
	}
	this.eventReceiver.OnEvent(
		EventScalingTaskExecutorManagerCreatedExecutor,
		EventDataWithName("ScalingTaskExecutorMeta", CloneableString(meta.String())))
	return executor, nil
}

func NewDefaultScalingTaskExecutorManager(eventReceiver EventReceiver) (*ScalingTaskExecutorManager, error) {
	manager := NewScalingTaskExecutorManager(eventReceiver)

	// for ClusterScaleInterfaceV1, all input versions are irrelevant, so we register them in a batch
	inputInterfaces := []InputInterfaceVersion{
		InputInterfaceV1IdStr,
	}
	for _, inputInterface := range inputInterfaces {
		meta := &ScalingTaskExecutorMeta{
			ScalerInterfaceVersion: ClusterScalerInterfaceV1IdStr,
			InputInterfaceVersion:  inputInterface,
			OutputInterfaceVersion: OutputInterfaceV1IdStr,
			ExecutorName:           ScalerInterfaceV1ExecutorSyncLoopIdStr,
		}
		meta.ScalerType = ClusterScalerMockEmptyIdStr

		err := manager.Register(
			meta,
			func(
				eventReceiver EventReceiver,
				clusterId ClusterId,
				pScaler any,
				pInput any,
				pOutput any,
				executorConfig ConfigData) (ScalingTaskExecutor, error) {
				scaler, ok := pScaler.(ClusterScalerInterfaceV1)
				if !ok {
					return nil, fmt.Errorf("[ScalingTaskExecutorManager.Creator] scaler interface unmatched")
				}
				input, ok := pInput.(InputInterfaceV1)
				if !ok {
					return nil, fmt.Errorf("[ScalingTaskExecutorManager.Creator] input interface unmatched")
				}
				output, ok := pOutput.(OutputInterfaceV1)
				if !ok {
					return nil, fmt.Errorf("[ScalingTaskExecutorManager.Creator] output interface unmatched")
				}
				cfg, ok := executorConfig.(*ScalerInterfaceV1ExecutorSyncLoopConfig)
				if !ok || cfg == nil {
					return nil, fmt.Errorf("[ScalingTaskExecutorManager.Creator] executor config unmatched")
				}
				return NewScalerInterfaceV1ExecutorSyncLoop(
					eventReceiver,
					clusterId,
					scaler,
					input,
					output,
					cfg), nil
			},
		)
		if err != nil {
			return nil, err
		}

		meta.ScalerType = ClusterScalerPhasedComponentScalerV1IdStr
		err = manager.Register(
			meta,
			func(
				eventReceiver EventReceiver,
				clusterId ClusterId,
				pScaler any,
				pInput any,
				pOutput any,
				executorConfig ConfigData) (ScalingTaskExecutor, error) {
				scaler, ok := pScaler.(ClusterScalerInterfaceV1)
				if !ok {
					return nil, fmt.Errorf("[ScalingTaskExecutorManager.Creator] scaler interface unmatched")
				}
				input, ok := pInput.(InputInterfaceV1)
				if !ok {
					return nil, fmt.Errorf("[ScalingTaskExecutorManager.Creator] input interface unmatched")
				}
				output, ok := pOutput.(OutputInterfaceV1)
				if !ok {
					return nil, fmt.Errorf("[ScalingTaskExecutorManager.Creator] output interface unmatched")
				}
				cfg, ok := executorConfig.(*ScalerInterfaceV1ExecutorSyncLoopConfig)
				if !ok || cfg == nil {
					return nil, fmt.Errorf("[ScalingTaskExecutorManager.Creator] executor config unmatched")
				}
				return NewScalerInterfaceV1ExecutorSyncLoop(
					eventReceiver,
					clusterId,
					scaler,
					input,
					output,
					cfg), nil
			},
		)
		if err != nil {
			return nil, err
		}
	}

	return manager, nil
}
