package com.dsk.cscec.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dsk.common.core.domain.R;
import com.dsk.common.excel.ExcelResult;
import com.dsk.common.exception.ServiceException;
import com.dsk.common.utils.poi.ExcelUtil;
import com.dsk.cscec.constant.CbProjectConstants;
import com.dsk.cscec.domain.*;
import com.dsk.cscec.domain.bo.CbSummaryActualBo;
import com.dsk.cscec.domain.bo.CbSummaryActualListBo;
import com.dsk.cscec.domain.bo.CbSummaryActualLockBo;
import com.dsk.cscec.domain.bo.CbSummaryBo;
import com.dsk.cscec.domain.vo.CbSummaryActualListVo;
import com.dsk.cscec.domain.vo.CbSummaryCostAccountImportVo;
import com.dsk.cscec.domain.vo.CbSummaryProjectImportVo;
import com.dsk.cscec.listener.CbSummaryCostAccountImportListener;
import com.dsk.cscec.listener.CbSummaryProjectImportListener;
import com.dsk.cscec.mapper.*;
import com.dsk.cscec.service.CbSummaryService;
import com.dsk.system.service.ISysOssService;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.*;

/**
 * 成本汇总(CbSummary)表服务实现类
 *
 * @author cyf
 * @since 2024-02-06
 */
@Slf4j
@Service
public class CbSummaryServiceImpl extends ServiceImpl<CbSummaryMapper, CbSummary> implements CbSummaryService {
    @Autowired
    private CbProjectFileMapper cbProjectFileMapper;

    @Autowired
    private CbSummaryActualMapper cbSummaryActualMapper;

    @Autowired
    private CbProjectRecordMapper cbProjectRecordMapper;

    @Autowired
    private CbSummaryActualLockMapper cbSummaryActualLockMapper;

    @Autowired
    private ISysOssService ossService;

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Async
    @Override
    public void importCbSummary(Long projectId) {

        //获取待解析和解析失败文件信息
        List<CbProjectFile> cbProjectFiles = cbProjectFileMapper.selectAnalysisList(projectId, CbProjectConstants.CB_TYPE_SUMMARY, null);

        //解析数据
        cbProjectFiles.forEach(cbProjectFile -> {
            cbProjectFile.setFailRemark(null);
//            if (cbProjectFile.getDelFlag() == 1) {
//                //根据fileId删除相关基础数据，并删除文件
//                baseMapper.delete(new LambdaQueryWrapper<CbSummary>().eq(CbSummary::getCbProjectFileId, cbProjectFile.getId()));
//                cbProjectFileMapper.removeById(cbProjectFile.getId());
//            } else {
                try {
                    if (FileNameUtil.getPrefix(cbProjectFile.getFileName()).equals("成本汇总项目结构汇总")) {
                        saveCbSummaryProject(projectId, cbProjectFile);
                    } else if (FileNameUtil.getPrefix(cbProjectFile.getFileName()).equals("成本汇总按成本科目")) {
                        saveCbSummaryCostAccount(projectId, cbProjectFile);
                    } else {
                        throw new ServiceException("文件名错误");
                    }
                } catch (Exception e) {
                    //修改文件状态
                    cbProjectFile.setFileParseStatus(CbProjectConstants.PROJECT_FILE_STATUS_PARSE_FAIL);
                    cbProjectFile.setFailRemark(e.getMessage());
                    cbProjectFileMapper.updateById(cbProjectFile);
                    log.error(projectId+"项目 成本汇总解析失败" + e);
                }
//            }

        });
        log.info(projectId+"项目 成本汇总解析成功");
    }

