package com.artfess.cqlt.manager.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.artfess.base.enums.ReportTypeEnum;
import com.artfess.base.enums.SubjectTypeEnum;
import com.artfess.base.exception.BaseException;
import com.artfess.base.manager.impl.BaseManagerImpl;
import com.artfess.base.util.BeanUtils;
import com.artfess.cqlt.dao.QfFinancePlBudgetMDao;
import com.artfess.cqlt.manager.QfEnterpriseInfoManager;
import com.artfess.cqlt.manager.QfFinancePlBudgetDManager;
import com.artfess.cqlt.manager.QfFinancePlBudgetMManager;
import com.artfess.cqlt.manager.QfFinancePlBudgetSManager;
import com.artfess.cqlt.manager.QfFinancialStatisticalManager;
import com.artfess.cqlt.manager.QfReportLogManager;
import com.artfess.cqlt.manager.QfSubjectInternationalInfoManager;
import com.artfess.cqlt.model.QfEnterpriseInfo;
import com.artfess.cqlt.model.QfFinancePlBudgetD;
import com.artfess.cqlt.model.QfFinancePlBudgetM;
import com.artfess.cqlt.model.QfFinancePlBudgetS;
import com.artfess.cqlt.model.QfFinancePlD;
import com.artfess.cqlt.model.QfFinancePlM;
import com.artfess.cqlt.model.QfFinancePlS;
import com.artfess.cqlt.model.QfSubjectInternationalInfo;
import com.artfess.cqlt.vo.DataInfoVo;
import com.artfess.cqlt.vo.ReportVo;
import com.artfess.i18n.util.I18nUtil;
import com.artfess.poi.util.HeaderNode;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.api.client.util.Sets;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * 财务--利润预算填报主表 服务实现类
 *
 * @author min.wu
 * @company 阿特菲斯信息技术有限公司
 * @since 2023-03-02
 */
@Slf4j
@Service
public class QfFinancePlBudgetMManagerImpl extends BaseManagerImpl<QfFinancePlBudgetMDao, QfFinancePlBudgetM> implements QfFinancePlBudgetMManager {

    @Autowired
    private QfFinancePlBudgetDManager financePlBudgetDManager;

    @Autowired
    private QfFinancePlBudgetSManager financePlBudgetSManager;

    @Autowired
    private QfSubjectInternationalInfoManager subjectInternationalInfoManager;

    @Autowired
    private QfEnterpriseInfoManager enterpriseInfoManager;

    @Autowired
    private QfFinancialStatisticalManager qfFinancialStatisticalManager;

    @Autowired
    private QfReportLogManager reportLogManager;

