package com.artfess.cqlt.manager.impl;

import com.alibaba.fastjson.JSONObject;
import com.artfess.base.enums.DelStatusEnum;
import com.artfess.base.enums.ReportTypeEnum;
import com.artfess.base.manager.impl.BaseManagerImpl;
import com.artfess.base.util.StringUtil;
import com.artfess.cqlt.dao.QfEnterpriseInfoDao;
import com.artfess.cqlt.dao.QfFinanceCfDDao;
import com.artfess.cqlt.dao.QfFinanceCfMDao;
import com.artfess.cqlt.manager.QfEnterpriseInfoManager;
import com.artfess.cqlt.manager.QfFinanceCfDManager;
import com.artfess.cqlt.manager.QfFinanceCfSManager;
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.QfFinanceCfBudgetD;
import com.artfess.cqlt.model.QfFinanceCfBudgetM;
import com.artfess.cqlt.model.QfFinanceCfBudgetS;
import com.artfess.cqlt.model.QfFinanceCfD;
import com.artfess.cqlt.model.QfFinanceCfM;
import com.artfess.cqlt.model.QfFinanceCfS;
import com.artfess.cqlt.model.QfSubjectInternationalInfo;
import com.artfess.cqlt.vo.DataInfoVo;
import com.artfess.cqlt.vo.ReportDataVo;
import com.artfess.cqlt.vo.ReportVo;
import com.artfess.i18n.util.I18nUtil;
import com.artfess.poi.util.CustomHeader;
import com.artfess.poi.util.HeaderNode;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.api.client.util.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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * 财务-现金流量填报详情表 服务实现类
 *
 * @company 阿特菲斯信息技术有限公司
 * @author min.wu
 * @since 2023-03-02
 */
@Slf4j
@Service
public class QfFinanceCfDManagerImpl extends BaseManagerImpl<QfFinanceCfDDao, QfFinanceCfD> implements QfFinanceCfDManager {

    @Resource
    private QfFinanceCfMDao mainDao;

    @Autowired
    private QfFinanceCfSManager sumManager;

    @Resource
    private QfSubjectInternationalInfoManager subjectInternationalInfoManager;

    @Resource
    private QfEnterpriseInfoDao enterpriseInfoDao;

