package com.kcim.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kcim.common.enums.ReportTypeEnum;
import com.kcim.common.exception.CostException;
import com.kcim.common.util.BeanUtil;
import com.kcim.common.util.CommonUtil;
import com.kcim.dao.mapper.ReportFormMapper;
import com.kcim.dao.model.ReportForm;
import com.kcim.dao.model.ReportRelation;
import com.kcim.dao.model.dto.CopyReportDTO;
import com.kcim.dao.model.dto.ReportFormEditDTO;
import com.kcim.dao.model.dto.ReportFormSaveDTO;
import com.kcim.vo.ReportFormVO;
import com.kcim.vo.SessionUserVO;
import com.kcim.service.ReportFormService;
import com.kcim.service.ReportRelationService;
import com.kcim.common.constants.NumberConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;


@Slf4j
@Service("reportFormService")
public class ReportFormServiceImpl extends ServiceImpl<ReportFormMapper, ReportForm> implements ReportFormService {

    private static final String LIMIT_ONE = "limit 1";
    private final ReportRelationService reportRelationService;

    public ReportFormServiceImpl(ReportRelationService reportRelationService) {
        this.reportRelationService = reportRelationService;
    }

    /**
     * 报表项目列表（必定只有两层）
     *
     * @param reportType 报表类型
     * @param user       当前登录用户
     * @return {@link ReportFormVO}
     */
    @Override
    public List<ReportFormVO> selectList(Integer reportType, SessionUserVO user) {
        List<ReportForm> list = this.list(
                new LambdaQueryWrapper<ReportForm>()
                        .eq(ReportForm::getReportType, reportType)
                        .eq(ReportForm::getHospId, user.getHospId())
                        .orderByAsc(ReportForm::getSort)
        );
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        List<ReportFormVO> reportFormVOS = list.stream().map(i -> {
            ReportFormVO reportFormVO = BeanUtil.convertObj(i, ReportFormVO.class);
            if (i.getCalcType() == 1) {
                reportFormVO.setShowAddRelation(1);
            } else if (i.getCalcType() == 2) {
                reportFormVO.setShowAddRelation(2);
            } else if (i.getCalcType() == 5) {
                reportFormVO.setShowAddRelation(3);
            }
            if (i.getDataType() == 1) {
                reportFormVO.setDataTypeName("数值");
            } else if (i.getDataType() == 2) {
                reportFormVO.setDataTypeName("百分比");
            } else {
                reportFormVO.setDataTypeName("未知");
            }
            return reportFormVO;
        }).collect(Collectors.toList());

        Map<Long, List<ReportFormVO>> collect = reportFormVOS.stream().collect(Collectors.groupingBy(ReportFormVO::getParentId));
        List<ReportFormVO> roots = collect.get(NumberConstant.ZERO_L);
        collect.remove(NumberConstant.ZERO_L);
        for (ReportFormVO root : roots) {
            List<ReportFormVO> reportFormVOS1 = collect.get(root.getId());
            if(!CollectionUtils.isEmpty(reportFormVOS1)){
                root.setChildren(getChildren(collect.get(root.getId()),collect));
            }
        }
//
//
//        List<ReportFormVO> roots = reportFormVOS.stream().filter(i -> i.getParentId() == 0L).collect(Collectors.toList());
//
//
//
//
//
//        for (ReportFormVO root : roots) {
//            List<ReportFormVO> children = root.getChildren();
//            if (CollUtil.isEmpty(children)) {
//                children = new ArrayList<>();
//            }
//            for (ReportFormVO reportFormVO : reportFormVOS) {
//                if (reportFormVO.getParentId().equals(root.getId())) {
//                    children.add(reportFormVO);
////                    this.setRelation(reportFormVO, user.getHospId());
//                }
//            }
//            root.setChildren(children);
//        }
        return roots;
    }

    private List<ReportFormVO> getChildren(List<ReportFormVO> reportFormVOS, Map<Long, List<ReportFormVO>> collect) {
        if(!CollectionUtils.isEmpty(reportFormVOS)){
            reportFormVOS.forEach(reportFormVO -> reportFormVO.setChildren(getChildren(collect.get(reportFormVO.getId()), collect)));
            return reportFormVOS;
        }
        return new ArrayList<>();

    }