    @Resource(name = "bmpExecutorService")
    private ExecutorService executorService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insertInfo(QfFinancePlBudgetM t) {
        QueryWrapper<QfFinancePlBudgetM> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("fill_year_", t.getFillYear());
        queryWrapper.eq("fill_month_", t.getFillMonth());
        queryWrapper.eq("report_id_", t.getReportId());
        List<QfFinancePlBudgetM> QfFinancePlBudgetMS = this.baseMapper.selectList(queryWrapper);
        if (!CollectionUtils.isEmpty(QfFinancePlBudgetMS)) {
            throw new BaseException(I18nUtil.getMessage("QfOperationKpiM.repeat", LocaleContextHolder.getLocale()));
        }
        int insert = this.baseMapper.insert(t);
        if (insert > 0) {
            return true;
        }
        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateInfo(QfFinancePlBudgetM t) {
        QueryWrapper<QfFinancePlBudgetM> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("fill_year_", t.getFillYear());
        queryWrapper.eq("fill_month_", t.getFillMonth());
        queryWrapper.eq("report_id_", t.getReportId());
        queryWrapper.ne("id_", t.getId());
        List<QfFinancePlBudgetM> QfFinancePlBudgetMS = this.baseMapper.selectList(queryWrapper);
        if (!CollectionUtils.isEmpty(QfFinancePlBudgetMS)) {
            throw new BaseException(I18nUtil.getMessage("QfOperationKpiM.repeat", LocaleContextHolder.getLocale()));
        }
        int insert = this.baseMapper.updateById(t);
        if (insert > 0) {
            return true;
        }
        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateStatus(QfFinancePlBudgetM t) {
        QfFinancePlBudgetM QfFinancePlBudgetM = baseMapper.selectById(t.getId());
        if (null == QfFinancePlBudgetM) {
            return false;
        }
        QfFinancePlBudgetM.setStatus(QfFinancePlBudgetM.getStatus() == 0 ? 1 : 0);
        int i = this.baseMapper.updateById(QfFinancePlBudgetM);
        if (i > 0) {
            return true;
        }
        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean importExcel(List<HeaderNode> list, String mainId) {
        if (CollectionUtils.isEmpty(list)) {
            return false;
        }
        QfFinancePlBudgetM mainInfo = this.baseMapper.selectById(mainId);
        Assert.notNull(mainInfo, I18nUtil.getMessage("filldata.notExist", LocaleContextHolder.getLocale()));
        Assert.isTrue(!"1".equals(mainInfo.getStatus()),  I18nUtil.getMessage("data_operate", LocaleContextHolder.getLocale()));
        this.removeInfo(mainId);

        //获取上个月的利润信息
        Map<String, List<QfFinancePlBudgetD>> historyMap = this.historyList(mainInfo);
        //获取财务相关国际科目
        Map<String, QfSubjectInternationalInfo> subjectMap = subjectInternationalInfoManager.getSubjectCodeMap("PL");
        //获取境外企业
        Map<String, QfEnterpriseInfo> enterpriseInfoMap = enterpriseInfoManager.getEnterpriseInfoMap(null);

        Set<String> noexitSubjectCodes = Sets.newHashSet();
        List<HeaderNode> headerNodeList = list.stream().collect(Collectors.groupingBy(HeaderNode::getRow)).get(0);
        Map<Integer, String> headerMap = Maps.newHashMap();
        for (HeaderNode headerNode : headerNodeList) {
            headerMap.put(headerNode.getColumn(), headerNode.getHeaderName());
        }

        //获取科目所在行 以及科目编码信息
        Map<Integer, List<HeaderNode>> detailMap = list.stream().collect(Collectors.groupingBy(HeaderNode::getColumn));
        List<HeaderNode> subjectCodes = detailMap.get(1);
        Map<Integer, String> subjectCodeMap = Maps.newHashMap();
        Map<String, Integer> subjectCodeRowMap =Maps.newHashMap();
        for (HeaderNode headerNode : subjectCodes) {
            subjectCodeMap.put(headerNode.getRow(), headerNode.getHeaderName());
            subjectCodeRowMap.put(headerNode.getHeaderName(),headerNode.getRow());
        }
        Set<String> subjectCodeList = Sets.newHashSet();
        int maxColumn = detailMap.size();
        List<QfFinancePlBudgetD> detailList = Lists.newArrayList();
        JSONObject totalInfo = new JSONObject();
        for (Map.Entry<Integer, List<HeaderNode>> entry : detailMap.entrySet()) {
            Integer column = entry.getKey();
            List<HeaderNode> nodes = entry.getValue();
            //获取企业编码
            String enterpriseCode = headerMap.get(column);
            List<QfFinancePlBudgetD> historyDetailList = Lists.newArrayList();
            if (historyMap.containsKey(enterpriseCode)) {
                historyDetailList = historyMap.get(enterpriseCode);
            }

            for (HeaderNode node : nodes) {
                if (0 == node.getRow()) {
                    continue;
                }
                String subjectCode = subjectCodeMap.get(node.getRow());
                subjectCodeList.add(subjectCode);
                if (node.getColumn() > 1 && node.getColumn() < maxColumn - 3 && !StringUtils.isEmpty(node.getHeaderName())) {
                    createDetail(mainId, mainInfo, subjectMap, enterpriseInfoMap, noexitSubjectCodes, subjectCodeMap, detailList, enterpriseCode, historyDetailList, node);
                } else if (node.getColumn() >= maxColumn - 3 && !StringUtils.isEmpty(node.getHeaderName())) {
                    if (node.getColumn() == maxColumn - 1) {
                        totalInfo.put(subjectCode + ":budgetConsolidated", node.getHeaderName());
                    } else if (node.getColumn() == maxColumn - 2) {
                        totalInfo.put(subjectCode + ":budgetPosting", node.getHeaderName());
                    } else if (node.getColumn() == maxColumn - 3) {
                        totalInfo.put(subjectCode + ":budgetTotal", node.getHeaderName());
                    }
                }

            }
        }
        if (!CollectionUtils.isEmpty(noexitSubjectCodes)) {
// todo           throw new BaseException(noexitSubjectCodes.toString() + I18nUtil.getMessage("code.notExist", LocaleContextHolder.getLocale()));
        }
        List<QfFinancePlBudgetS> totalList = getQfFinancePlBudgetS(mainInfo, subjectMap, new ArrayList<>(subjectCodeList), totalInfo,subjectCodeRowMap);
        financePlBudgetSManager.saveBatch(totalList);
        try {
            partitionSave(detailList);
        } catch (Exception e) {
            log.error("导入例如报表数据失败:{}", e.getMessage());
            return false;
        }

        asyncReportInfo(mainInfo, detailList, totalList);
        return true;
    }

    private void asyncReportInfo(QfFinancePlBudgetM mainInfo, List<QfFinancePlBudgetD> list, List<QfFinancePlBudgetS> totalList) {
        executorService.execute(() -> {
            String msg = null;
            String reportStatus = "0";
            try {
                saveFinancialVo(mainInfo, list, totalList);
                reportStatus = "1";
            } catch (Exception e) {
                msg = e.getMessage();
                log.error("财务大屏报表实际数据生成失败:{}", e.getMessage());
            }
            reportLogManager.saveReportInfo(reportStatus, msg, ReportTypeEnum.PL_BUDGET.getType(), mainInfo.getFillYear(), mainInfo.getFillMonth());
        });
    }

    private void partitionSave(List<QfFinancePlBudgetD> detailList) throws InterruptedException {
        List<List<QfFinancePlBudgetD>> partition = BeanUtils.partition(detailList, detailList.size()/10);
        // 创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(partition.size());
        // 声明线程计数器 记录单个任务的执行次数
        CountDownLatch countDownLatch = new CountDownLatch(partition.size());
        // 遍历处理拆分的list数据
        for (int i = 0; i < partition.size(); i++) {
            int finalI = i;
            executorService.execute(() -> {
                // 业务处理部分
                List<QfFinancePlBudgetD> importParamDTOList = partition.get(finalI);
                financePlBudgetDManager.saveOrUpdateBatch(importParamDTOList);
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        //关闭线程池
        executorService.shutdown();
    }
    private void saveFinancialVo(QfFinancePlBudgetM mainInfo, List<QfFinancePlBudgetD> detailList, List<QfFinancePlBudgetS> totalList) {
        //获取上个月的利润信息
        Map<String, QfFinancePlBudgetS> historyMap = this.historyTotalList(mainInfo);
        QfEnterpriseInfo group = enterpriseInfoManager.getGroup();
        List<ReportVo> financialVoList = Lists.newArrayList();
        DataInfoVo dataInfoVo = new DataInfoVo();
        dataInfoVo.setYear(mainInfo.getFillYear());
        dataInfoVo.setMonth(mainInfo.getFillMonth());
        dataInfoVo.setQuarter(mainInfo.getFillQuarter());
        detailList.forEach(detail -> {
            ReportVo financialVo = new ReportVo();
            financialVo.setEnterpriseCode(detail.getEnterpriseCode());
            financialVo.setBudget(detail.getBudget());
            financialVo.setBudgetYtd(detail.getBudgetYtd());
            financialVo.setSubjectCode(detail.getSubjectCode());
            financialVoList.add(financialVo);
        });

        totalList.forEach(detail->{
            ReportVo financialVo = new ReportVo();
            financialVo.setEnterpriseCode(group.getCode());
            financialVo.setBudgetYtd(detail.getBudgetConsolidated());
            if(historyMap.containsKey(detail.getSubjectCode())) {
                QfFinancePlBudgetS plS = historyMap.get(detail.getSubjectCode());
                if(null == financialVo.getBudgetYtd()) {
                    financialVo.setBudgetYtd(BigDecimal.ZERO);
                }
                if(null == plS || null == plS.getBudgetConsolidated()) {
                    financialVo.setBudget(financialVo.getBudgetYtd());
                }else {
                    financialVo.setBudget(financialVo.getBudgetYtd().subtract(historyMap.get(detail.getSubjectCode()).getBudgetConsolidated()));
                }
            }else{
                financialVo.setBudget(financialVo.getBudgetYtd());
            }
            financialVo.setSubjectCode(detail.getSubjectCode());
            financialVoList.add(financialVo);
        });

        qfFinancialStatisticalManager.saveData(financialVoList, dataInfoVo, 2);
    }

    private List<QfFinancePlBudgetS> getQfFinancePlBudgetS(QfFinancePlBudgetM mainInfo, Map<String, QfSubjectInternationalInfo> subjectMap,
                                                           List<String> subjectCodeList, JSONObject totalInfo,Map<String, Integer> subjectCodeRowMap) {
        List<QfFinancePlBudgetS> totalList = Lists.newArrayList();
        subjectCodeList.forEach(subjectCode -> {
            QfFinancePlBudgetS plS = new QfFinancePlBudgetS();
            plS.setMainId(mainInfo.getId());
            QfSubjectInternationalInfo subjectInternationalInfo = subjectMap.get(subjectCode);
            if(null != subjectInternationalInfo){
                //处理课目
                plS.setSubjectNameEn(subjectInternationalInfo.getNameEn());
                plS.setSubjectUnit(subjectInternationalInfo.getUnit());
                plS.setSubjectName(subjectInternationalInfo.getName());
            }

            if (totalInfo.containsKey(subjectCode + ":budgetConsolidated")) {
                plS.setBudgetConsolidated(totalInfo.getBigDecimal(subjectCode + ":budgetConsolidated"));
            }
            if (totalInfo.containsKey(subjectCode + ":budgetPosting")) {
                plS.setBudgetPosting(totalInfo.getBigDecimal(subjectCode + ":budgetPosting"));
            }
            if (totalInfo.containsKey(subjectCode + ":budgetTotal")) {
                plS.setBudgetTotal(totalInfo.getBigDecimal(subjectCode + ":budgetTotal"));
            }

            if (null != subjectInternationalInfo && subjectInternationalInfo.getType().equals(SubjectTypeEnum.CBFY.getType())) {
                if(null != plS.getBudgetConsolidated()){
                    plS.setBudgetConsolidated(plS.getBudgetConsolidated().multiply(new BigDecimal(-1)));
                }
                if(null != plS.getBudgetPosting()){
                    plS.setBudgetPosting(plS.getBudgetPosting().multiply(new BigDecimal(-1)));
                }
                if(null != plS.getBudgetTotal()){
                    plS.setBudgetTotal(plS.getBudgetTotal().multiply(new BigDecimal(-1)));
                }
            }
            plS.setFillYear(mainInfo.getFillYear());
            plS.setFillMonth(mainInfo.getFillMonth());
            plS.setFillDate(mainInfo.getFillDate());
            plS.setFillQuarter(mainInfo.getFillQuarter());
            plS.setSubjectCode(subjectCode);
            plS.setSn(subjectCodeRowMap.get(subjectCode));
            totalList.add(plS);

        });
        return totalList;
    }

    private void createDetail(String mainId, QfFinancePlBudgetM mainInfo, Map<String, QfSubjectInternationalInfo> subjectMap, Map<String, QfEnterpriseInfo> enterpriseInfoMap, Set<String> noexitSubjectCodes, Map<Integer, String> subjectCodeMap, List<QfFinancePlBudgetD> detailList, String enterpriseCode, List<QfFinancePlBudgetD> historyDetailList, HeaderNode node) {
        QfFinancePlBudgetD financePlBudgetD = new QfFinancePlBudgetD();
        financePlBudgetD.setMainId(mainId);
        financePlBudgetD.setSn(node.getRow());
        financePlBudgetD.setColumn(node.getColumn());
        financePlBudgetD.setFillDate(mainInfo.getFillDate());
        financePlBudgetD.setEnterpriseCode(enterpriseCode);
        financePlBudgetD.setSubjectCode(subjectCodeMap.get(node.getRow()));
        //处理课目
        if (!subjectMap.containsKey(financePlBudgetD.getSubjectCode())) {
            noexitSubjectCodes.add(financePlBudgetD.getSubjectCode());
        } else {
            QfSubjectInternationalInfo subjectInternationalInfo = subjectMap.get(financePlBudgetD.getSubjectCode());
            financePlBudgetD.setSubjectNameEn(subjectInternationalInfo.getNameEn());
            financePlBudgetD.setSubjectUnit(subjectInternationalInfo.getUnit());
            financePlBudgetD.setSubjectName(subjectInternationalInfo.getName());
            if(!StringUtils.isEmpty(subjectInternationalInfo.getLevel())) {
                financePlBudgetD.setSubjectLevel(Integer.parseInt(subjectInternationalInfo.getLevel()));
            }
            //利润表 数据值 需要转换正负数 正数转负数 负数转正数
            if(subjectInternationalInfo.getType().equals(SubjectTypeEnum.CBFY.getType())) {
                financePlBudgetD.setBudgetYtd(new BigDecimal(node.getHeaderName()).multiply(new BigDecimal(-1)));
            }else{
                financePlBudgetD.setBudgetYtd(new BigDecimal(node.getHeaderName()));
            }
        }
        //  TODO  当年实际值
        Map<String, QfFinancePlBudgetD> historyDetailMap = historyDetailList.stream().collect(Collectors.toMap(item -> item.getSubjectCode(), item -> item));
        if (historyDetailMap.containsKey(financePlBudgetD.getSubjectCode())) {
            QfFinancePlBudgetD historyDetail = historyDetailMap.get(financePlBudgetD.getSubjectCode());
            if(null == financePlBudgetD.getBudgetYtd()) {
                financePlBudgetD.setBudgetYtd(BigDecimal.ZERO);
            }
            financePlBudgetD.setBudget(financePlBudgetD.getBudgetYtd().subtract(historyDetail.getBudgetYtd()));
        } else {
            financePlBudgetD.setBudget(financePlBudgetD.getBudgetYtd());
        }

        //处理企业
        if (enterpriseInfoMap.containsKey(financePlBudgetD.getEnterpriseCode())) {
            QfEnterpriseInfo qfEnterpriseInfo = enterpriseInfoMap.get(financePlBudgetD.getEnterpriseCode());
            financePlBudgetD.setEnterpriseName(qfEnterpriseInfo.getName());
            financePlBudgetD.setEnterpriseNameEn(qfEnterpriseInfo.getNameEn());
        }
        detailList.add(financePlBudgetD);
    }

    private void removeInfo(String mainId) {
        QueryWrapper<QfFinancePlBudgetD> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("main_id_", mainId);
        financePlBudgetDManager.remove(queryWrapper);

        QueryWrapper<QfFinancePlBudgetS> query = new QueryWrapper<>();
        query.eq("main_id_", mainId);
        financePlBudgetSManager.remove(query);
    }

    private Map<String, List<QfFinancePlBudgetD>> historyList(QfFinancePlBudgetM QfFinancePlBudgetM) {
        //获取上个月的累计销售额数据
        List<QfFinancePlBudgetD> historyList = this.baseMapper.historyList(QfFinancePlBudgetM.getFillMonth() - 1, QfFinancePlBudgetM.getFillYear());

        if (CollectionUtils.isEmpty(historyList)) {
            return Maps.newHashMap();
        }
        return historyList.stream().collect(Collectors.groupingBy(QfFinancePlBudgetD::getEnterpriseCode));
    }

    private Map<String, QfFinancePlBudgetS> historyTotalList(QfFinancePlBudgetM qfFinancePlBudgetM) {
        //获取上个月的累计销售额数据
        List<QfFinancePlBudgetS> historyList = this.baseMapper.historyTotalList(qfFinancePlBudgetM.getFillMonth() - 1, qfFinancePlBudgetM.getFillYear());

        if (CollectionUtils.isEmpty(historyList)) {
            return Maps.newHashMap();
        }
        return historyList.stream().collect(Collectors.toMap(item -> item.getSubjectCode(), item -> item));
    }
}
