package com.usoftchina.uas.office.listener;

import com.usoftchina.uas.office.context.MasterHolder;
import com.usoftchina.uas.office.dto.UasEvent;
import com.usoftchina.uas.office.entity.DataCenter;
import com.usoftchina.uas.office.entity.Master;
import com.usoftchina.uas.office.jdbc.DataSourceHolder;
import com.usoftchina.uas.office.service.MasterService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author yingp
 * @date 2020/2/16
 */
@Component
public class UasEventListenerFactory {
    private final Logger logger = LoggerFactory.getLogger(UasEventListenerFactory.class);
    /**
     * <caller, <operation, bean+method>>
     */
    private Map<String, Map<String, List<BeanMethod>>> beanMethodMap = new HashMap<>();

    @Autowired
    private MasterService masterService;

    public void addListener(UasEventListener listener, Object bean, Method method) {
        Map<String, List<BeanMethod>> map = beanMethodMap.get(listener.caller());
        if (null == map) {
            map = new HashMap<>(1);
            beanMethodMap.put(listener.caller(), map);
        }
        if (null != listener.operation() && listener.operation().length > 0) {
            for (String opera : listener.operation()) {
                List<BeanMethod> beanMethodList = map.get(opera);
                if (null == beanMethodList) {
                    beanMethodList = new ArrayList<>(1);
                    map.put(opera, beanMethodList);
                }
                beanMethodList.add(new BeanMethod(bean, method));
            }
        }
    }

    /**
     * 查找配置的事件监听，并执行
     *
     * @param event
     * @throws Exception
     */
    @Async
    public void invokeListeners(UasEvent event) {
        try {
            DataCenter dataCenter = DataCenter.INSTANCE;
            if (null == dataCenter) {
                return;
            }
            DataSourceHolder.set(dataCenter);
            Master master = masterService.findByName(event.getMaster());
            if (null == master) {
                return;
            }
            MasterHolder.set(master);
            DataSourceHolder.set(master);
            Map<String, List<BeanMethod>> map = beanMethodMap.get(event.getCaller());
            if (null != map) {
                List<BeanMethod> beanMethodList = map.get(event.getOperation());
                if (null != beanMethodList) {
                    for (BeanMethod beanMethod : beanMethodList) {
                        try {
                            logger.debug("invoke on {}.{} {}", beanMethod.bean.getClass().getSimpleName(), beanMethod.method.getName(), event);
                            beanMethod.method.invoke(beanMethod.bean, event);
                        } catch (Exception e) {
                            logger.error("invoke error on " + event, e);
                        }
                    }
                }
            }
        } finally {
            MasterHolder.clear();
            DataSourceHolder.clear();
        }
    }

    class BeanMethod {
        private final Object bean;
        private final Method method;

        public BeanMethod(Object bean, Method method) {
            this.bean = bean;
            this.method = method;
        }
    }
}
