package com.dsk.cscec.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dsk.common.core.domain.PageQuery;
import com.dsk.common.core.page.TableDataInfo;
import com.dsk.common.helper.LoginHelper;
import com.dsk.common.utils.StringUtils;
import com.dsk.component.DataAnalysisComponent;
import com.dsk.cscec.constant.CbProjectConstants;
import com.dsk.cscec.domain.CbProjectFile;
import com.dsk.cscec.domain.CbProjectRecord;
import com.dsk.cscec.domain.bo.*;
import com.dsk.cscec.domain.vo.CbProjectCbStageNotDraftVo;
import com.dsk.cscec.domain.vo.CbProjectHeadInfoVo;
import com.dsk.cscec.domain.vo.CbProjectRecordSearchVo;
import com.dsk.cscec.mapper.CbProjectFileMapper;
import com.dsk.cscec.mapper.CbProjectRecordMapper;
import com.dsk.cscec.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 项目台账表(CbProjectRecord)表服务实现类
 *
 * @author sxk
 * @since 2024-02-05 11:37:53
 */
@Service("cbProjectRecordService")
@Slf4j
public class CbProjectRecordServiceImpl extends ServiceImpl<CbProjectRecordMapper, CbProjectRecord> implements CbProjectRecordService {
    @Resource
    private CbProjectRecordMapper baseMapper;
    @Resource
    private CbProjectFileMapper projectFileMapper;
    @Resource
    private DataAnalysisComponent dataAnalysisComponent;
    @Resource
    private CbSummaryService cbSummaryService;
    @Resource
    private CbProjectExpenseSummaryService projectExpenseSummaryService;
    @Resource
    private CbSceneExpenseChildrenService sceneExpenseChildrenService;
    @Resource
    private CbProjectOtherService projectOtherService;
    @Resource
    private ICbCostMeasureService costMeasureService;
    @Resource
    private CbDirectExpenseService directExpenseService;
    @Resource
    private TransactionTemplate transactionTemplate;

    /**
     * 新增项目
     *
     * @param addCbProjectBo 新增对象
     * @return 新增结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CbProjectRecord addProject(AddCbProjectBo addCbProjectBo) {
        CbProjectRecord projectRecord = BeanUtil.toBean(addCbProjectBo, CbProjectRecord.class);

        //生成唯一关联ID
        boolean idFlag = true;
        long relatedId = -1;
        while (idFlag) {
            relatedId = IdUtil.getSnowflakeNextId();
            idFlag = baseMapper.exists(new LambdaQueryWrapper<CbProjectRecord>()
                    .eq(CbProjectRecord::getRelatedId, relatedId));
        }

        projectRecord.setRelatedId(relatedId);
        //项目文件状态:准备中
        projectRecord.setProjectFileStatus(CbProjectConstants.PROJECT_FILE_STATUS_PREPARING);
        //删除状态:否
        projectRecord.setDelFlag(CbProjectConstants.DELETE_FLAG_EXIST);
        if (baseMapper.insert(projectRecord) > 0) {
            return projectRecord;
        }
        return null;
    }

    /**
     * 根据项目文件导入数据
     *
     * @param projectId 项目ID
     */
    @Override
    public void importData(Long projectId) throws Exception {
        //校验项目是否存在
        CbProjectRecord projectRecord = this.checkProjectExist(projectId);

        List<CbProjectFile> projectFileList = new ArrayList<>();
        //校验各个成本类型是否至少有1个文件
        //直接费成本
        projectFileList.addAll(this.checkCbTypeExistFile(projectId, CbProjectConstants.CB_TYPE_DIRECT_EXPENSE, CbProjectConstants.CB_TYPE_DIRECT_EXPENSE_NAME));
        //工料汇总
        projectFileList.addAll(this.checkCbTypeExistFile(projectId, CbProjectConstants.CB_TYPE_QUANTITY_SUMMARY, CbProjectConstants.CB_TYPE_QUANTITY_SUMMARY_NAME));
        //措施项目
        projectFileList.addAll(this.checkCbTypeExistFile(projectId, CbProjectConstants.CB_TYPE_MEASURE_PROJECT, CbProjectConstants.CB_TYPE_MEASURE_PROJECT_NAME));
        //其他项目
        projectFileList.addAll(this.checkCbTypeExistFile(projectId, CbProjectConstants.CB_TYPE_OTHER_PROJECT, CbProjectConstants.CB_TYPE_OTHER_PROJECT_NAME));
        //现场经费
        projectFileList.addAll(this.checkCbTypeExistFile(projectId, CbProjectConstants.CB_TYPE_SCENE_EXPENSE, CbProjectConstants.CB_TYPE_SCENE_EXPENSE_NAME));
        //成本汇总
        projectFileList.addAll(this.checkCbTypeExistFile(projectId, CbProjectConstants.CB_TYPE_SUMMARY, CbProjectConstants.CB_TYPE_SUMMARY_NAME));

        //修改文件和项目解析状态为解析中
        ((CbProjectRecordServiceImpl) AopContext.currentProxy()).editParseStatus(projectRecord, projectFileList);

        //调各个成本类型的解析文件方法
        Integer cbStage = projectRecord.getCbStage();
        String username = Objects.requireNonNull(LoginHelper.getLoginUser()).getUsername();
        //直接费成本
        directExpenseService.parseDirectExpenseData(projectId, cbStage);
        //工料汇总
        CbProjectBaseBo projectBaseBo = new CbProjectBaseBo();
        projectBaseBo.setProjectId(projectId);
        projectBaseBo.setCbStage(cbStage);
        dataAnalysisComponent.quantitySummaryDataAnalysis(projectBaseBo);
        //措施项目
        costMeasureService.parseCbCostMeasureFile(projectId);
        //其他项目
        projectOtherService.projectOtherDataAnalysis(projectBaseBo);
        //现场经费
        projectExpenseSummaryService.parseSceneExpenseSummaryData(projectId, cbStage, username);
        sceneExpenseChildrenService.parseSceneExpenseChildrenData(projectId, cbStage, username);
        //成本汇总
        cbSummaryService.importCbSummary(projectId);
    }