    @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 batchUpdate(QfFinanceCfM t) {
        Assert.hasText(t.getId(), I18nUtil.getMessage("QfOperationKpiM.reportId", LocaleContextHolder.getLocale()));
        QfFinanceCfM financeCfM = mainDao.selectById(t.getId());
        Assert.notNull(financeCfM, I18nUtil.getMessage("filldata.notExist", LocaleContextHolder.getLocale()));
        Assert.isTrue(!"1".equals(financeCfM.getStatus()),  I18nUtil.getMessage("data_operate", LocaleContextHolder.getLocale()));
        List<QfFinanceCfD> list = Lists.newArrayList();
        List<QfFinanceCfS> totalList = Lists.newArrayList();
        Map<String, QfSubjectInternationalInfo> subjectMap = subjectInternationalInfoManager.getSubjectCodeMap("PL");
        QueryWrapper<QfEnterpriseInfo> enterpriseInfoQuery = new QueryWrapper<>();
        enterpriseInfoQuery.eq("IS_DELE_", DelStatusEnum.N.getType());
        List<QfEnterpriseInfo> enterpriseInfoList = enterpriseInfoDao.selectList(enterpriseInfoQuery);
        Map<String, QfEnterpriseInfo> enterpriseInfoMap = enterpriseInfoList.stream().collect(Collectors.toMap(item -> item.getCode(), item -> item));
        t.getList().forEach(detail -> {
            detail.setFillDate(financeCfM.getFillDate());
            detail.setMainId(t.getId());
            QueryWrapper<QfFinanceCfD> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("subject_code_", detail.getSubjectCode());
            queryWrapper.eq("main_id_", detail.getMainId());
            queryWrapper.eq("enterprise_code_", detail.getEnterpriseCode());
            QfFinanceCfD QfFinanceCfD = this.baseMapper.selectOne(queryWrapper);
            if (null != QfFinanceCfD) {
                QfFinanceCfD.setActualYtd(detail.getActualYtd());
                list.add(QfFinanceCfD);
            } else {
                QfSubjectInternationalInfo subjectInternationalInfo = subjectMap.get(detail.getSubjectCode());
                detail.setSubjectNameEn(subjectInternationalInfo.getNameEn());
                detail.setSubjectUnit(subjectInternationalInfo.getUnit());
                detail.setSubjectName(subjectInternationalInfo.getName());
                if (!StringUtils.isEmpty(subjectInternationalInfo.getLevel())) {
                    detail.setSubjectLevel(Integer.parseInt(subjectInternationalInfo.getLevel()));
                }
                //处理企业
                if (enterpriseInfoMap.containsKey(detail.getEnterpriseCode())) {
                    QfEnterpriseInfo qfEnterpriseInfo = enterpriseInfoMap.get(detail.getEnterpriseCode());
                    detail.setEnterpriseName(qfEnterpriseInfo.getName());
                    detail.setEnterpriseNameEn(qfEnterpriseInfo.getNameEn());
                }
                list.add(detail);
            }
        });

        t.getTotalList().forEach(detail -> {
            QueryWrapper<QfFinanceCfS> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("subject_code_", detail.getSubjectCode());
            queryWrapper.eq("main_id_", t.getId());
            QfFinanceCfS financePlActualS = sumManager.getBaseMapper().selectOne(queryWrapper);
            if (null != financePlActualS) {
                if(null != detail.getActualTotal()) {
                    financePlActualS.setActualTotal(detail.getActualTotal());
                }
                if(null != detail.getActualPosting()) {
                    financePlActualS.setActualPosting(detail.getActualPosting());
                }
                if(null != detail.getActualConsolidated()) {
                    financePlActualS.setActualConsolidated(detail.getActualConsolidated());
                }
                totalList.add(financePlActualS);
            } else {
                detail.setFillDate(financeCfM.getFillDate());
                detail.setFillQuarter(financeCfM.getFillQuarter());
                detail.setFillMonth(financeCfM.getFillMonth());
                detail.setFillYear(financeCfM.getFillYear());
                detail.setMainId(t.getId());
                QfSubjectInternationalInfo subjectInternationalInfo = subjectMap.get(detail.getSubjectCode());
                detail.setSubjectNameEn(subjectInternationalInfo.getNameEn());
                detail.setSubjectUnit(subjectInternationalInfo.getUnit());
                detail.setSubjectName(subjectInternationalInfo.getName());
                totalList.add(detail);
            }
        });
        if (!CollectionUtils.isEmpty(list)) {
            this.saveOrUpdateBatch(list);
        }
        if (!CollectionUtils.isEmpty(totalList)) {
            sumManager.saveOrUpdateBatch(totalList);
        }
        asyncReportInfo(financeCfM, list, totalList);
        return true;
    }