    /**
     * 新增一个报表项目
     *
     * @param reportFormDTO {@link ReportFormSaveDTO}
     * @param hospId        医院id
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
    public void saveReport(ReportFormSaveDTO reportFormDTO, Long hospId) {
        ReportForm reportForm = BeanUtil.convertObj(reportFormDTO, ReportForm.class);
        Integer num = this.createNum(hospId, reportFormDTO.getReportType());
        reportForm.setHospId(hospId)
                .setNum(num)
                .setCreateTime(System.currentTimeMillis());
        // 如果新增的是父节点
        Long parentId = reportFormDTO.getParentId();
        // 根据是否父节点设置必要的数据
        setDataByParentId(hospId, reportForm, parentId);

        this.save(reportForm);

    }

    /**
     * 根据是否父节点设置必要的数据
     * ***后台默认处理报表节点不一致的问题***
     *
     * @param hospId     医院id
     * @param reportForm 需要构建的实体
     * @param parentId   父级id
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void setDataByParentId(Long hospId, ReportForm reportForm, Long parentId) {
        if (parentId == 0L) {
//            reportForm.setCalcType(0);
//            reportForm.setCalcFormula("");
//            reportForm.setReportType(0);
            // 如果编辑的时候子集的reportType 要和父级一致
            Long id = reportForm.getId();
            List<ReportForm> list = this.list(
                    new LambdaQueryWrapper<ReportForm>().eq(ReportForm::getParentId, id).eq(ReportForm::getHospId, hospId)
            );
            if (!list.isEmpty()) {
                list.forEach(i -> i.setReportType(reportForm.getReportType()));
                this.updateBatchById(list);
            }
        } else {
            // 父子节点报表类型必须一致
            ReportForm byId = this.getById(parentId);
            checkExist(byId, "选择的父报表层级不正确");
            reportForm.setReportType(byId.getReportType());
            // 如果子节点 小计只能存在一个
            if (reportForm.getCalcType() == 3) {
                this.isSubtotal(parentId, hospId, reportForm.getId());
            }
            if (reportForm.getCalcType() == 4) {
                // 校验计算公式合法性 [7]+[11]-[22]
                this.checkCalcFormula(reportForm.getCalcFormula(), hospId, reportForm.getReportType());
            }
        }
    }

    /**
     * 校验表达式中的内容是否存在
     *
     * @param calcFormula 表达式 [7]+[11]-[22]
     */
    private void checkCalcFormula(String calcFormula, Long hospId, Integer reportType) {
        // 校验公式不存在单独的'[' 或 ']'，中括号未闭合
        if (StrUtil.isBlank(calcFormula)) {
            throw new CostException("计算公式未传递");
        }
        boolean flag = CommonUtil.whetherStringClose(calcFormula);
        if (!flag) {
            throw new CostException("计算公式错误，英文中括号未正确闭合");
        }
        // 以下校验公式编号是否合法
        Pattern pattern = Pattern.compile("(\\[[^\\]]*\\])");
        Matcher m = pattern.matcher(calcFormula);
        List<String> list = new ArrayList<>();
        while (m.find()) {
            if (StrUtil.isNotBlank(m.group())) {
                list.add(m.group());
            }
        }
        if (list.size() == 0) {
            throw new CostException("计算公式错误，请使用英文输入状态下括号标识");
        }
        // 校验所有编号不是同一个
        Set<String> set = new HashSet<>(list);
        if (set.size() != list.size()) {
            throw new CostException("公式中编号有重复");
        }

        // 校验编号是否存在
        list.forEach(i -> {
            String sub = StrUtil.sub(i, i.indexOf("[") + 1, i.lastIndexOf("]"));
            ReportForm one = this.getOne(
                    new LambdaQueryWrapper<ReportForm>().select(ReportForm::getId)
                            .eq(ReportForm::getNum, sub)
                            .eq(ReportForm::getHospId, hospId)
                            .eq(ReportForm::getReportType, reportType)
                            .last(LIMIT_ONE)
            );
            if (Objects.isNull(one)) {
                throw new CostException("计算公式中编号不存在");
            }
        });
    }

