// Copyright (c) Shenyang Leading Edge Intelligent Technology Co., Ltd. All rights reserved.

package container

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
)

type DefaultComponentContainer struct {
	methodMapping map[string]func([]reflect.Value) []reflect.Value
}

func NewDefaultComponentContainer() *DefaultComponentContainer {
	return &DefaultComponentContainer{
		methodMapping: make(map[string]func([]reflect.Value) []reflect.Value),
	}
}

func (container *DefaultComponentContainer) RegisterService(factory interface{}) error {

	valueOfFactory := reflect.ValueOf(factory)
	typeOfFactory := valueOfFactory.Type()
	if typeOfFactory.Kind() != reflect.Func {
		return errors.New("工厂必须是func!")
	}
	if typeOfFactory.NumIn() != 0 {
		return errors.New("工厂不能有参数!")
	}
	if typeOfFactory.NumOut() < 1 {
		return errors.New("工厂必值需有返回值!")
	}
	if typeOfFactory.NumOut() > 1 {
		return errors.New("工厂只能有一个返回值!")
	}
	if typeOfFactory.Out(0).Kind() != reflect.Interface {
		return errors.New("工厂的返回值必需是interface!")
	}
	serviceType := typeOfFactory.Out(0)
	serviceName := serviceType.Name()
	if serviceType.Kind() != reflect.Interface {
		return errors.New(fmt.Sprintf("服务类型必须是接口! 服务: %s", serviceName))
	}
	if !strings.HasSuffix(serviceName, ServiceNameSuffix) {
		return errors.New(fmt.Sprintf("服务%s接口名称的后缀不是%s!", serviceType.Name(), ServiceNameSuffix))
	}
	packageList := strings.Split(serviceType.PkgPath(), PathSeparator)
	servicePackagePath := ""
	for index, packageName := range packageList {
		if strings.ToLower(packageName) == ServicesPackageName {
			servicePackagePath = strings.Join(packageList[index+1:], PathSeparator)
			break
		}
	}
	if servicePackagePath == "" {
		return errors.New(fmt.Sprintf("服务%s不在包%s或他的子包之下!", serviceType.Name(), ServicesPackageName))
	}
	for i := 0; i < serviceType.NumMethod(); i++ {
		method := serviceType.Method(i)
		serviceValue := valueOfFactory.Call(emptyParameters)[0]
		methodValue := serviceValue.MethodByName(method.Name)
		methodFullName := fmt.Sprintf("%s/%s.%s", servicePackagePath, serviceType.Name(), method.Name)
		container.methodMapping[methodFullName] = methodValue.Call
	}
	return nil
}

func (container *DefaultComponentContainer) GetServiceCaller(methodFullName string) func([]reflect.Value) []reflect.Value {
	caller, ok := container.methodMapping[methodFullName]
	if ok {
		return caller
	}
	return nil
}