    @Transactional(rollbackFor = Exception.class)
    public void editParseStatus(CbProjectRecord projectRecord, List<CbProjectFile> projectFileList) {
        //修改项目状态:解析中
        projectRecord.setProjectFileStatus(CbProjectConstants.PROJECT_FILE_STATUS_PARSING);
        baseMapper.updateById(projectRecord);
        //修改所有该项目的项目文件状态:解析中
        CbProjectFileServiceImpl projectFileService = new CbProjectFileServiceImpl();
        projectFileService.updateBatchById(projectFileList.stream().peek(projectFile -> {
            projectFile.setFileParseStatus(CbProjectConstants.PROJECT_FILE_STATUS_PARSING);
            projectFile.setFailRemark(null);
        }).collect(Collectors.toList()));
    }

    /**
     * 校验各个成本类型是否至少有1个文件
     *
     * @param projectId  项目ID
     * @param cbType     成本类型字典（0:直接费成本、1:工料汇总、2:措施项目、3:其他项目、4:现场经费、5:成本汇总）
     * @param cbTypeName 成本类型名称
     */
    private List<CbProjectFile> checkCbTypeExistFile(Long projectId, Integer cbType, String cbTypeName) {
        List<CbProjectFile> projectFileList = projectFileMapper.selectList(new LambdaQueryWrapper<CbProjectFile>()
                .eq(CbProjectFile::getProjectId, projectId)
                .eq(CbProjectFile::getCbType, cbType)
                //项目文件状态:准备中or解析失败
                .eq(CbProjectFile::getFileParseStatus, CbProjectConstants.PROJECT_FILE_STATUS_PREPARING)
                .or()
                .eq(CbProjectFile::getFileParseStatus, CbProjectConstants.PROJECT_FILE_STATUS_PARSE_FAIL));
        Assert.isFalse(projectFileList.isEmpty(), cbTypeName + "至少需要上传1个文件");
        return projectFileList;
    }