    private void asyncReportInfo(QfFinanceCfM mainInfo, List<QfFinanceCfD> list, List<QfFinanceCfS> 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.CF_ACTUAL.getType(), mainInfo.getFillYear(), mainInfo.getFillMonth());
        });
    }

    private void saveFinancialVo(QfFinanceCfM mainInfo, List<QfFinanceCfD> detailList, List<QfFinanceCfS> totalList) {
        QfEnterpriseInfo group = enterpriseInfoManager.getGroup();
        //获取上个月的利润信息
        Map<String, QfFinanceCfS> historyMap = this.historyTotalList(mainInfo);
        List<ReportVo> financialVoList = com.google.common.collect.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.setActual(detail.getActual());
            financialVo.setActualYtd(detail.getActualYtd());
            financialVo.setSubjectCode(detail.getSubjectCode());
            financialVoList.add(financialVo);
        });
        totalList.forEach(detail->{
            ReportVo financialVo = new ReportVo();
            financialVo.setEnterpriseCode(group.getCode());
            financialVo.setActualYtd(detail.getActualConsolidated());
            if(historyMap.containsKey(detail.getSubjectCode())) {
                QfFinanceCfS plS = historyMap.get(detail.getSubjectCode());
                if(null == financialVo.getActualYtd()) {
                    financialVo.setActualYtd(BigDecimal.ZERO);
                }
                if(null == plS || null == plS.getActualConsolidated()) {
                    financialVo.setActual(financialVo.getActualYtd());
                }else {
                    financialVo.setActual(financialVo.getActualYtd().subtract(historyMap.get(detail.getSubjectCode()).getActualConsolidated()));
                }
            }else{
                financialVo.setActual(financialVo.getActualYtd());
            }
            financialVo.setSubjectCode(detail.getSubjectCode());
            financialVoList.add(financialVo);
        });

        qfFinancialStatisticalManager.saveData(financialVoList, dataInfoVo, 1);
    }

    @Override
    public void export(HttpServletRequest request, HttpServletResponse response, List<QfFinanceCfD> list, String sheetName) throws IOException {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        String mainId = list.get(0).getMainId();
        QueryWrapper<QfFinanceCfS> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("main_id_", mainId);
        List<QfFinanceCfS> financePlSList = sumManager.getBaseMapper().selectList(queryWrapper);

        List<String> enterpriseCodeHeader = this.baseMapper.getEnterpriseCodeHeader(mainId);

        List<String> subjectCodes = this.baseMapper.getSubjectCodes(mainId);
        Map<String, Integer> rowMap = Maps.newHashMap();
        AtomicReference<Integer> row = new AtomicReference<>(0);
        subjectCodes.forEach(subjectCode -> {
            row.getAndSet(row.get() + 1);
            rowMap.put(subjectCode, row.get());
        });
        Map<String, Integer> columMap = Maps.newHashMap();
        List<HeaderNode> headerNodeList = Lists.newArrayList();
        AtomicReference<Integer> column = new AtomicReference<>(1);
        enterpriseCodeHeader.forEach(code -> {
            column.getAndSet(column.get() + 1);
            HeaderNode headerNode = new HeaderNode();
            headerNode.setRow(0);
            headerNode.setColumn(column.get());
            headerNode.setHeaderName(code);
            headerNodeList.add(headerNode);
            columMap.put(code, headerNode.getColumn());
        });

        HeaderNode headerNode = new HeaderNode();
        headerNode.setRow(0);
        headerNode.setColumn(0);
        headerNode.setHeaderName("subjectName");
        headerNodeList.add(headerNode);

        headerNode = new HeaderNode();
        headerNode.setRow(0);
        headerNode.setColumn(1);
        headerNode.setHeaderName("subjectCode");
        headerNodeList.add(headerNode);

        headerNode = new HeaderNode();
        headerNode.setRow(0);
        headerNode.setColumn(columMap.size() + 1);
        headerNode.setHeaderName("Total");
        headerNodeList.add(headerNode);

        headerNode = new HeaderNode();
        headerNode.setRow(0);
        headerNode.setColumn(columMap.size() + 2);
        headerNode.setHeaderName("Consolidation Posting");
        headerNodeList.add(headerNode);

        headerNode = new HeaderNode();
        headerNode.setRow(0);
        headerNode.setColumn(columMap.size() + 3);
        headerNode.setHeaderName("Consolidated Financial Statement");
        headerNodeList.add(headerNode);

        list.forEach(detail -> {
            HeaderNode node = new HeaderNode();
            node.setRow(rowMap.get(detail.getSubjectCode()));
            node.setColumn(columMap.get(detail.getEnterpriseCode()));
            node.setHeaderName(detail.getActualYtd()+ "");
            headerNodeList.add(node);

            node = new HeaderNode();
            node.setRow(rowMap.get(detail.getSubjectCode()));
            node.setColumn(0);
            String spaceStr = StringUtil.addSpace(detail.getSubjectLevel());
            node.setHeaderName(spaceStr + detail.getSubjectName());
            headerNodeList.add(node);

            node = new HeaderNode();
            node.setRow(rowMap.get(detail.getSubjectCode()));
            node.setColumn(1);
            node.setHeaderName(detail.getSubjectCode());
            headerNodeList.add(node);
        });
        Integer maxColum = columMap.size();
        financePlSList.forEach(pls -> {
            HeaderNode node = new HeaderNode();
            if(null != pls.getActualConsolidated()) {
                node.setRow(rowMap.get(pls.getSubjectCode()));
                node.setColumn(maxColum + 3);
                node.setHeaderName(pls.getActualConsolidated() + "");
                headerNodeList.add(node);
                node = new HeaderNode();
            }

            if(null != pls.getActualPosting()) {
                node.setRow(rowMap.get(pls.getSubjectCode()));
                node.setColumn(maxColum + 2);
                node.setHeaderName(pls.getActualPosting() + "");
                headerNodeList.add(node);
            }

            if(null != pls.getActualTotal()) {
                node = new HeaderNode();
                node.setRow(rowMap.get(pls.getSubjectCode()));
                node.setColumn(maxColum + 1);
                node.setHeaderName(pls.getActualTotal() + "");
                headerNodeList.add(node);
            }
        });

        String date = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        String fileName = String.format(sheetName + "-%s", date);
        CustomHeader.export(headerNodeList, response, fileName, sheetName);

    }

    @Override
    public List<ReportDataVo> getCfDataList(List<String> subjectCodes, Integer fillYear, Integer endMonth) {
        if(CollectionUtils.isEmpty(subjectCodes)) {
            return Lists.newArrayList();
        }
        return this.baseMapper.getCfDataList(subjectCodes, fillYear, endMonth);
    }

    @Override
    public List<JSONObject> detailQuery(List<QfFinanceCfD> list) {
        if (CollectionUtils.isEmpty(list)) {
            return Lists.newArrayList();
        }
        String mainId = list.get(0).getMainId();
        QueryWrapper<QfFinanceCfS> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("main_id_", mainId);
        List<QfFinanceCfS> financePlSList = sumManager.getBaseMapper().selectList(queryWrapper);
        List<String> enterpriseCodeHeader = this.baseMapper.getEnterpriseCodeHeader(mainId);

        List<String> subjectCodes = this.baseMapper.getSubjectCodes(mainId);

        List<JSONObject> detailList = Lists.newArrayList();

        JSONObject detail = new JSONObject(true);
        detail.put("subjectName", "subjectName");
        detail.put("subjectCode", "subjectCode");
        detail.put("subjectUnit", "subjectUnit");
        detail.put("subjectLevel", "subjectLevel");
        enterpriseCodeHeader.forEach(code -> {
            detail.put(code, code);
        });
        detail.put("Total", "Total");
        detail.put("Consolidation Posting", "Consolidation Posting");
        detail.put("Consolidated Financial Statement", "Consolidated Financial Statement");
        detailList.add(detail);
        for (String subjectCode : subjectCodes) {
            JSONObject data = new JSONObject(true);
            data.put("subjectCode", subjectCode);
            for (QfFinanceCfD pld : list) {
                if (pld.getSubjectCode().equals(subjectCode)) {
                    data.put("subjectName", pld.getSubjectName());
                    data.put("subjectUnit", pld.getSubjectUnit());
                    data.put("subjectLevel", pld.getSubjectLevel());
                    data.put(pld.getEnterpriseCode(), pld.getActualYtd());
                }
            }
            for (QfFinanceCfS pls : financePlSList) {
                if (pls.getSubjectCode().equals(subjectCode)) {
                    data.put("Total", pls.getActualTotal());
                    data.put("Consolidation Posting", pls.getActualPosting());
                    data.put("Consolidated Financial Statement", pls.getActualConsolidated());
                }
            }
            detailList.add(data);
        }

        return detailList;
    }

    private Map<String, QfFinanceCfS> historyTotalList(QfFinanceCfM mainInfo) {
        //获取上个月的累计销售额数据
        List<QfFinanceCfS> historyList = mainDao.historyTotalList(mainInfo.getFillMonth() - 1, mainInfo.getFillYear());

        if (CollectionUtils.isEmpty(historyList)) {
            return Maps.newHashMap();
        }
        return historyList.stream().collect(Collectors.toMap(item -> item.getSubjectCode(), item -> item));
    }

}
