package com.kcim.service.impl;

import com.kcim.common.constants.Constant;
import com.kcim.common.constants.NumberConstant;
import com.kcim.common.constants.ParameterConstant;
import com.kcim.common.enums.EmpCostComputeTypeEnum;
import com.kcim.common.exception.CostException;
import com.kcim.common.util.BeanUtil;
import com.kcim.common.util.UserContext;
import com.kcim.dao.model.ComputeEmpCost;
import com.kcim.dao.model.EmpCostMap;
import com.kcim.dao.model.EmpCostType;
import com.kcim.dao.model.ImportEmpCost;
import com.kcim.dao.repository.ComputeEmpCostRepository;
import com.kcim.dao.repository.EmpCostMapRepository;
import com.kcim.dao.repository.EmpCostTypeRepository;
import com.kcim.dao.repository.ImportEmpCostRepository;
import com.kcim.service.CenterService;
import com.kcim.service.EmpCostCalculateService;
import com.kcim.vo.CommonParameterVo;
import com.kcim.vo.DictDataVo;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

import static com.kcim.common.enums.EmpCostComputeTypeEnum.POSITION;

/**
 * @program: CostAccount
 * @description: 单位人事成本计算实现类
 * @author: Wang.YS
 * @create: 2023-10-23 16:11
 **/
@Service("EmpCostCalculateService")
@Slf4j
@AllArgsConstructor
public class EmpCostCalculateServiceImpl implements EmpCostCalculateService {
    /**
     * 月度人事成本计算
     */
    ComputeEmpCostRepository repository;
    /**
     * 人事分类字典
     */
    EmpCostTypeRepository empCostTypeRepository;
    /**
     * 人事分类与人员对照
     */
    EmpCostMapRepository empCostMapRepository;
    /**
     * 人事成本导入数据
     */
    ImportEmpCostRepository importEmpCostRepository;
    CenterService centerService;
    /**
     * 成本核算-单位人事成本计算-获取计算列表
     *
     * @param computeDate 核算年月
     * @param filter      过滤  人事分类名称
     * @return 列表
     */
    @Override
    public Object getEmpCostCalculateList(String computeDate, String filter) {

        if(!StringUtils.isEmpty(filter)){
            List<ComputeEmpCost> filterList = repository.getByComputeDate(computeDate, filter);
            if(!CollectionUtils.isEmpty(filterList)){
                List<ComputeEmpCost> parentList = repository.getByParentComputeDate(computeDate);
                Map<String,ComputeEmpCost> parentMap = parentList.stream().collect(Collectors.toMap(ComputeEmpCost::getCode, computeEmpCost -> computeEmpCost, (a, b) -> b));

                //先取出过滤条件中的 上层数据
                List<ComputeEmpCost> collect = filterList.stream().filter(f -> f.getParentCode().equals(NumberConstant.ZERO_S)).collect(Collectors.toList());
                if(!CollectionUtils.isEmpty(collect)){
                    filterList.removeAll(collect);
                }
                //子集去除上层数据 是否还有数据
                if(!CollectionUtils.isEmpty(filterList)){
                    //找出符合条件的 子集 没有上层的数据  在所有上层数据中找到父类用于显示
                    for (ComputeEmpCost computeEmpCost : filterList) {
                        //组装成符合条件的上层数据中
                        collect.add(parentMap.get(computeEmpCost.getParentCode()));
                    }
                    //可能存在重复 去重 用于显示的最外层
                    List<ComputeEmpCost> collectParent = collect.stream().distinct().collect(Collectors.toList());
                    Map<String, List<ComputeEmpCost>> map = filterList.stream().collect(Collectors.groupingBy(ComputeEmpCost::getParentCode));
                    for (ComputeEmpCost empCost : collectParent) {
                        List<ComputeEmpCost> childList = map.get(empCost.getCode());
                        if(!CollectionUtils.isEmpty(childList)){
                            empCost.setChildList(childList);
                        }
                    }
                    return collectParent;
                }else {
                    return collect;
                }
            }else {
                return new ArrayList<>();
            }
        }else {
            List<ComputeEmpCost> list = repository.getByComputeDate(computeDate);
            if(!CollectionUtils.isEmpty(list)){
                Map<String, List<ComputeEmpCost>> map = list.stream().collect(Collectors.groupingBy(ComputeEmpCost::getParentCode));
                List<ComputeEmpCost> parent = map.get(NumberConstant.ZERO_S);
                for (ComputeEmpCost empCost : parent) {
                    List<ComputeEmpCost> childList = map.get(empCost.getCode());
                    if(!CollectionUtils.isEmpty(childList)){
                        empCost.setChildList(childList);
                    }
                }
                return parent;
            }else {
                return new ArrayList<>();
            }
        }
    }