    /**
     * 获取项目台账列表
     *
     * @param searchBo  查询条件
     * @param pageQuery 分页对象
     * @return 分页数据
     */
    @Override
    public TableDataInfo<CbProjectRecordSearchVo> getProjectList(CbProjectSearchBo searchBo, PageQuery pageQuery) {
        Page<CbProjectRecordSearchVo> page = baseMapper.selectPageProjectRecordList(pageQuery.build(), searchBo, CbProjectConstants.DELETE_FLAG_EXIST, CbProjectConstants.PROJECT_FILE_STATUS_PREPARING);

        for (CbProjectRecordSearchVo searchVo : page.getRecords()) {
            //判断是否有历史阶段
            searchVo.setHasChildren(baseMapper.selectCount(new LambdaQueryWrapper<CbProjectRecord>()
                    .eq(CbProjectRecord::getRelatedId, searchVo.getRelatedId())
                    .ne(CbProjectRecord::getProjectFileStatus, CbProjectConstants.PROJECT_FILE_STATUS_PREPARING)) > 1
            );

            //关键字标红
            if (StringUtils.isNotBlank(searchBo.getProjectName())) {
                searchVo.setProjectName(StringUtils.markInRed(searchVo.getProjectName(), searchBo.getProjectName()));
            }

            //补充更新时间、更新人
            CbProjectFile projectFile = projectFileMapper.selectOne(new LambdaQueryWrapper<CbProjectFile>()
                    .eq(CbProjectFile::getProjectId, searchVo.getId())
                    .orderByDesc(CbProjectFile::getUpdateTime)
                    .last("LIMIT 1"));
            searchVo.setUpdateBy(projectFile.getUpdateBy());
            searchVo.setUpdateTime(projectFile.getUpdateTime());
        }
        return TableDataInfo.build(page);
    }

    /**
     * 获取项目历史阶段信息(懒加载)
     *
     * @param searchBo 查询条件
     * @return 历史阶段信息
     */
    @Override
    public List<CbProjectRecord> getProjectHistoryInfo(CbProjectHistorySearchBo searchBo) {
        //校验项目是否存在
        CbProjectRecord projectRecord = this.checkProjectExist(searchBo.getProjectId());

        //查询所有历史阶段项目(不含准备中的项目)
        List<CbProjectRecord> projectRecordList = baseMapper.selectList(new LambdaQueryWrapper<CbProjectRecord>()
                .eq(CbProjectRecord::getRelatedId, projectRecord.getRelatedId())
                .ne(CbProjectRecord::getId, projectRecord.getId())
                .ne(CbProjectRecord::getProjectFileStatus, CbProjectConstants.PROJECT_FILE_STATUS_PREPARING)
                .like(StringUtils.isNotBlank(searchBo.getProjectName()), CbProjectRecord::getProjectName, searchBo.getProjectName())
                .eq(StringUtils.isNotBlank(searchBo.getIpmProjectNo()), CbProjectRecord::getIpmProjectNo, searchBo.getIpmProjectNo())
                .eq(ObjectUtil.isNotNull(searchBo.getCbStage()), CbProjectRecord::getCbStage, searchBo.getCbStage())
                .eq(ObjectUtil.isNotNull(searchBo.getProjectFileStatus()), CbProjectRecord::getProjectFileStatus, searchBo.getProjectFileStatus()));

        projectRecordList.forEach(record -> {
            //关键字标红
            if (StringUtils.isNotBlank(searchBo.getProjectName())) {
                record.setProjectName(StringUtils.markInRed(record.getProjectFileName(), searchBo.getProjectName()));
            }
            //补充更新时间、更新人
            CbProjectFile projectFile = projectFileMapper.selectOne(new LambdaQueryWrapper<CbProjectFile>()
                    .eq(CbProjectFile::getProjectId, record.getId())
                    .orderByDesc(CbProjectFile::getUpdateTime)
                    .last("LIMIT 1"));
            record.setUpdateBy(projectFile.getUpdateBy());
            record.setUpdateTime(projectFile.getUpdateTime());
        });

        return projectRecordList;
    }

    /**
     * 修改项目信息
     *
     * @param editBo 修改对象
     * @return 修改结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer editProjectInfo(EditProjectInfoBo editBo) {
        //校验项目是否存在
        CbProjectRecord projectRecord = this.checkProjectExist(editBo.getProjectId());

        for (CbProjectRecord record : baseMapper.selectList(new LambdaQueryWrapper<CbProjectRecord>()
                .eq(CbProjectRecord::getRelatedId, projectRecord.getRelatedId()))) {
            record.setProjectName(editBo.getProjectName());
            record.setIpmProjectNo(editBo.getIpmProjectNo());
            record.setIsGetProjectDetail(editBo.getIsGetProjectDetail());
            record.setProjectFileName(editBo.getProjectFileName());
            Assert.isTrue(baseMapper.updateById(record) > 0, "修改失败,请联系系统管理员");
        }
        return 1;
    }

    /**
     * 获取新阶段项目可选成本阶段
     *
     * @param relatedId 关联ID
     * @return 可选成本阶段
     */
    @Override
    public List<Integer> getProjectAvailableCbStage(Long relatedId) {
        //通过关联ID校验项目是否存在
        List<CbProjectRecord> projectRecordList = this.checkRelatedIdExist(relatedId);

        ArrayList<Integer> cbStageList = new ArrayList<>();
        cbStageList.add(CbProjectConstants.CB_STAGE_BEFORE_BID);
        cbStageList.add(CbProjectConstants.CB_STAGE_AFTER_BID);
        cbStageList.add(CbProjectConstants.CB_STAGE_TO_SOLID);

        projectRecordList.forEach(projectRecord -> {
            cbStageList.remove(projectRecord.getCbStage());
        });
        return cbStageList;
    }