    /**
     * 成本汇总-项目汇总导入
     *
     * @param projectId
     * @param file
     * @return
     */
    boolean saveCbSummaryProject(Long projectId, CbProjectFile file) {
        //修改文件状态为解析中
//        file.setFileParseStatus(CbProjectConstants.PROJECT_FILE_STATUS_PARSING);
//        cbProjectFileMapper.updateById(file);

        //文件下载
        InputStream inputStream = ossService.downFileIO(file.getFileOssId());
        if (ObjectUtil.isNull(inputStream)) {
            throw new ServiceException("文件数据不存在!");
        }

        //解析数据
        ExcelResult<CbSummaryProjectImportVo> importVoExcelResult = ExcelUtil.importExcel(inputStream, CbSummaryProjectImportVo.class, new CbSummaryProjectImportListener(projectId, file.getId()));
        log.info(importVoExcelResult.getAnalysis());

        if (CollectionUtil.isEmpty(importVoExcelResult.getList())) {
            throw new ServiceException("文件中不存在待导入数据!");
        }
        List<CbSummary> addList = BeanUtil.copyToList(importVoExcelResult.getList(), CbSummary.class);

        transactionTemplate.execute(status -> {
            try {
                //分批次插入
                if (addList.size() > 1000) {
                    int index = 0;
                    int total = addList.size();
                    while (index < total) {
                        List<CbSummary> divideList = addList.subList(index, Math.min(index += 1000, total));
                        Assert.isTrue(this.saveBatch(divideList), "数据插入失败");
                    }
                } else {
                    Assert.isTrue(this.saveBatch(addList), "数据插入失败");
                }


                //处理父级id
                List<CbSummary> cbSummaryList = baseMapper.selectList(
                        new LambdaQueryWrapper<CbSummary>()
                                .eq(CbSummary::getProjectId, projectId)
                                .eq(CbSummary::getCbType, 1)
                                .orderByAsc(CbSummary::getSort)
                );
                for (CbSummary cbSummary : cbSummaryList) {
                    if (cbSummary.getLevel() == 0) {
                        continue;
                    }
                    //获取父级id
                    Integer parentLevel = cbSummary.getLevel() - 1;
                    CbSummary parent = null;
                            Optional<CbSummary> max = cbSummaryList.stream().filter(summary -> summary.getSort() < cbSummary.getSort() && summary.getLevel() == parentLevel)
                            .max(Comparator.comparing(CbSummary::getSort));
                    if (max.isPresent()) {
                        parent = max.get();
                    }
                    if(ObjectUtil.isNull(parent)){
                        throw new ServiceException("'序号'列数据错误，无法匹配层级!");
                    }
                    cbSummary.setParentId(parent.getId());
                }
                //分批次修改
                if (cbSummaryList.size() > 1000) {
                    int index = 0;
                    int total = cbSummaryList.size();
                    while (index < total) {
                        List<CbSummary> divideList = cbSummaryList.subList(index, Math.min(index += 1000, total));
                        Assert.isTrue(this.updateBatchById(divideList), "更新数据失败");
                    }
                } else {
                    Assert.isTrue(this.updateBatchById(cbSummaryList), "更新数据失败");
                }

                //修改文件状态为解析成功
                file.setFileParseStatus(CbProjectConstants.PROJECT_FILE_STATUS_PARSE_SUCCESS);
                Assert.isTrue(cbProjectFileMapper.updateById(file) > 0, "更新文件状态失败");

            } catch (Exception e) {
                status.setRollbackOnly();
                throw e;
            }
            return Boolean.TRUE;
        });
        return true;
    }


