package com.dsk.cscec.service.impl;

import cn.hutool.core.bean.BeanUtil;
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.excel.ExcelUtils;
import com.dsk.common.utils.StringUtils;
import com.dsk.cscec.constant.CbProjectConstants;
import com.dsk.cscec.constant.CbSceneExpenseConstants;
import com.dsk.cscec.domain.CbProjectFile;
import com.dsk.cscec.domain.CbProjectRecord;
import com.dsk.cscec.domain.CbSceneExpenseChildren;
import com.dsk.cscec.domain.bo.CbSceneExpenseChildrenDataBo;
import com.dsk.cscec.domain.bo.CbSceneExpenseChildrenImportBo;
import com.dsk.cscec.domain.vo.CbSceneExpenseMenuVo;
import com.dsk.cscec.mapper.CbProjectFileMapper;
import com.dsk.cscec.mapper.CbProjectRecordMapper;
import com.dsk.cscec.mapper.CbSceneExpenseChildrenMapper;
import com.dsk.cscec.service.CbSceneExpenseChildrenService;
import com.dsk.system.service.ISysOssService;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 现场经费-工资统筹、其他费用、现场管理费表(CbSceneExpenseChildren)表服务实现类
 *
 * @author sxk
 * @since 2024-02-22 09:59:01
 */
@Service("cbSceneExpenseChildrenService")
public class CbSceneExpenseChildrenServiceImpl extends ServiceImpl<CbSceneExpenseChildrenMapper, CbSceneExpenseChildren> implements CbSceneExpenseChildrenService {
    @Resource
    private CbSceneExpenseChildrenMapper baseMapper;
    @Resource
    private CbProjectRecordMapper projectRecordMapper;
    @Resource
    private CbProjectFileMapper projectFileMapper;
    @Resource
    private ISysOssService ossService;
    @Resource
    private TransactionTemplate transactionTemplate;

    /**
     * 获取现场经费菜单
     *
     * @param projectId 项目ID
     * @return 菜单
     */
    @Override
    public List<CbSceneExpenseMenuVo> getMenuList(Long projectId) {
        //校验项目是否存在
        this.checkProjectExist(projectId);

        //TODO:菜单来源分为两部分,1、导入的文件,2、财务一体化系统
        //从导入文件获取菜单
        List<CbSceneExpenseMenuVo> menuList = projectFileMapper.getSceneExpenseMenuFromFile(projectId, CbProjectConstants.DELETE_FLAG_EXIST, CbProjectConstants.CB_TYPE_SCENE_EXPENSE, CbProjectConstants.PROJECT_FILE_STATUS_PARSE_SUCCESS);
        menuList.forEach(menu -> {
            //菜单名称去除文件格式尾缀
            menu.setMenuName(menu.getMenuName().substring(0, menu.getMenuName().lastIndexOf(".")));
            //若是现场经费汇总,则是一级,否则均为二级

            if (CbProjectConstants.CB_TYPE_SCENE_EXPENSE_NAME.equals(menu.getMenuName())) {
                menu.setMenuLevel(CbSceneExpenseConstants.MENU_LEVEL1);
            } else {
                menu.setMenuLevel(CbSceneExpenseConstants.MENU_LEVEL2);
            }
            //菜单来源
            menu.setMenuSource(CbSceneExpenseConstants.MENU_SOURCE_FILE_TABLE);
        });
        //TODO:从财务一体化系统获取菜单
        return menuList;
    }