    /**
     * 新增新阶段项目
     *
     * @param newProjectBo 新增对象
     * @return 新增结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CbProjectRecord addNewStageProject(AddNewCbProjectBo newProjectBo) {
        //修改基础信息
        this.editProjectInfo(BeanUtil.toBean(newProjectBo, EditProjectInfoBo.class));

        //通过关联ID校验项目是否存在
        List<CbProjectRecord> projectRecordList = this.checkRelatedIdExist(newProjectBo.getRelatedId());
        //校验待新增的阶段是否已存在
        projectRecordList.forEach(record -> {
            Assert.isFalse(record.getCbStage().equals(newProjectBo.getCbStage()), "当前项目在该阶段已存在记录,请检查草稿箱或项目台账列表");
        });

        //新增该项目的新阶段
        CbProjectRecord projectRecord = BeanUtil.toBean(newProjectBo, CbProjectRecord.class);
        //项目文件状态:准备中
        projectRecord.setProjectFileStatus(CbProjectConstants.PROJECT_FILE_STATUS_PREPARING);
        //删除状态:否
        projectRecord.setDelFlag(CbProjectConstants.DELETE_FLAG_EXIST);
        if (baseMapper.insert(projectRecord) > 0) {
            return projectRecord;
        }
        return null;
    }

    /**
     * 查询当前项目非草稿的成本阶段
     *
     * @param relatedId 关联ID
     * @return 已有数据的成本阶段
     */
    @Override
    public List<CbProjectCbStageNotDraftVo> getProjectCbStageNotDraft(Long relatedId) {
        //通过关联ID校验项目是否存在
        List<CbProjectRecord> projectRecordList = this.checkRelatedIdExist(relatedId);

        ArrayList<CbProjectCbStageNotDraftVo> cbStageList = new ArrayList<>();

        projectRecordList.forEach(projectRecord -> {
            //文件状态为解析成功or解析失败即为有数据
            if (!projectRecord.getProjectFileStatus().equals(CbProjectConstants.PROJECT_FILE_STATUS_PREPARING)
                    && !projectRecord.getProjectFileStatus().equals(CbProjectConstants.PROJECT_FILE_STATUS_PARSING)
                    && projectFileMapper.exists(new LambdaQueryWrapper<CbProjectFile>()
                    .eq(CbProjectFile::getProjectId, projectRecord.getId())
                    .eq(CbProjectFile::getFileParseStatus, CbProjectConstants.PROJECT_FILE_STATUS_PARSE_SUCCESS)
                    .or()
                    .eq(CbProjectFile::getFileParseStatus, CbProjectConstants.PROJECT_FILE_STATUS_PARSE_FAIL))) {
                cbStageList.add(BeanUtil.toBean(projectRecord, CbProjectCbStageNotDraftVo.class));
            }
        });
        return cbStageList;
    }

    /**
     * 批量删除项目
     *
     * @param projectIds 项目ID
     * @return 删除结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer batchDeleteProject(List<Long> projectIds) {
        int flag = 0;
        for (Long projectId : projectIds) {
            //校验项目是否存在
            CbProjectRecord projectRecord = this.checkProjectExist(projectId);
            //不允许删除正在解析中的项目
            Assert.isFalse(projectRecord.getProjectFileStatus().equals(CbProjectConstants.PROJECT_FILE_STATUS_PARSING)
                    , "存在解析中的项目,删除失败");

            //各个成本类型数据和对应每月成本数据暂不做删除,项目台账列表查不出来自然也无法查看数据

            //删除项目文件记录
            flag = projectFileMapper.delete(new LambdaQueryWrapper<CbProjectFile>()
                    .eq(CbProjectFile::getProjectId, projectId));
            //删除项目记录
            Assert.isTrue(flag > 0, "操作异常,请联系管理员");
            flag = baseMapper.deleteById(projectId);
        }
        return flag;
    }

    /**
     * 通过关联ID校验项目是否存在
     *
     * @return 该关联ID下的所有项目记录
     */
    private List<CbProjectRecord> checkRelatedIdExist(Long relatedId) {
        List<CbProjectRecord> projectRecordList = baseMapper.selectList(new LambdaQueryWrapper<CbProjectRecord>()
                .eq(CbProjectRecord::getRelatedId, relatedId));
        Assert.isFalse(projectRecordList.isEmpty(), "关联ID无效");
        return projectRecordList;
    }

