package com.nzwz.serivce.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.nzwz.common.constants.Constants;
import com.nzwz.common.response.RestResponse;
import com.nzwz.quartz.model.QuartzJob;
import com.nzwz.quartz.transfer.QuartzJobPauseInVo;
import com.nzwz.quartz.transfer.QuartzJobResumeInVo;
import com.nzwz.transfer.synchronization.AbnormalSynchronizationInVo;
import com.nzwz.transfer.synchronization.InitConnectParmCreateInVo;
import com.nzwz.transfer.synchronization.InitConnectParmOutVo;
import com.nzwz.utils.JsonUtil;
import com.nzwz.utils.KingDeeUtil;
import com.nzwz.utils.StringUtil;
import com.nzwz.api.KingDeeApi;
import com.nzwz.api.KingDeeK3CloudApi;
import com.nzwz.config.ThreadLocalConfig;
import com.nzwz.dao.InitConnectParamMapper;
import com.nzwz.dao.LogMapper;
import com.nzwz.dao.TableRelationMapper;
import com.nzwz.model.InitConnectParam;
import com.nzwz.model.Log;
import com.nzwz.model.TableRelation;
import com.nzwz.quartz.service.IQuartzJobService;
import com.nzwz.serivce.ISynchronizationService;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author Inori
 */
@Service
public class SynchronizationServiceImpl implements ISynchronizationService {

    @Autowired
    private KingDeeApi kingDeeApi;

    @Autowired
    private TableRelationMapper tableRelationMapper;

    @Autowired
    private LogMapper logMapper;

    @Autowired
    private IQuartzJobService quartzJobService;

    @Autowired
    private InitConnectParamMapper initConnectParamMapper;

    private ExecutorService threadPool = new ThreadPoolExecutor(5, 5,
            0L,
            TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>(1024),
            new ThreadFactoryBuilder().setNameFormat("syn-pool-%d").build(),
            new ThreadPoolExecutor.AbortPolicy());


    @Override
    public RestResponse startSynchronization() {
        QuartzJob quartzJob = quartzJobService.getBaseMapper().selectById("1");

        if (StringUtil.isNotNull(quartzJob)) {
            if (quartzJob.getStatus() == 0) {
                quartzJobService.quartzJobResume(new QuartzJobResumeInVo(quartzJob.getJobClassName()));
                return RestResponse.success();
            }
        } else {
            return RestResponse.fail("自动同步定时任务不存在！");
        }

        return RestResponse.fail("自动同步已经启动，请勿重复启动！");
    }

    @Override
    public RestResponse turnOffSynchronization() {
        QuartzJob quartzJob = quartzJobService.getBaseMapper().selectById("1");

        if (StringUtil.isNotNull(quartzJob)) {
            if (quartzJob.getStatus() == 1) {
                quartzJobService.quartzJobPause(new QuartzJobPauseInVo(quartzJob.getJobClassName()));
                return RestResponse.success();
            }
        } else {
            return RestResponse.fail("自动同步定时任务不存在！");
        }

        return RestResponse.fail("自动同步已经关闭，请勿重复关闭！");
    }

    @Override
    public InitConnectParmOutVo initConnectParam() {
        return initConnectParamMapper.initConnectParam();
    }

    @Override
    public RestResponse initConnectParamCreate(InitConnectParmCreateInVo inVo) {
        QuartzJob quartzJob = quartzJobService.getBaseMapper().selectById("1");

        if (StringUtil.isNotNull(quartzJob)) {
            if (quartzJob.getStatus() == 1) {
                return RestResponse.fail("请先关闭同步再保存!");
            }
        } else {
            return RestResponse.fail("自动同步定时任务不存在！");
        }

        KingDeeK3CloudApi kingDeeK3CloudApi = new KingDeeK3CloudApi(inVo.getK3CloudUrl());
        Boolean flag;
        try {
            flag = kingDeeK3CloudApi.login(inVo.getDbid(), inVo.getUid(), inVo.getPwd(), inVo.getLang());
        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
        }

        kingDeeApi.setFlag(flag);
        kingDeeApi.setKingDeeK3CloudApi(kingDeeK3CloudApi);

        if (!flag) {
            return RestResponse.fail("参数错误，金蝶登陆失败！");
        }

        InitConnectParam initConnectParam = new InitConnectParam();
        initConnectParam.setId(1);
        initConnectParam.setK3CloudUrl(inVo.getK3CloudUrl());
        initConnectParam.setDbid(inVo.getDbid());
        initConnectParam.setLang(inVo.getLang());
        initConnectParam.setUid(inVo.getUid());
        initConnectParam.setPwd(inVo.getPwd());
        initConnectParamMapper.updateById(initConnectParam);

        return RestResponse.success();
    }

    @Override
    public RestResponse manualSynchronization() {
        QuartzJob quartzJob = quartzJobService.getBaseMapper().selectById("1");

        if (StringUtil.isNotNull(quartzJob)) {
            if (quartzJob.getStatus() == 1) {
                return RestResponse.fail("请先关闭同步再开启!");
            }
        } else {
            return RestResponse.fail("自动同步定时任务不存在！");
        }

        threadPool.execute(this::synchronization);
        return RestResponse.success();
    }

    @Override
    public RestResponse abnormalSynchronization(AbnormalSynchronizationInVo inVo) {
        Log log = logMapper.selectLogById(inVo.getId());

        List<Object> tempList = JsonUtil.toList(log.getData(), Object.class);
        for (Object object : tempList) {
            Map<String, Object> data = JsonUtil.toMap(JsonUtil.toString(object), String.class, Object.class);
            this.reload(data, log);
        }

        return RestResponse.success();
    }