    /**
     * 现场经费二级分类数据解析
     *
     * @param projectId 项目ID
     * @param cbStage   成本阶段
     * @param username  用户名
     */
    @Override
    @Async
    public void parseSceneExpenseChildrenData(Long projectId, Integer cbStage, String username) throws Exception {
        //查找该项目下所有相关文件
        List<CbProjectFile> projectFileList = projectFileMapper.selectAnalysisList(projectId, CbProjectConstants.CB_TYPE_SCENE_EXPENSE, cbStage);
        //待解析文件
        List<CbProjectFile> waitParseList = new ArrayList<>();
        //提取非现场经费汇总文件
        projectFileList.forEach(projectFile -> {
            if (!CbProjectConstants.CB_TYPE_SCENE_EXPENSE_NAME.equals(projectFile.getFileName().substring(0, projectFile.getFileName().lastIndexOf(".")))) {
                //待解析文件
                waitParseList.add(projectFile);
            }
        });

        //处理待解析文件
        for (CbProjectFile projectFile : waitParseList) {
            if (ObjectUtil.isNull(projectFile)) {
                continue;
            }

            //文件下载
            InputStream inputStream = ossService.downFileIO(projectFile.getFileOssId());
            if (ObjectUtil.isNull(inputStream)) {
                projectFile.setFileParseStatus(CbProjectConstants.PROJECT_FILE_STATUS_PARSE_FAIL);
                log.error("FileId:" + projectFile.getId() + ",文件数据不存在");
                projectFile.setFailRemark("文件数据不存在");
                projectFileMapper.updateById(projectFile);
                continue;
            }
            //解析数据
            List<CbSceneExpenseChildrenImportBo> importList = new ExcelUtils<>(CbSceneExpenseChildrenImportBo.class).importExcelAllSheet(inputStream, 0);
            if (importList.isEmpty()) {
                projectFile.setFileParseStatus(CbProjectConstants.PROJECT_FILE_STATUS_PARSE_FAIL);
                log.error("FileId:" + projectFile.getId() + ",表格中不存在待导入数据");
                projectFile.setFailRemark("表格中不存在待导入数据");
                projectFileMapper.updateById(projectFile);
                continue;
            }

            //筛选有效数据
            List<CbSceneExpenseChildrenImportBo> chidrenList = importList.stream().parallel()
                    //筛选名称列不为空数据
                    .filter(item -> StringUtils.isNotBlank(item.getExpenseName()))
                    .peek(item -> {
                        item.setProjectId(projectId);
                        item.setProjectFileId(projectFile.getId());

                        //因为现场管理费Excel表中字段名称不一样,所以做以下单独映射
                        //公司单价/不含税单价
                        if (StringUtils.isNotBlank(item.getExcludeTaxUnitPrice())) {
                            item.setUnitPrice(item.getExcludeTaxUnitPrice());
                        }
                        //目标成本合价(不含税)/不含税合价
                        if (StringUtils.isNotBlank(item.getExcludeTaxSumPrice())) {
                            item.setTargetCbSumPriceExcludeTax(item.getExcludeTaxSumPrice());
                        }
                        //目标成本合价(含税)/含税合价
                        if (StringUtils.isNotBlank(item.getIncludeTaxSumPrice())) {
                            item.setTargetCbSumPriceIncludeTax(item.getIncludeTaxSumPrice());
                        }

                        item.setCreateBy(username);
                        item.setCreateTime(new Date());
                    })
                    .collect(Collectors.toList());
            if (chidrenList.size() != importList.size()) {
                projectFile.setFileParseStatus(CbProjectConstants.PROJECT_FILE_STATUS_PARSE_FAIL);
                log.error("FileId:" + projectFile.getId() + ",表格中\"名称\"列存在空数据");
                projectFile.setFailRemark("表格中\"名称\"列存在空数据");
                projectFileMapper.updateById(projectFile);
                continue;
            }
            transactionTemplate.execute(status -> {
                try {
                    //批量插入数据
                    CbSceneExpenseChildrenServiceImpl impl = new CbSceneExpenseChildrenServiceImpl();
                    Assert.isTrue(impl.saveBatch(BeanUtil.copyToList(chidrenList, CbSceneExpenseChildren.class)), "数据插入失败");
                    //更新文件状态
                    projectFile.setFileParseStatus(CbProjectConstants.PROJECT_FILE_STATUS_PARSE_SUCCESS);
                    //场景:若第一次解析失败了,用户未删除失败的文件,又解析一次,这时解析成功了,因此将失败备注置空
                    projectFile.setFailRemark(null);
                    Assert.isTrue(projectFileMapper.updateById(projectFile) > 0, "解析成功后文件状态更新失败");
                } catch (Exception e) {
                    status.setRollbackOnly();
                    projectFile.setFileParseStatus(CbProjectConstants.PROJECT_FILE_STATUS_PARSE_FAIL);
                    projectFile.setFailRemark(e.getMessage());
                    projectFileMapper.updateById(projectFile);
                }
                return Boolean.TRUE;
            });
        }
    }

    /**
     * 获取现场经费二级分类数据
     *
     * @param childrenDataBo 查询体
     * @return 现场经费二级分类数据
     */
    @Override
    public List<CbSceneExpenseChildren> getChildrenData(CbSceneExpenseChildrenDataBo childrenDataBo) {
        //校验项目是否存在
        this.checkProjectExist(childrenDataBo.getProjectId());

        return baseMapper.selectList(new LambdaQueryWrapper<CbSceneExpenseChildren>()
                .eq(CbSceneExpenseChildren::getProjectId, childrenDataBo.getProjectId())
                .eq(CbSceneExpenseChildren::getProjectFileId, childrenDataBo.getFileId()));
    }

    /**
     * 校验项目是否存在
     *
     * @param projectId 项目ID
     * @return 项目实体
     */
    private CbProjectRecord checkProjectExist(Long projectId) {
        CbProjectRecord projectRecord = projectRecordMapper.selectById(projectId);
        Assert.notNull(projectRecord, "该项目不存在");
        return projectRecord;
    }
}