    /**
     * 子类型中小计计算类型只能存在一个
     * 判断是否存在小计的计算类型，存在抛出异常
     *
     * @param id
     * @param parentId 父级id
     * @param hospId   医院id
     */
    private void isSubtotal(Long id, Long parentId, Long hospId) {
        List<ReportForm> list = this.list(
                new LambdaQueryWrapper<ReportForm>().select(ReportForm::getId)
                        .eq(ReportForm::getParentId, parentId).eq(ReportForm::getHospId, hospId).eq(ReportForm::getCalcType, NumberConstant.THREE)
        ).stream().filter(i -> !i.getId().equals(id)).collect(Collectors.toList());

        if (list.isEmpty()) {
            return;
        }
        throw new CostException("小计类型已存在，请修改计算类型");
    }

    /**
     * 创建最新的num 获取最大的num + 1
     *
     * @param hospId     医院id
     * @param reportType 计算类型
     * @return 返回本医院最新编号
     */
    private Integer createNum(Long hospId, Integer reportType) {
        ReportForm one = this.getOne(
                new LambdaQueryWrapper<ReportForm>().select(ReportForm::getNum)
                        .eq(ReportForm::getHospId, hospId)
                        .eq(ReportForm::getReportType, reportType)
                        .orderByDesc(ReportForm::getNum).last(LIMIT_ONE)
        );
        if (Objects.isNull(one)) {
            return 1;
        }
        return one.getNum() + 1;
    }

    /**
     * 更新某个报表项目
     *
     * @param formEditDTO {@link ReportFormEditDTO}
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
    public void updateReport(ReportFormEditDTO formEditDTO) {
        Long id = formEditDTO.getId();
        ReportForm byId = this.getById(id);
        checkExist(byId, "选择的报表项目不存在");

        BeanUtil.convertObj(formEditDTO, byId);

        Long parentId = byId.getParentId();
        // 根据是否父节点设置必要的数据
        this.setDataByParentId(byId.getHospId(), byId, parentId);

        this.updateById(byId);
    }

    /**
     * 抛出异常
     *
     * @param byId     POJO类
     * @param errorMsg 抛出信息
     */
    private void checkExist(ReportForm byId, String errorMsg) {
        if (Objects.isNull(byId)) {
            throw new CostException(errorMsg);
        }
    }