    /**
     * 成本汇总-成本科目汇总导入
     *
     * @param projectId
     * @param file
     * @return
     */
    boolean saveCbSummaryCostAccount(Long projectId, CbProjectFile file) {
        //修改文件状态为解析中
//        file.setFileParseStatus(CbProjectConstants.PROJECT_FILE_STATUS_PARSING);
//        cbProjectFileMapper.updateById(file);

        //文件下载
        InputStream inputStream = ossService.downFileIO(file.getFileOssId());
        if (ObjectUtil.isNull(inputStream)) {
            throw new ServiceException("文件数据不存在");
        }

        //解析数据
        ExcelResult<CbSummaryCostAccountImportVo> importVoExcelResult = ExcelUtil.importExcel(inputStream, CbSummaryCostAccountImportVo.class, new CbSummaryCostAccountImportListener(projectId, file.getId()));
        log.info(importVoExcelResult.getAnalysis());

        if (CollectionUtil.isEmpty(importVoExcelResult.getList())) {
            throw new ServiceException("文件中不存在待导入数据!");
        }
        List<CbSummary> addList = BeanUtil.copyToList(importVoExcelResult.getList(), CbSummary.class);

        transactionTemplate.execute(status -> {
            try {
                //分批次插入
                if (addList.size() > 1000) {
                    int index = 0;
                    int total = addList.size();
                    while (index < total) {
                        List<CbSummary> divideList = addList.subList(index, Math.min(index += 1000, total));
                        Assert.isTrue(this.saveBatch(divideList), "数据插入失败");
                    }
                } else {
                    Assert.isTrue(this.saveBatch(addList), "数据插入失败");
                }


                //处理父级id
                List<CbSummary> cbSummaryList = baseMapper.selectList(
                        new LambdaQueryWrapper<CbSummary>()
                                .eq(CbSummary::getProjectId, projectId)
                                .eq(CbSummary::getCbType, 2)
                                .orderByAsc(CbSummary::getSort)
                );
                for (CbSummary cbSummary : cbSummaryList) {
                    if (cbSummary.getLevel() == 0) {
                        continue;
                    }
                    CbSummary parent = null;
                    //通过序号获取parentId
                    if (StringUtil.count(cbSummary.getNumber(), "-") > 1) {
                        String parentNumber = cbSummary.getNumber().substring(0, cbSummary.getNumber().lastIndexOf("-"));
                        Optional<CbSummary> max = cbSummaryList.stream().limit(cbSummary.getSort()).filter(summary -> summary.getNumber().equals(parentNumber))
                                .max(Comparator.comparing(CbSummary::getSort));
                        if (max.isPresent()) {
                            parent = max.get();
                        }
                    } else {
                        Integer parentLevel = cbSummary.getLevel() - 1;
                        Optional<CbSummary> max = cbSummaryList.stream().filter(summary -> summary.getSort() < cbSummary.getSort() && summary.getLevel() == parentLevel)
                                .max(Comparator.comparing(CbSummary::getSort));
                        if (max.isPresent()) {
                            parent = max.get();
                        }
                    }
                    if(ObjectUtil.isNull(parent)){
                        throw new ServiceException("'序号'列数据错误，无法匹配层级!");
                    }
                    cbSummary.setParentId(parent.getId());
                }
                //分批次修改
                if (cbSummaryList.size() > 1000) {
                    int index = 0;
                    int total = cbSummaryList.size();
                    while (index < total) {
                        List<CbSummary> divideList = cbSummaryList.subList(index, Math.min(index += 1000, total));
                        Assert.isTrue(this.updateBatchById(divideList), "更新数据失败");
                    }
                } else {
                    Assert.isTrue(this.updateBatchById(cbSummaryList), "更新数据失败");
                }
                //修改文件状态为解析成功
                file.setFileParseStatus(CbProjectConstants.PROJECT_FILE_STATUS_PARSE_SUCCESS);
                Assert.isTrue(cbProjectFileMapper.updateById(file) > 0, "更新文件状态失败");

            } catch (Exception e) {
                status.setRollbackOnly();
                throw e;
            }
            return Boolean.TRUE;
        });

        return true;
    }

    @Override
    public List<Map<String, Object>> getCbNameList(CbSummaryBo bo) {
        Assert.notNull(bo.getProjectId(), "项目id不能为空");
//        Assert.notNull(bo.getCbStage(), "成本阶段不能为空");
        Assert.notNull(bo.getCbType(), "成本类型不能为空");

        List<Map<String, Object>> resultList = new ArrayList<>();

        bo.setLevel(0);
        List<Map<String, Object>> cbNameTop = baseMapper.selectByLevel(bo);

        bo.setLevel(1);
        List<Map<String, Object>> cbNames = baseMapper.selectByLevel(bo);
        resultList.addAll(cbNameTop);
        resultList.addAll(cbNames);

        return resultList;
    }