    /**
     * 成本核算-单位人事成本计算-计算
     *
     * @param computeDate 核算年月
     */
    @Override
    public void empCostCalculate(String computeDate) {

        List<EmpCostType> list = empCostTypeRepository.getList();
        if(CollectionUtils.isEmpty(list)){
            log.error("当前院区未维护人事分类,计算中止");
            throw new CostException("当前院区未维护人事分类,计算中止");
        }
        List<EmpCostMap> empCostMapList = empCostMapRepository.getList();
        if(CollectionUtils.isEmpty(empCostMapList)){
            log.info("当前院区人事分类未对照人员信息,计算中止");
            throw new CostException("当前院区人事分类未对照人员信息,计算中止");
        }
        List<ImportEmpCost> importEmpCosts = importEmpCostRepository.getByComputeDate(computeDate);
        if(CollectionUtils.isEmpty(importEmpCosts)){
            log.info("当前核算年月【"+computeDate+"】未导入人事成本数据");
            throw new CostException("当前核算年月【"+computeDate+"】未导入人事成本数据,计算中止");
        }

        //获取中台科室字典
        Map<String, String> department = centerService.getDepartment();
        if(CollectionUtils.isEmpty(department)){
            throw new CostException("未获取到当前院区科室信息，计算中止");
        }

        //获取中台岗位字典
        DictDataVo dict = centerService.getCenterDict(Constant.POSITION);

        List<DictDataVo> dataVoList = dict.getDataVoList();
        Map<String,String> positionMap = dataVoList.stream().collect(Collectors.toMap(DictDataVo::getCode, DictDataVo::getName, (a, b) -> b));
        //获取中台应工作天数字典

        DictDataVo monthWorkdays = centerService.getCenterDict(Constant.MONTH_WORKDAYS);

        Map<String, String> monthWorkdayMap = monthWorkdays.getDataVoList().stream().collect(Collectors.toMap(DictDataVo::getCode, DictDataVo::getExpandOne, (a, b) -> b));
        String currentWorkDay = monthWorkdayMap.get(computeDate);

        //获取每日工时参数
        CommonParameterVo parameter = centerService.getParameter(ParameterConstant.DAY_HOUR);
        String dayHour = parameter.getValue();
        if(StringUtils.isEmpty(dayHour)){
            throw new CostException("中台未找到每日工时参数， 请确认参数数据是否正确");
        }

        Map<String,ImportEmpCost> importEmpCostMap = importEmpCosts.stream().collect(Collectors.toMap(ImportEmpCost::getEmpCode, importEmpCost -> importEmpCost, (a, b) -> b));
        Map<String, List<EmpCostMap>> costTypeGroup = empCostMapList.stream().collect(Collectors.groupingBy(EmpCostMap::getCostTypeCode));
        ComputeEmpCost computeEmpCost = new ComputeEmpCost();
        computeEmpCost.setHospId(UserContext.getHospId());
        computeEmpCost.setComputeDate(computeDate);
        computeEmpCost.setCreateUser(String.valueOf(UserContext.getCurrentUser().getId()));
        computeEmpCost.setCreateTime(new Date());

        List<ComputeEmpCost> saveList = new ArrayList<>();
        for (EmpCostType empCostType : list) {
            ComputeEmpCost empCost = BeanUtil.convertObj(computeEmpCost, ComputeEmpCost.class);
            String code = empCostType.getCostTypeCode();
            empCost.setCode(code);
            empCost.setName(empCostType.getCostTypeName());
            empCost.setParentCode(NumberConstant.ZERO_S);
            empCost.setEmpType(code);
            String computeType = empCostType.getComputeType();

            if(!CollectionUtils.isEmpty(costTypeGroup)){
                //有子类 计算子类项目
                List<EmpCostMap> empCostMaps = costTypeGroup.get(code);
                if(!CollectionUtils.isEmpty(empCostMaps)){
                    List<String> collect = empCostMaps.stream().map(EmpCostMap::getAccount).collect(Collectors.toList());
                    List<ImportEmpCost> costTypeEmpCost = collect.stream().map(importEmpCostMap::get).filter(Objects::nonNull).collect(Collectors.toList());
                    log.info("----for-8----"+costTypeEmpCost.toString());

                    if(!CollectionUtils.isEmpty(costTypeEmpCost)){
                        // 实际编制人数
                        int empNum = costTypeEmpCost.size();
                        empCost.setEmpNum(empNum);
                        setSaveCost(currentWorkDay, dayHour, empCost, empNum, costTypeEmpCost);
                        if(computeType.equals(EmpCostComputeTypeEnum.DEPARTMENT.getCode())){
                            //按科室计算子类

                            Map<String,ImportEmpCost> empCostMap = costTypeEmpCost.stream().collect(Collectors.toMap(ImportEmpCost::getEmpCode, importEmpCost -> importEmpCost, (a, b) -> b));
                            Map<String, List<EmpCostMap>> departGroup = empCostMaps.stream().collect(Collectors.groupingBy(EmpCostMap::getDepartmentCode));
                            for (String s : departGroup.keySet()) {
                                ComputeEmpCost empChildCost = BeanUtil.convertObj(computeEmpCost, ComputeEmpCost.class);
                                //单个科室所有人员
                                List<EmpCostMap> empCostMaps1 = departGroup.get(s);
                                //人员工号
                                List<String> collect1 = empCostMaps1.stream().map(EmpCostMap::getAccount).collect(Collectors.toList());
                                //获取实院导入数据
                                List<ImportEmpCost> costTypeChildEmpCost = collect1.stream().map(empCostMap::get).filter(Objects::nonNull).collect(Collectors.toList());
                                empChildCost.setCode(s);
                                empChildCost.setParentCode(code);
                                empChildCost.setEmpType(code);
                                empChildCost.setName(department.get(s));
                                if(!CollectionUtils.isEmpty(costTypeChildEmpCost)){
                                    empChildCost.setEmpNum(costTypeChildEmpCost.size());// 人数
                                    setSaveCost(currentWorkDay, dayHour, empChildCost, costTypeChildEmpCost.size(), costTypeChildEmpCost);
                                    saveList.add(empChildCost);
                                }
                            }
                        } else if (computeType.equals(POSITION.getCode())) {

                            //按岗位计算子类
                            Map<String,ImportEmpCost> empCostMap = costTypeEmpCost.stream().collect(Collectors.toMap(ImportEmpCost::getEmpCode, importEmpCost -> importEmpCost, (a, b) -> b));
                            Map<String, List<EmpCostMap>> positionGroup = empCostMaps.stream().collect(Collectors.groupingBy(EmpCostMap::getPosition));
                            for (String s : positionGroup.keySet()) {
                                ComputeEmpCost empChildCost = BeanUtil.convertObj(computeEmpCost, ComputeEmpCost.class);
                                //单个科室所有人员
                                List<EmpCostMap> empCostMaps1 = positionGroup.get(s);
                                //人员工号
                                List<String> collect1 = empCostMaps1.stream().map(EmpCostMap::getAccount).collect(Collectors.toList());
                                //获取实院导入数据
                                List<ImportEmpCost> costTypeChildEmpCost = collect1.stream().map(empCostMap::get).filter(Objects::nonNull).collect(Collectors.toList());
                                empChildCost.setCode(s);
                                empChildCost.setParentCode(code);
                                empChildCost.setEmpType(code);
                                empChildCost.setName(positionMap.get(s));
                                if(!CollectionUtils.isEmpty(costTypeChildEmpCost)){
                                    empChildCost.setEmpNum(costTypeChildEmpCost.size());// 实际总工时
                                    setSaveCost(currentWorkDay, dayHour, empChildCost, costTypeChildEmpCost.size(), costTypeChildEmpCost);
                                    saveList.add(empChildCost);
                                }
                            }
                        }
                    }
                }
            }else {
                //子类人员为空 无子集
                empCost.setEmpNum(NumberConstant.ZERO);
                // 实际总工时
                empCost.setActualHour(BigDecimal.ZERO);
                // 预计总工时 预计总工时=应上班天数*实际编制人数*每日工时
                empCost.setExpectHour(BigDecimal.ZERO);
                // 总成本
                empCost.setTotalCost(BigDecimal.ZERO);
                // 人均成本
                empCost.setAvgCost(BigDecimal.ZERO);
                // 每分钟成本
                empCost.setMinuteCost(BigDecimal.ZERO);
            }
            saveList.add(empCost);

        }
        if(!CollectionUtils.isEmpty(saveList)){
            //判断当前核算年月是否有数据 有 作废 无保存
            repository.removeByComputeDate(computeDate);

            repository.saveBatch(saveList);
        }
    }