    @Override
    public void synchronization() {
        List<TableRelation> tableRelationList = tableRelationMapper.selectList(new QueryWrapper<>());

        List<TableRelation> tempList = tableRelationList.stream().filter(m -> StringUtil.isNotBlank(m.getTableDate())).collect(Collectors.toList());
        for (TableRelation tableRelation : tempList) {
            Map<String, Object> data = this.assemblyMap(tableRelation);
            Log log = new Log(tableRelation.getTableName(), tableRelation.getTableHeader(), JsonUtil.toString(Collections.singletonList(data)));
            logMapper.insert(log);
            String result = this.reload(data, log);

            List<TableRelation> subTableRelationList = new ArrayList<>();
            this.getTableRelationTree(subTableRelationList, tableRelation.getTableHeader(), tableRelationList);
            if (!CollectionUtils.isEmpty(subTableRelationList)) {
                for (TableRelation subTableRelation : subTableRelationList) {
                    Map<String, Object> dataMap = this.assemblyMap(subTableRelation);
                    dataMap.put("DATE_FORM", data.get("DATE_FORM"));
                    dataMap.put("DATE_TO", data.get("DATE_TO"));

                    Log tempLog = new Log(subTableRelation.getTableName(), subTableRelation.getTableHeader(), JsonUtil.toString(Collections.singletonList(dataMap)));
                    logMapper.insert(tempLog);

                    Map<String, Object> tempMap = JsonUtil.toMap(result, String.class, Object.class);
                    if (!CollectionUtils.isEmpty(tempMap)) {
                        String flagName = "DNResult";
                        if (tempMap.containsKey(flagName)) {
                            if (Constants.ROOT_MENU_CODE.equals(tempMap.get(flagName))) {
                                logMapper.updateById(new Log(tempLog.getId(), 0, "未查询到数据", 0));
                                continue;
                            }
                        }
                    }
                    this.reload(dataMap, tempLog);
                }
            }
        }
    }

    private void getTableRelationTree(List<TableRelation> result, String tableHeader, List<TableRelation> tableRelationList) {
        List<TableRelation> tempList = tableRelationList.stream()
                .filter(m -> tableHeader.equals(m.getSubTableHeader()))
                .sorted(Comparator.comparing(TableRelation::getSortOrder))
                .collect(Collectors.toList());

        for (TableRelation tableRelation : tempList) {
            result.add(tableRelation);
            this.getTableRelationTree(result, tableRelation.getTableHeader(), tableRelationList);
        }
    }

    private Map<String, Object> assemblyMap(TableRelation tableRelation) {
        Map<String, Object> data = new HashMap<>(6);
        data.put("TABLE_HEADER", tableRelation.getTableHeader());
        data.put("TABLE_DATE", tableRelation.getTableDate());
        data.put("SUB_TABLE_HEADER", tableRelation.getSubTableHeader());
        data.put("PRIMARY_KEY", tableRelation.getPrimaryKey());
        data.put("FOREIGN_KEY", tableRelation.getForeignKey());
        data.put("IS_TABLE_HEADER", tableRelation.getForeignKey());

        if (StringUtil.isNotBlank(tableRelation.getTableDate())) {
            String dateForm = logMapper.selectOrderCreateTimeByContent(tableRelation.getTableHeader());
            if (StringUtil.isNotBlank(dateForm)) {
                data.put("DATE_FORM", dateForm);
            } else {
                data.put("DATE_FORM", "2020-01-01 00:00:00");
            }
            data.put("DATE_TO", DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        }
        return data;
    }

    private String reload(Map<String, Object> data, Log log) {
        System.out.println("金蝶请求参数: " + data);

        String result = this.send(Collections.singletonList(data), 3);

        System.out.println("金蝶返回值: " + result);

        Map<String, Object> tempMap = ThreadLocalConfig.get();
        int costTime = StringUtil.isNotNull(tempMap.get("costTime")) ? Integer.parseInt(tempMap.get("costTime").toString()) : 0;

        String flagName = "DNResult";
        Map<String, Object> map = JsonUtil.toMap(result, String.class, Object.class);
        if (!CollectionUtils.isEmpty(map)) {
            if (KingDeeUtil.isError(result)) {
                logMapper.updateById(new Log(log.getId(), 0, result, costTime));
            } else {
                if (map.containsKey(flagName)) {
                    if (Constants.SYN_SUCCESS_CODE.equals(String.valueOf(map.get(flagName)))) {
                        logMapper.updateById(new Log(log.getId(), 1, result, costTime));
                    } else if (Constants.ROOT_MENU_CODE.equals(String.valueOf(map.get(flagName)))) {
                        logMapper.updateById(new Log(log.getId(), 0, "未查询到数据", 0));
                    } else {
                        logMapper.updateById(new Log(log.getId(), 0, result, costTime));
                    }
                } else {
                    logMapper.updateById(new Log(log.getId(), 0, result, costTime));
                }
            }
        } else {
            logMapper.updateById(new Log(log.getId(), 0, result, costTime));
        }
        return result;
    }

    private String send(List<Map<String, Object>> data, Integer index) {
        String result;
        try {
            result = kingDeeApi.synchronization(data);
        } catch (Exception e) {
            if (index != 0) {
                result = this.send(data, index - 1);
            } else {
                return "金蝶服务器地址异常";
            }
        }
        return result;
    }


}