    /**
     * 获取草稿弹窗列表
     *
     * @param pageQuery 分页对象
     * @return 分页数据
     */
    @Override
    public TableDataInfo<CbProjectRecord> getDraftDialogList(PageQuery pageQuery) {
        QueryWrapper<CbProjectRecord> wrapper = Wrappers.query();
        wrapper
                .eq("project_file_status", CbProjectConstants.PROJECT_FILE_STATUS_PREPARING)
                .eq("del_flag", CbProjectConstants.DELETE_FLAG_EXIST)
                .orderByDesc("update_time");
        return TableDataInfo.build(baseMapper.selectPageDraftDialogList(pageQuery.build(), wrapper));
    }

    /**
     * 删除草稿
     *
     * @param projectId 项目ID
     * @return 删除结果
     */
    @Override
    public Integer deleteDraft(Long projectId) {
        //校验项目是否存在
        CbProjectRecord projectRecord = this.checkProjectExist(projectId);
        //删除该项目下已上传的文件
        List<CbProjectFile> idList = projectFileMapper.selectList(new LambdaQueryWrapper<CbProjectFile>()
                .eq(CbProjectFile::getProjectId, projectId));
        if (!idList.isEmpty()) {
            projectFileMapper.deleteBatchIds(idList);
        }
        //删除项目记录
        return baseMapper.deleteById(projectRecord);
    }

    /**
     * 获取项目详情头部信息
     *
     * @param projectId 项目ID
     * @return 项目详情头部信息
     */
    @Override
    public CbProjectHeadInfoVo getCbProjectHeadInfo(Long projectId) {
        //校验项目是否存在
        CbProjectRecord projectRecord = this.checkProjectExist(projectId);

        //TODO:客户名称、签订单位字段未确定
        CbProjectHeadInfoVo infoVo = BeanUtil.toBean(projectRecord, CbProjectHeadInfoVo.class);
        return infoVo;
    }

    /**
     * 每2分钟刷新一次项目台账列表内"解析中"项目的状态
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateCbProjectRecordParseStatus() {
        //查询所有解析中的项目
        List<CbProjectRecord> projectRecordList = baseMapper.selectList(new LambdaQueryWrapper<CbProjectRecord>()
                .eq(CbProjectRecord::getProjectFileStatus, CbProjectConstants.PROJECT_FILE_STATUS_PARSING));

        a:
        for (CbProjectRecord projectRecord : projectRecordList) {
            //查询该项目的所有文件
            List<CbProjectFile> projectFileList = projectFileMapper.selectList(new LambdaQueryWrapper<CbProjectFile>()
                    .eq(CbProjectFile::getProjectId, projectRecord.getId()));

            int failNum = 0;
            for (CbProjectFile projectFile : projectFileList) {
                //如果有一个文件处于解析中，则该项目的解析状态继续保持解析中
                if (projectFile.getFileParseStatus().equals(CbProjectConstants.PROJECT_FILE_STATUS_PARSING)) {
                    continue a;
                } else if (projectFile.getFileParseStatus().equals(CbProjectConstants.PROJECT_FILE_STATUS_PARSE_FAIL)) {
                    failNum++;
                }
            }

            if (failNum > 0) {
                //如果存在解析失败的文件，则项目的解析状态改为解析失败
                log.info("项目台账列表:ID为" + projectRecord.getId() + "的项目解析失败");
                projectRecord.setProjectFileStatus(CbProjectConstants.PROJECT_FILE_STATUS_PARSE_FAIL);
                baseMapper.updateById(projectRecord);
            } else {
                //否则改为解析成功
                log.info("项目台账列表:ID为" + projectRecord.getId() + "的项目解析成功");
                projectRecord.setProjectFileStatus(CbProjectConstants.PROJECT_FILE_STATUS_PARSE_SUCCESS);
                baseMapper.updateById(projectRecord);
            }
        }
    }

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