    @Override
    public List<CbSummaryActualListVo> getActualList(CbSummaryActualListBo bo) {
        Assert.notNull(bo.getId(), "id不能为空");
        Assert.notNull(bo.getCbType(), "成本类型不能为空");
        if (StringUtil.isBlank(bo.getExpenseDate())) {
            //默认当前月
            bo.setExpenseDate(DatePattern.SIMPLE_MONTH_FORMAT.format(new Date()));
        }
        List<CbSummaryActualListVo> list = new ArrayList<>();
        //当前父级数据
        CbSummaryActualListVo cbSummaryActualListVo = baseMapper.getById(bo.getId(), bo.getExpenseDate());
        if (ObjectUtil.isEmpty(cbSummaryActualListVo)) {
            return list;
        }
        //截至本月费用汇总
        Map<String, BigDecimal> parentTotal = cbSummaryActualMapper.getTotal(cbSummaryActualListVo.getId(), bo.getExpenseDate());
        cbSummaryActualListVo.setTaxExclusiveExpenseTotal(parentTotal.get("taxExclusiveExpenseTotal"));
        cbSummaryActualListVo.setTaxInclusiveExpenseTotal(parentTotal.get("taxInclusiveExpenseTotal"));

        List<CbSummaryActualListVo> childrenList = baseMapper.getByParentId(bo.getId(), bo.getExpenseDate());
        //截至本月费用汇总
        childrenList.parallelStream().forEach(children -> {
            Map<String, BigDecimal> total = cbSummaryActualMapper.getTotal(children.getId(), bo.getExpenseDate());
            children.setTaxExclusiveExpenseTotal(total.get("taxExclusiveExpenseTotal"));
            children.setTaxInclusiveExpenseTotal(total.get("taxInclusiveExpenseTotal"));
        });
        if (CollectionUtil.isNotEmpty(childrenList)) {
            cbSummaryActualListVo.setHasChildren(1);
        } else {
            cbSummaryActualListVo.setHasChildren(0);
        }

        if (0 == cbSummaryActualListVo.getLevel() && 1 == bo.getCbType()) {
            //项目汇总一级列表
            return childrenList;
        } else if (0 == cbSummaryActualListVo.getLevel() && 2 == bo.getCbType()) {
            //成本科目汇总一级列表
            cbSummaryActualListVo.setChildren(childrenList);

            list.add(cbSummaryActualListVo);
            return list;
        } else {
//            //递归列表
//            childrenList = getProjectSumList(bo);
            childrenList.parallelStream().forEach(child->{
                Long count = baseMapper.selectCount(new LambdaQueryWrapper<CbSummary>().eq(CbSummary::getParentId,child.getId()));
                if(count>0l){
                    child.setHasChildren(1);
                }else {
                    child.setHasChildren(0);
                }
            });
            cbSummaryActualListVo.setChildren(childrenList);

            list.add(cbSummaryActualListVo);
            return list;
        }

    }

    /**
     * 成本汇总递归列表
     *
     * @param bo
     * @return
     */
    private List<CbSummaryActualListVo> getProjectSumList(CbSummaryActualListBo bo) {

        List<CbSummaryActualListVo> childrenList = baseMapper.getByParentId(bo.getId(), bo.getExpenseDate());
        childrenList.forEach(children -> {
            CbSummaryActualListBo childBo = new CbSummaryActualListBo();
            childBo.setId(children.getId());
            childBo.setExpenseDate(bo.getExpenseDate());
            children.setChildren(getProjectSumList(childBo));
            //截至本月费用汇总
            Map<String, BigDecimal> total = cbSummaryActualMapper.getTotal(children.getId(), bo.getExpenseDate());
            children.setTaxExclusiveExpenseTotal(total.get("taxExclusiveExpenseTotal"));
            children.setTaxInclusiveExpenseTotal(total.get("taxInclusiveExpenseTotal"));
        });

        return childrenList;

    }

    @Override
    public List<Map<String, Object>> getExpenseDateList(CbSummaryBo bo) {
        Assert.notNull(bo.getProjectId(), "项目id不能为空");
//        Assert.notNull(bo.getCbStage(),"成本阶段不能为空");
        Assert.notNull(bo.getCbType(), "成本类型不能为空");

        return baseMapper.getExpenseDateList(bo);
    }

