package cn.estsh.i3plus.ext.mes.apiservice.serviceimpl.busi.bu3;

import cn.estsh.i3plus.ext.mes.api.busi.bu3.ISxProdDataToSoftAdapterCfgService;
import cn.estsh.i3plus.ext.mes.api.busi.bu3.ISxProdDataToSoftAdapterService;
import cn.estsh.i3plus.ext.mes.pojo.bean.MesProdDataToSoftAdapterCfg;
import cn.estsh.i3plus.ext.mes.pojo.model.bu3.SxProdDataToSoftAdapterCfgModel;
import cn.estsh.i3plus.ext.mes.pojo.model.bu3.SxProdDataToSoftAdapterDataModel;
import cn.estsh.i3plus.ext.mes.pojo.util.MesExtConstWords;
import cn.estsh.impp.framework.boot.util.SpringContextsUtil;
import com.google.common.util.concurrent.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

@Slf4j
public class SxProdDataToSoftAdapterService implements ISxProdDataToSoftAdapterService {

    private ISxProdDataToSoftAdapterCfgService prodDataToSoftAdapterCfgService;

    protected static final Map<String, ListenableFuture<Boolean>> listenFutureTaskMap = new ConcurrentHashMap<>();

    public SxProdDataToSoftAdapterService() {
        this.prodDataToSoftAdapterCfgService = (ISxProdDataToSoftAdapterCfgService) SpringContextsUtil.getBean("sxProdDataToSoftAdapterCfgService");
    }

    @Override
    public void exec(SxProdDataToSoftAdapterDataModel data) {

        Map<String, List<SxProdDataToSoftAdapterCfgModel>> cfgMap = getCfgMap(data);

        if (CollectionUtils.isEmpty(cfgMap)) return;

        CountDownLatch countDownLatch = new CountDownLatch(cfgMap.size());

        ExecutorService executorService = Executors.newFixedThreadPool(cfgMap.size());

        for (Map.Entry<String, List<SxProdDataToSoftAdapterCfgModel>> entry : cfgMap.entrySet()) {
            if (null == entry) {
                countDownLatch.countDown();
                continue;
            }

            log.info("MES工厂{}DATE{}执行生产数据回传软适配JOB --- EXEC STRATEGY_CLASS[{}] --- START --- THREAD:[{}]", data.getOrganizeCode(), data.getDate(), entry.getKey(), data.getCurrentThreadName());

            String key = new StringJoiner(MesExtConstWords.AND).add(data.getOrganizeCode()).add(entry.getKey()).toString();

            ListeningExecutorService listenExecutor = MoreExecutors.listeningDecorator(executorService);
            ListenableFuture<Boolean> listenFuture = ListenableFutureTask.create(() ->
                    execAsynchronous(new SxProdDataToSoftAdapterDataModel(data, entry.getKey(), getClassName(entry.getKey()), entry.getValue()))
            );

            addTask(key, listenFuture);
            listenExecutor.submit((ListenableFutureTask) listenFuture);
            Futures.addCallback(listenFuture, new FutureCallback<Boolean>() {
                @Override
                public void onSuccess(Boolean obj) {
                    log.info("MES工厂{}DATE{}执行生产数据回传软适配JOB --- EXEC STRATEGY_CLASS[{}] --- SUCCESS --- THREAD:[{}]", data.getOrganizeCode(), data.getDate(), entry.getKey(), data.getCurrentThreadName());
                    countDownLatch.countDown();
                }

                @Override
                public void onFailure(Throwable t) {
                    log.info("MES工厂{}DATE{}执行生产数据回传软适配JOB --- EXEC STRATEGY_CLASS[{}] --- FAILURE:[{}] --- THREAD:[{}]", data.getOrganizeCode(), data.getDate(), entry.getKey(), t.getMessage(), data.getCurrentThreadName());
                    countDownLatch.countDown();
                }
            }, listenExecutor);
        }

        try {

            countDownLatch.await(5, TimeUnit.MINUTES);

            log.info("MES工厂{}DATE{}执行生产数据回传软适配JOB --- EXEC FINALLY END --- THREAD:[{}]", data.getOrganizeCode(), data.getDate(), data.getCurrentThreadName());

        } catch (Exception e) {

            log.info("MES工厂{}DATE{}执行生产数据回传软适配JOB --- EXEC FINALLY ERROR:[{}] --- THREAD:[{}]", data.getOrganizeCode(), data.getDate(), e.getMessage(), data.getCurrentThreadName());

        } finally {

            for (String key : cfgMap.keySet()) {
                if (StringUtils.isEmpty(key)) continue;
                cancelRemoveTask(new StringJoiner(MesExtConstWords.AND).add(data.getOrganizeCode()).add(key).toString());
            }

            executorService.shutdown();

        }

    }

