package framework

import (
	"fmt"
)

type OutputCreator func(
	eventReceiver EventReceiver,
	config ConfigData) (pOutput any, err error)

type OutputManager struct {
	eventReceiver EventReceiver
	creators      map[OutputMeta]OutputCreator
}

func NewOutputManager(eventReceiver EventReceiver) *OutputManager {
	return &OutputManager{
		eventReceiver: eventReceiver,
		creators:      make(map[OutputMeta]OutputCreator),
	}
}

func (this *OutputManager) Register(meta OutputMeta, creator OutputCreator) error {
	_, ok := this.creators[meta]
	if ok {
		this.eventReceiver.OnEvent(
			EventOutputManagerRegisterCreatorDuplicated,
			EventDataWithName("OutputMeta", CloneableString(meta.String())))
		return fmt.Errorf("[OutputManager.Register] meta `%s` already exists", meta.String())
	}
	this.creators[meta] = creator
	this.eventReceiver.OnEvent(
		EventOutputManagerRegisterCreatorOk,
		EventDataWithName("OutputMeta", CloneableString(meta.String())))
	return nil
}

func (this *OutputManager) NewOutput(meta OutputMeta, config ConfigData) (any, error) {
	creator, ok := this.creators[meta]
	if !ok {
		this.eventReceiver.OnEvent(
			EventOutputManagerCreatorNotFound,
			EventDataWithName("OutputMeta", CloneableString(meta.String())))
		return nil, fmt.Errorf("[OutputManager.NewOutput] meta `%s` does not exist", meta.String())
	}
	output, err := creator(this.eventReceiver, config)
	if err != nil {
		return nil, err
	}
	this.eventReceiver.OnEvent(
		EventOutputManagerCreatedOutput,
		EventDataWithName("OutputMeta", CloneableString(meta.String())))
	switch meta.InterfaceVersion {
	case OutputInterfaceV1IdStr:
		if _, ok := output.(OutputInterfaceV1); !ok {
			return nil, fmt.Errorf("[OutputManager.NewOutput] output `%s` does not implement OutputInterfaceV1", meta.String())
		}
	case OutputInterfaceV2IdStr:
		if _, ok := output.(OutputInterfaceV2); !ok {
			return nil, fmt.Errorf("[OutputManager.NewOutput] output `%s` does not implement OutputInterfaceV2", meta.String())
		}
	}
	return output, nil
}

func NewDefaultOutputManager(eventReceiver EventReceiver) (*OutputManager, error) {
	manager := NewOutputManager(eventReceiver)

	err := manager.Register(
		OutputMockEmptyMeta,
		func(eventReceiver EventReceiver, _ ConfigData) (any, error) {
			return NewOutputMockEmpty(eventReceiver), nil
		},
	)
	if err != nil {
		return nil, err
	}

	return manager, nil
}