    @Override
    public List<String> getLockedList(CbSummaryBo bo) {
        Assert.notNull(bo.getProjectId(), "项目id不能为空");
        Assert.notNull(bo.getCbType(), "成本类型不能为空");

        return cbSummaryActualLockMapper.getLockedList(bo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insertOrUpdateActual(List<CbSummaryActualBo> boList) {
        if (CollectionUtil.isEmpty(boList)) {
            return false;
        }
        boList.forEach(bo -> {
            Assert.notNull(bo.getCbSummaryId(), "成本汇总id不能为空");
            Assert.notNull(bo.getExpenseDate(), "费用日期不能为空");

        });

        CbSummary cbSummary = baseMapper.selectById(boList.get(0).getCbSummaryId());
        Assert.isFalse(ObjectUtil.isNull(cbSummary), "成本汇总数据不存在！");

        String expenseDate = boList.get(0).getExpenseDate();
        boolean exists = cbSummaryActualLockMapper.exists(
                new LambdaQueryWrapper<CbSummaryActualLock>()
                        .eq(CbSummaryActualLock::getProjectId, cbSummary.getProjectId())
                        .eq(CbSummaryActualLock::getExpenseDate, expenseDate)
                        .eq(CbSummaryActualLock::getCbType, cbSummary.getCbType())
        );
        Assert.isFalse(exists, "该月成本已锁定");

        List<CbSummaryActual> actualList = BeanUtil.copyToList(boList, CbSummaryActual.class);
        return cbSummaryActualMapper.insertOrUpdateBatch(actualList) > 0;
    }

    @Override
    public R getUnfilled(CbSummaryActualLockBo bo) {
        Assert.notNull(bo.getCbType(), "成本类型不能为空");
        Assert.notNull(bo.getProjectId(), "项目id不能为空");
        Assert.notBlank(bo.getExpenseDate(), "费用日期不能为空");

        StringBuilder unfilled = new StringBuilder();
        //获取一级大类成本id
        List<CbSummary> firstList = baseMapper.selectList(
                new LambdaQueryWrapper<CbSummary>()
                        .eq(CbSummary::getProjectId, bo.getProjectId())
                        .eq(CbSummary::getLevel, 1)
                        .eq(CbSummary::getCbType, bo.getCbType())
                        .orderByAsc(CbSummary::getSort)
        );

        firstList.forEach(firstCb -> {
            //获取大类下的所有子级id
            List<CbSummary> allList = new ArrayList<>();
            allList.add(firstCb);
            allList.addAll(getAllChild(firstCb.getId(), new ArrayList<>()));
            //根据子级id查询是否填写实际成本
            List<CbSummaryActualListVo> vos = baseMapper.getByIds(allList, bo.getExpenseDate());
            if (CollectionUtil.isEmpty(vos)) {
                unfilled.append(firstCb.getCbName()).append("、");
            }
        });

        if (unfilled.length() > 0) {
            unfilled.deleteCharAt(unfilled.lastIndexOf("、"));
            unfilled.append(" 未填写成本，是否继续锁定？");
            return R.ok(unfilled.toString(),500);
        } else {
            return R.ok();
        }
    }

    private List<CbSummary> getAllChild(Long id, List<CbSummary> resultList) {
        List<CbSummary> childList = baseMapper.selectList(
                new LambdaQueryWrapper<CbSummary>()
                        .eq(CbSummary::getParentId, id)
        );
        if (CollectionUtil.isNotEmpty(childList)) {
            resultList.addAll(childList);
            childList.forEach(child -> {
                getAllChild(child.getId(), resultList);
            });
        }

        return resultList;
    }

    @Override
    public boolean lockActual(CbSummaryActualLockBo bo) {
        Assert.notNull(bo.getCbType(), "成本类型不能为空");
        Assert.notNull(bo.getProjectId(), "项目id不能为空");
        Assert.notBlank(bo.getExpenseDate(), "费用日期不能为空");

        boolean exists = cbSummaryActualLockMapper.exists(
                new LambdaQueryWrapper<CbSummaryActualLock>()
                        .eq(CbSummaryActualLock::getProjectId, bo.getProjectId())
                        .eq(CbSummaryActualLock::getExpenseDate, bo.getExpenseDate())
                        .eq(CbSummaryActualLock::getCbType, bo.getCbType())
        );
        Assert.isFalse(exists, "该月成本已锁定，请勿重复操作！");
        CbSummaryActualLock lock = BeanUtil.copyProperties(bo, CbSummaryActualLock.class);
        return cbSummaryActualLockMapper.insert(lock) > 0;
    }

    @Override
    public List<CbSummaryActualListVo> getAll(CbSummaryActualListBo bo) {
        Assert.notNull(bo.getProjectId(), "项目id不能为空");
        Assert.notNull(bo.getCbType(), "成本类型不能为空");
        Assert.notNull(bo.getExpenseDate(), "费用日期不能为空");
        //获取项目成本阶段
        CbProjectRecord cbProjectRecord = cbProjectRecordMapper.selectById(bo.getProjectId());
        if (ObjectUtil.isNull(cbProjectRecord)) {
            throw new ServiceException("当前项目不存在");
        }
//        bo.setCbStage(cbProjectRecord.getCbStage());
        List<CbSummaryActualListVo> resultList = new ArrayList<>();
        if (bo.getCbType() == 1) {
            resultList = baseMapper.getProjectAll(bo);
        } else {
            resultList = baseMapper.getCostAccountAll(bo);
        }
        //截至本月费用汇总
        resultList.forEach(cbSummaryActualListVo -> {
            Map<String, BigDecimal> total = cbSummaryActualMapper.getTotal(cbSummaryActualListVo.getId(), bo.getExpenseDate());
            cbSummaryActualListVo.setTaxExclusiveExpenseTotal(total.get("taxExclusiveExpenseTotal"));
            cbSummaryActualListVo.setTaxInclusiveExpenseTotal(total.get("taxInclusiveExpenseTotal"));
        });
        return resultList;
    }
}