    /**
     * 辅助数据
     * 计算过于复杂建议多线程处理
     *
     * @param copyReportDTO {@link CopyReportDTO}
     * @param hospId        医院id
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
    public void copyReport(CopyReportDTO copyReportDTO, Long hospId) {
        Integer toReportType = copyReportDTO.getToReportType();
        Integer fromReportType = copyReportDTO.getFromReportType();
        List<ReportForm> fromReport = this.list(
                new LambdaQueryWrapper<ReportForm>()
                        .eq(ReportForm::getHospId, hospId)
                        .eq(ReportForm::getReportType, fromReportType)
        );
        if (fromReport.isEmpty()) {
            throw new CostException("复制的报表类型下不存在数据");
        }

        //查询需要复制到的数据id 用于处理关联数据
        List<ReportForm> oldReport = this.list(
                new LambdaQueryWrapper<ReportForm>()
                        .eq(ReportForm::getHospId, hospId)
                        .eq(ReportForm::getReportType, toReportType)
        );
        // 删除掉需要复制到的数据
        this.remove(new LambdaQueryWrapper<ReportForm>().eq(ReportForm::getHospId, hospId).eq(ReportForm::getReportType, toReportType));



        List<ReportForm> toReport = BeanUtil.convertList(fromReport, ReportForm.class);
        toReport.forEach(reportForm -> {
            reportForm.setReportType(toReportType);
            reportForm.setOldId(reportForm.getId());
            reportForm.setCreateTime(System.currentTimeMillis());
            reportForm.setId(null);
        });
        this.saveBatch(toReport);
        Map<Long,Long> oldIdNewIdMap = toReport.stream().collect(Collectors.toMap(ReportForm::getOldId, ReportForm::getId, (a, b) -> b));

        for (ReportForm reportForm : toReport) {
            Long parentId = reportForm.getParentId();
            if(parentId.equals(NumberConstant.ZERO_L)){
                continue;
            }
            reportForm.setParentId(oldIdNewIdMap.get(parentId));
        }
        this.updateBatchById(toReport);
        //处理关联项目
        /*
          1  查询复制报表的所有关联
          2 将所有关联的reportId 替换成新的reportId
         */
        List<ReportRelation> fromReportRelation = reportRelationService.list(
                new LambdaQueryWrapper<ReportRelation>()
                        .eq(ReportRelation::getHospId, hospId)
                        .in(ReportRelation::getReportId, fromReport.stream().map(ReportForm::getId).collect(Collectors.toList()))
        );
        //无关联对象 不需要处理
        if(!CollectionUtils.isEmpty(fromReportRelation)){
            List<ReportRelation> toReportRelation = BeanUtil.convertList(fromReportRelation, ReportRelation.class);

            for (ReportRelation reportRelation : toReportRelation) {
                reportRelation.setId(null);
                reportRelation.setReportId(oldIdNewIdMap.get(reportRelation.getReportId()));
            }
            //移除复制到的数据历史关联
            if(!CollectionUtils.isEmpty(oldReport)){
                reportRelationService.remove(new LambdaQueryWrapper<ReportRelation>().eq(ReportRelation::getHospId, hospId).in(ReportRelation::getReportId, oldReport.stream().map(ReportForm::getId).collect(Collectors.toList())));
            }
            reportRelationService.saveBatch(toReportRelation);
        }




//        // 先添加父层级数据
//        list.forEach(i -> {
//            if (i.getParentId() == 0) {
//                // 旧id
//                i.setOldId(i.getId());
//                i.setId(null);
//                i.setReportType(toReportType);
//                i.setCreateTime(System.currentTimeMillis());
//                // 父节点不存在计算公式问题
//                this.save(i);
//            }
//        });

        // 处理子节点 以及子节点关联的关系
//        List<ReportForm> childrenForms = list.stream().filter(i -> i.getParentId() != 0).collect(Collectors.toList());
//        List<ReportForm> newParentForms = this.list(
//                new LambdaQueryWrapper<ReportForm>()
//                        .select(ReportForm::getOldId, ReportForm::getId)
//                        .eq(ReportForm::getHospId, hospId)
//                        .eq(ReportForm::getReportType, toReportType)
//                        .eq(ReportForm::getParentId, 0)
//        );
//        if (newParentForms.isEmpty()) {
//            return;
//        }
//        List<ReportRelation> relations = reportRelationService.list(
//                new LambdaQueryWrapper<ReportRelation>()
//                        .eq(ReportRelation::getHospId, hospId)
//                        .in(ReportRelation::getReportId, childrenForms.stream().map(ReportForm::getId).collect(Collectors.toList()))
//        );
//        if (childrenForms.isEmpty()) {
//            return;
//        }
//        childrenForms.forEach(i -> {
//            newParentForms.forEach(j -> {
//                if (j.getOldId() != 0 && j.getOldId().equals(i.getParentId())) {
//                    i.setOldId(i.getId());
//                    i.setId(null);
//                    i.setParentId(j.getId());
//                    i.setCreateTime(System.currentTimeMillis());
//                    i.setReportType(toReportType);
//                    this.save(i);
//                }
//            });
//        });

