package com.prolog.cs.biz.outbound.service.strategy;

import com.prolog.cs.biz.outbound.service.strategy.handles.AbstractStrategyHandle;
import com.prolog.cs.biz.outbound.service.strategy.handles.IStrategyHandle;
import com.prolog.framework.core.exception.BizException;
import com.prolog.upcloud.base.strategy.model.BusinessControl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;

import java.beans.Introspector;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @program: upcloud-base-wh-outbound
 * @description: 业务配置参数工厂类
 * @author: Huanglh
 * @create: 2022-01-12 12:12
 **/
@Service
@Slf4j
public class StrategyFactory implements ApplicationContextAware {
    // 同 bean 工厂一级缓存,用线程安全 map，避免缓存整个 context
    private Map<String, Object> handleBeans = new ConcurrentHashMap<>();

    /**
     * 根据业务参数枚举返回对应的 handle
     *
     * @param code
     * @param point              配置点，用来表示使用handle的当前操作点，如是在拣货，还是在外复核，以便触发对应的操作
     * @param businessControlMap 如果没有配置则value为 null
     * @return
     */
    public IStrategyHandle getStrategy(STRATEGY_CODE code, String point, Map<STRATEGY_CODE, BusinessControl> businessControlMap) {
        AbstractStrategyHandle res;

        try {
            // 将类名格式化成 Spring 中的标准类名
            res = (AbstractStrategyHandle) this.handleBeans.get(Introspector.decapitalize(code.getClazz().getSimpleName()));
            res.setPoint(point);
            res.setBusinessControlMap(businessControlMap);
        } catch (Exception e) {
            throw new BizException("业务配置参数：" + code.getCode() + " 的对应操作方法实例化失败：" + e.getMessage());
        }

        return res;
    }

    /**
     * 多个配置，返回所有配置 handle 的 map
     *
     * @param point              配置点，用来表示使用handle的当前操作点，如是在拣货，还是在外复核，以便触发对应的操作
     * @param businessControlMap 如果没有配置则value为 null
     * @return
     */
    public Map<STRATEGY_CODE, IStrategyHandle> getStrategy(String point, Map<STRATEGY_CODE, BusinessControl> businessControlMap) {
        Map<STRATEGY_CODE, IStrategyHandle> res = new HashMap<>();

        for (Map.Entry<STRATEGY_CODE, BusinessControl> entry : businessControlMap.entrySet()) {
            STRATEGY_CODE code = entry.getKey();
            // 存放已生成的handle示例，避免同一handle实例化多个(可能会有数据隔离问题)
            Map<Class<?>, AbstractStrategyHandle> existHandle = new HashMap<>();
            try {
                AbstractStrategyHandle handle;
                if (existHandle.containsKey(code.getClazz())) {
                    handle = existHandle.get(code.getClazz());
                } else {
                    handle = (AbstractStrategyHandle) this.handleBeans.get(Introspector.decapitalize(code.getClazz().getSimpleName()));
                    handle.setPoint(point);
                    handle.setBusinessControlMap(businessControlMap);
                }

                res.put(code, handle);
            } catch (Exception e) {
                throw new BizException("业务配置参数：" + code.getCode() + " 的对应操作方法实例化失败：" + e.getMessage());
            }
        }

        return res;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, AbstractStrategyHandle> beanMap = applicationContext.getBeansOfType(AbstractStrategyHandle.class);
        beanMap.forEach((k, v) -> this.handleBeans.put(k, v));
    }
}