    private Map<String, List<SxProdDataToSoftAdapterCfgModel>> getCfgMap(SxProdDataToSoftAdapterDataModel data) {
        List<MesProdDataToSoftAdapterCfg> cfgList = prodDataToSoftAdapterCfgService.queryProdDataToSoftAdapterCfgList(data.getOrganizeCode());
        if (CollectionUtils.isEmpty(cfgList)) log.info("MES工厂{}DATE{}执行生产数据回传软适配JOB --- EXEC ERROR --- 未配置生产数据回传软适配配置信息 --- THREAD:[{}]", data.getOrganizeCode(), data.getDate(), data.getCurrentThreadName());
        cfgList = CollectionUtils.isEmpty(data.getSuitCaseCodeList()) ? cfgList :
                (CollectionUtils.isEmpty(cfgList) ? cfgList : cfgList.stream().filter(o -> (null != o && data.getSuitCaseCodeList().contains(o.getSuitCaseCode()))).collect(Collectors.toList()));
        return getCfgMap(data, cfgList);
    }

    private Map<String, List<SxProdDataToSoftAdapterCfgModel>> getCfgMap(SxProdDataToSoftAdapterDataModel data, List<MesProdDataToSoftAdapterCfg> cfgList) {
        if (CollectionUtils.isEmpty(cfgList)) return null;
        Map<String, List<SxProdDataToSoftAdapterCfgModel>> cfgMap = null;
        for (MesProdDataToSoftAdapterCfg cfg : cfgList) {
            if (null == cfg || StringUtils.isEmpty(cfg.getSuitCaseCode())) continue;
            if (!checkCfgRequired(data, cfg) || !checkCfgSplitCount(data, cfg)) continue;
            if (CollectionUtils.isEmpty(cfgMap)) cfgMap = new HashMap<>();
            List<SxProdDataToSoftAdapterCfgModel> cfgModelList = cfgMap.get(cfg.getStrategyClass());
            if (CollectionUtils.isEmpty(cfgModelList)) cfgModelList = new ArrayList<>();
            SxProdDataToSoftAdapterCfgModel cfgModel = new SxProdDataToSoftAdapterCfgModel();
            BeanUtils.copyProperties(cfg, cfgModel);
            cfgModel.setClassName(getClassName(StringUtils.isEmpty(cfgModel.getExecClass()) ? cfgModel.getStrategyClass() : cfgModel.getExecClass()));
            cfgModelList.add(cfgModel);
            cfgMap.put(cfg.getStrategyClass(), cfgModelList);
        }
        if (CollectionUtils.isEmpty(cfgMap)) log.info("MES工厂{}DATE{}执行生产数据回传软适配JOB --- EXEC ERROR --- 未配置有效的生产数据回传软适配配置信息 --- THREAD:[{}]", data.getOrganizeCode(), data.getDate(), data.getCurrentThreadName());
        return cfgMap;
    }

    private Boolean checkCfgRequired(SxProdDataToSoftAdapterDataModel data, MesProdDataToSoftAdapterCfg cfg) {
        if (!StringUtils.isEmpty(cfg.getSuitSid()) && !StringUtils.isEmpty(cfg.getStrategyClass()) && !StringUtils.isEmpty(cfg.getFieldContent())) return true;
        log.info("MES工厂{}DATE{}执行生产数据回传软适配JOB --- EXEC ERROR --- 生产数据回传软适配配置信息适配器代码[{}]缺失必要信息 --- THREAD:[{}]",
                data.getOrganizeCode(), data.getDate(), cfg.getSuitCaseCode(), data.getCurrentThreadName());
        return false;
    }

    private Boolean checkCfgSplitCount(SxProdDataToSoftAdapterDataModel data, MesProdDataToSoftAdapterCfg cfg) {
        if (StringUtils.isEmpty(cfg.getSplitCount())) return true;
        try {
            Integer.valueOf(cfg.getSplitCount());
            return true;
        } catch (NumberFormatException e) {
            log.info("MES工厂{}DATE{}执行生产数据回传软适配JOB --- EXEC ERROR --- 生产数据回传软适配配置信息适配器代码[{}]文件数据量[{}]配置有误 --- THREAD:[{}]",
                    data.getOrganizeCode(), data.getDate(), cfg.getSuitCaseCode(), cfg.getSplitCount(), data.getCurrentThreadName());
            return false;
        }
    }

    private String getClassName(String clazz) {
        try {
            return clazz.substring(clazz.lastIndexOf(".") + 1);
        } catch (Exception e) {
            return null;
        }
    }


    private Boolean execAsynchronous(SxProdDataToSoftAdapterDataModel data) {
        new SxProdDataToSoftAdapterStrategyWatchService(new SxProdDataToSoftAdapterStrategyDispatchService()).exec(data);
        return true;
    }

    private static synchronized void cancelRemoveTask(String key) {
        cancelTask(key);
        removeTask(key);
    }

    private static synchronized void cancelTask(String key) {
        ListenableFuture<Boolean> cacheListenableFuture = listenFutureTaskMap.get(key);
        if (cacheListenableFuture != null && !cacheListenableFuture.isCancelled()) cacheListenableFuture.cancel(true);
    }

    private static synchronized void addTask(String key, ListenableFuture<Boolean> listenableFuture) { listenFutureTaskMap.put(key, listenableFuture); }

    private static synchronized void removeTask(String key) { listenFutureTaskMap.remove(key); }

}