        // 得到新的子节点
//        List<ReportForm> newSonForms = this.list(
//                new LambdaQueryWrapper<ReportForm>()
//                        .select(ReportForm::getOldId, ReportForm::getId)
//                        .eq(ReportForm::getHospId, hospId)
//                        .eq(ReportForm::getReportType, toReportType)
//                        .ne(ReportForm::getParentId, 0)
//        );

//        if (relations.isEmpty() || newSonForms.isEmpty()) {
//            return;
//        }
//        if(!CollectionUtils.isEmpty(relations)){
//            relations.forEach(relation -> relation.setId(null));
//
//        }
//        newSonForms.forEach(i -> {
//            relations.forEach(j -> {
//                if (i.getOldId() != 0 && i.getOldId().equals(j.getReportId())) {
//                    j.setReportId(i.getId());
//
//                    reportRelationService.save(j);
//                }
//            });
//        });
    }

    /**
     * 通过报表类型获取该医院报表列表
     *
     * @param hospId 医院id
     * @param type   报表类型 0.损益表、1.完全成本法表、2.变动成本表、3.全院损益表、4.全成本报表
     * @return ReportFrom List ,可能返回null 调用者自行判断
     */
    @Override
    public List<ReportForm> getListByReportType(Long hospId, Integer type) {
        List<ReportForm> list = this.list(
                new LambdaQueryWrapper<ReportForm>()
                        .eq(ReportForm::getReportType, type)
                        .eq(ReportForm::getHospId, hospId)
//                        .orderByAsc(ReportForm::getSort)
        );
        return list;
    }

    /**
     * 通过parentId 得到下面所有的分类
     *
     * @param hospId   医院id
     * @param parentId 父级id
     * @return List
     */
    @Override
    public List<ReportForm> getByParentId(Long hospId, Long parentId) {
        List<ReportForm> list = this.list(
                new LambdaQueryWrapper<ReportForm>()
                        .eq(ReportForm::getHospId, hospId)
                        .eq(ReportForm::getParentId, parentId)
                        .orderByAsc(ReportForm::getSort)
        );
        return list;
    }

    /**
     * 全院损益的树状结构
     *
     * @param hospId 医院id
     */
    @Override
    public List<ReportFormVO> getAllHospList(Long hospId) {
        List<ReportForm> list = this.list(
                new LambdaQueryWrapper<ReportForm>()
                        .eq(ReportForm::getReportType, ReportTypeEnum.HOSP_PROFIT_LOSS.getType())
                        .eq(ReportForm::getHospId, hospId)
                        .orderByAsc(ReportForm::getSort)
        );
        List<ReportFormVO> reportFormVOS = BeanUtil.convertList(list, ReportFormVO.class);
        List<ReportFormVO> vos = reportFormVOS.stream().filter(i -> i.getParentId() == 0)
                .peek(i -> i.setChildren(this.getSon(i, reportFormVOS)))
                .collect(Collectors.toList());
        return vos;
    }

    private List<ReportFormVO> getSon(ReportFormVO vo, List<ReportFormVO> reportFormVOS) {
        return reportFormVOS.stream().filter(o -> vo.getId().equals(o.getParentId())).collect(Collectors.toList());
    }

    /**
     * 校验这个医院全院损益计算的时候是否存在默认损益
     *
     * @param hospId
     */
    @Override
    @Transactional(rollbackFor = Throwable.class,propagation = Propagation.REQUIRED)
    public void checkExistLoss(Long hospId) {
        checkExistLoss(hospId,ReportTypeEnum.HOSP_PROFIT_LOSS.getType());
    }

    /**
     * 校验指定类型的全院损益报表是否存在默认损益
     * @param hospId
     * @param reportType
     */
    @Override
    public void checkExistLoss(Long hospId, Integer reportType) {
        List<ReportForm> list = this.list(
                new LambdaQueryWrapper<ReportForm>()
                        .eq(ReportForm::getReportType, reportType)
                        .eq(ReportForm::getHospId, hospId)
                        .eq(ReportForm::getIsLoss, 1)
        );
        if (list.size() != 1) {
            throw new CostException("请先设置默认损益类型,全院损益中默认损益只能有一个");
        }
    }

    @Override
    public Object getResponsibilities(String reportType, Long reportId) {


        return null;
    }

    /**
     * 此方法查询的数据包括已作废的数据，调用时需要区分
     * @param hospId 院区id
     * @param reportType 报表类型
     * @param reportId 报表id
     * @return 报表项目配置
     */
    @Override
    public ReportForm getByReportId(Long hospId, String reportType, Long reportId) {
        return this.baseMapper.getByReportId(hospId,reportType,reportId);


    }

    @Override
    public List<ReportForm> getByReportType(Long hospId, String reportType) {
        return this.baseMapper.getByReportType(hospId,reportType);


    }
}