    private void setSaveCost(String currentWorkDay, String dayHour, ComputeEmpCost empCost, int empNum, List<ImportEmpCost> costTypeEmpCost) {
        // 实际总工时
        BigDecimal totalHour = computeTotalHour(costTypeEmpCost);
        empCost.setActualHour(totalHour);
        // 预计总工时 预计总工时=应上班天数*实际编制人数*每日工时
        BigDecimal expectHour = computeExpectHour(empNum, currentWorkDay, dayHour);
        empCost.setExpectHour(expectHour);
        // 总成本
        BigDecimal totalCost = computeTotalCost(costTypeEmpCost);
        empCost.setTotalCost(totalCost);
        // 人均成本
        BigDecimal avgCost = computeAvgCost(totalCost, empNum);
        empCost.setAvgCost(avgCost);
        // 每分钟成本
        BigDecimal minuteCost = computeMinuteCost(totalCost, totalHour);
        empCost.setMinuteCost(minuteCost);
    }

    private BigDecimal computeExpectHour(int size, String i, String v) {
        double hour = size * Double.parseDouble(i) * Double.parseDouble(v);
        return BigDecimal.valueOf(hour);
    }

    /**
     * 计算每分钟成本 总成本/实际工时/60
     * @param total 总成本
     * @param totalHour 总工时
     * @return 每分钟成本
     */
    private BigDecimal computeMinuteCost(BigDecimal total, BigDecimal totalHour) {
        return total.divide(totalHour,4,RoundingMode.HALF_UP).divide(BigDecimal.valueOf(60),4,RoundingMode.HALF_UP).setScale(4,RoundingMode.HALF_UP);

    }

    /**
     * 计算总工时
     * @param costTypeEmpCost 导入数据
     * @return 总工时
     */
    private BigDecimal computeTotalHour(List<ImportEmpCost> costTypeEmpCost) {
        BigDecimal total = new BigDecimal("0.0000");
        for (ImportEmpCost importEmpCost : costTypeEmpCost) {
            total = total.add(importEmpCost.getHour());
        }
        return total;
    }

    /**
     * 计算人均成本 总成本/实际编制人数
     * @param total 总成本
     * @param size 实际编制人数
     * @return 人均成本
     */
    private BigDecimal computeAvgCost(BigDecimal total, int size) {
        return total.divide(BigDecimal.valueOf(size),4,RoundingMode.HALF_UP);

    }

    /**
     * 计算总成本
     * @param costTypeEmpCost 列表数据
     * @return 总成本
     */
    private BigDecimal computeTotalCost(List<ImportEmpCost> costTypeEmpCost) {
        BigDecimal total = new BigDecimal("0.0000");
        for (ImportEmpCost importEmpCost : costTypeEmpCost) {
            total = total.add(importEmpCost.getCost());
        }
        return total;
    }
}
