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.QfFinanceBsBudgetDDao;
import com.artfess.cqlt.dao.QfFinanceBsBudgetMDao;
import com.artfess.cqlt.manager.QfEnterpriseInfoManager;
import com.artfess.cqlt.manager.QfFinanceBsBudgetDManager;
import com.artfess.cqlt.manager.QfFinanceBsBudgetSManager;
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.QfFinanceBsBudgetD;
import com.artfess.cqlt.model.QfFinanceBsBudgetM;
import com.artfess.cqlt.model.QfFinanceBsBudgetS;
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.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 QfFinanceBsBudgetDManagerImpl extends BaseManagerImpl<QfFinanceBsBudgetDDao, QfFinanceBsBudgetD> implements QfFinanceBsBudgetDManager {

    @Resource
    private QfSubjectInternationalInfoManager subjectInternationalInfoManager;

    @Resource
    private QfEnterpriseInfoDao enterpriseInfoDao;

    @Resource
    private QfFinanceBsBudgetMDao mainDao;

    @Autowired
    private QfFinanceBsBudgetSManager sumManager;

    @Autowired
    private QfEnterpriseInfoManager enterpriseInfoManager;

    @Autowired
    private QfFinancialStatisticalManager qfFinancialStatisticalManager;

    @Resource(name = "bmpExecutorService")
    private ExecutorService executorService;

    @Autowired
    private QfReportLogManager reportLogManager;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchUpdate(QfFinanceBsBudgetM t) {

        Assert.hasText(t.getId(), I18nUtil.getMessage("QfOperationKpiM.reportId", LocaleContextHolder.getLocale()));
        QfFinanceBsBudgetM budgetM = mainDao.selectById(t.getId());
        Assert.notNull(budgetM, I18nUtil.getMessage("filldata.notExist", LocaleContextHolder.getLocale()));
        Assert.isTrue(!"1".equals(budgetM.getStatus()),  I18nUtil.getMessage("data_operate", LocaleContextHolder.getLocale()));
        List<QfFinanceBsBudgetD> list = Lists.newArrayList();
        List<QfFinanceBsBudgetS> 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(budgetM.getFillDate());
            detail.setMainId(t.getId());
            QueryWrapper<QfFinanceBsBudgetD> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("subject_code_", detail.getSubjectCode());
            queryWrapper.eq("main_id_", t.getId());
            queryWrapper.eq("enterprise_code_", detail.getEnterpriseCode());
            QfFinanceBsBudgetD QfFinanceBsBudgetD = this.baseMapper.selectOne(queryWrapper);
            if (null != QfFinanceBsBudgetD) {
                QfFinanceBsBudgetD.setBudgetYtd(detail.getBudgetYtd());
                list.add(QfFinanceBsBudgetD);
            } 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<QfFinanceBsBudgetS> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("subject_code_", detail.getSubjectCode());
            queryWrapper.eq("main_id_", t.getId());
            QfFinanceBsBudgetS financePlBudgetS = sumManager.getBaseMapper().selectOne(queryWrapper);
            if (null != financePlBudgetS) {
                if(null != detail.getBudgetTotal()) {
                    financePlBudgetS.setBudgetTotal(detail.getBudgetTotal());
                }
                if(null != detail.getBudgetPosting()) {
                    financePlBudgetS.setBudgetPosting(detail.getBudgetPosting());
                }
                if(null != detail.getBudgetConsolidated()) {
                    financePlBudgetS.setBudgetConsolidated(detail.getBudgetConsolidated());
                }

                totalList.add(financePlBudgetS);
            } else {
                detail.setFillDate(budgetM.getFillDate());
                detail.setFillQuarter(budgetM.getFillQuarter());
                detail.setFillMonth(budgetM.getFillMonth());
                detail.setFillYear(budgetM.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(budgetM, list, totalList);
        return true;
    }

    private void asyncReportInfo(QfFinanceBsBudgetM budgetM, List<QfFinanceBsBudgetD> list, List<QfFinanceBsBudgetS> totalList) {
        executorService.execute(() -> {
            String msg = null;
            String reportStatus = "0";
            try {
                saveFinancialVo(budgetM, list, totalList);
                reportStatus = "1";
            } catch (Exception e) {
                msg = e.getMessage();
                log.error("财务大屏报表实际数据生成失败:{}", e.getMessage());
            }
            reportLogManager.saveReportInfo(reportStatus, msg, ReportTypeEnum.BS_BUDGET.getType(), budgetM.getFillYear(), budgetM.getFillMonth());
        });
    }

    private void saveFinancialVo(QfFinanceBsBudgetM mainInfo, List<QfFinanceBsBudgetD> detailList,  List<QfFinanceBsBudgetS> totalList) {
        QfEnterpriseInfo group = enterpriseInfoManager.getGroup();
        //获取上个月的利润信息
        Map<String, QfFinanceBsBudgetS> 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.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())) {
                QfFinanceBsBudgetS 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 Map<String, QfFinanceBsBudgetS> historyTotalList(QfFinanceBsBudgetM mainInfo) {
        //获取上个月的累计销售额数据
        List<QfFinanceBsBudgetS> 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));
    }

    @Override
    public void export(HttpServletRequest request, HttpServletResponse response, List<QfFinanceBsBudgetD> list, String sheetName) throws IOException {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        String mainId = list.get(0).getMainId();
        QueryWrapper<QfFinanceBsBudgetS> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("main_id_", mainId);
        List<QfFinanceBsBudgetS> 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.getBudgetYtd().toString());
            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.getBudgetConsolidated()) {
                node.setRow(rowMap.get(pls.getSubjectCode()));
                node.setColumn(maxColum + 3);
                node.setHeaderName(pls.getBudgetConsolidated() + "");
                headerNodeList.add(node);
            }

            if(null != pls.getBudgetPosting()) {
                node = new HeaderNode();
                node.setRow(rowMap.get(pls.getSubjectCode()));
                node.setColumn(maxColum + 2);
                node.setHeaderName(pls.getBudgetPosting() + "");
                headerNodeList.add(node);
            }

            if(null != pls.getBudgetTotal()) {
                node = new HeaderNode();
                node.setRow(rowMap.get(pls.getSubjectCode()));
                node.setColumn(maxColum + 1);
                node.setHeaderName(pls.getBudgetTotal() + "");
                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<JSONObject> detailQuery(List<QfFinanceBsBudgetD> list) {
        if (CollectionUtils.isEmpty(list)) {
            return Lists.newArrayList();
        }
        String mainId = list.get(0).getMainId();
        QueryWrapper<QfFinanceBsBudgetS> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("main_id_", mainId);
        List<QfFinanceBsBudgetS> 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 (QfFinanceBsBudgetD 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.getBudgetYtd());
                }
            }
            for (QfFinanceBsBudgetS pls : financePlSList) {
                if (pls.getSubjectCode().equals(subjectCode)) {
                    data.put("Total", pls.getBudgetTotal());
                    data.put("Consolidation Posting", pls.getBudgetPosting());
                    data.put("Consolidated Financial Statement", pls.getBudgetConsolidated());
                }
            }
            detailList.add(data);
        }

        return detailList;
    }
}
