package com.kcim.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kcim.common.constants.NumberConstant;
import com.kcim.common.constants.SQLParameter;
import com.kcim.common.enums.CustomSqlTypeEnum;
import com.kcim.common.exception.CostException;
import com.kcim.common.util.ComputeDateUtils;
import com.kcim.dao.model.ComputeMedicalDepartmentProfit;
import com.kcim.dao.model.ReportForm;
import com.kcim.dao.model.Responsibility;
import com.kcim.dao.model.UserResponsibilityRight;
import com.kcim.dao.repository.ComputeMedicalDepartmentProfitRepository;
import com.kcim.dao.repository.CostDepartmentProfitRepository;
import com.kcim.service.*;
import com.kcim.vo.MedicalResponsibilityVo;
import com.kcim.vo.ReportFormProfitVo;
import com.kcim.web.reponse.MedicalProfitResponse;
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.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @program: CostAccount
 * @description:
 * @author: Wang.YS
 * @create: 2024-09-25 15:35
 **/
@Service("ComputeMedicalDepartmentProfitService")
@Slf4j
public class ComputeMedicalDepartmentProfitServiceImpl implements ComputeMedicalDepartmentProfitService {


    SqlService sqlService;
    ComputeMedicalDepartmentProfitRepository repository;
    ResponsibilityService responsibilityService ;
    ReportFormService reportFormService;
    CostDepartmentProfitRepository costDepartmentProfitRepository;
    UserResponsibilityRightService userResponsibilityRightService;

    public ComputeMedicalDepartmentProfitServiceImpl(SqlService sqlService, ComputeMedicalDepartmentProfitRepository repository, ResponsibilityService responsibilityService, ReportFormService reportFormService,UserResponsibilityRightService userResponsibilityRightService, CostDepartmentProfitRepository costDepartmentProfitRepository) {
        this.sqlService = sqlService;
        this.repository = repository;
        this.responsibilityService = responsibilityService;
        this.reportFormService = reportFormService;
        this.userResponsibilityRightService=userResponsibilityRightService;
        this.costDepartmentProfitRepository = costDepartmentProfitRepository;
    }

    /**
     * 科室门住损益计算
     *
     * @param computeDate 核算年月
     * @param hospId      院区id
     * @param reportType  报表类型
     */
    @Override
    public void calcMedicalProfit(String computeDate, Long hospId, String reportType) {
//        Integer year = ComputeDateUtils.getComputeYear(computeDate);
//        Integer month = ComputeDateUtils.getComputeMonth(computeDate);
//        List<CostDepartmentProfit> departmentProfits = costDepartmentProfitRepository.getCurrentByReportType(year, month, hospId, reportType);
//        if (CollectionUtils.isEmpty(departmentProfits)) {
//            throw new CostException(500, "未进行科室损益计算");
//        }
        Map<String,String> sqlParameter = new HashMap<>();
        sqlParameter.put(SQLParameter.COMPUTE_DATE_CODE,computeDate);
        sqlParameter.put(SQLParameter.REPORT_TYPE_CODE,reportType);
        sqlService.autoExecuteSql(CustomSqlTypeEnum.INTEGRATIVE_PROFIT_CALC.getCode(),sqlParameter);
    }

    /**
     * 获取科室门住损益计算列表
     *
     * @param computeDate        核算年月
     * @param hospId             院区id
     * @param reportType         报表类型
     * @param responsibilityCode 责任中心代码
     * @param responsibilityType 责任中心类型
     * @return 列表
     */
    @Override
    public Object getMedicalProfit(String computeDate, Long hospId, String reportType, String responsibilityCode, Integer responsibilityType) {
        List<ComputeMedicalDepartmentProfit> medicalProfitList = repository.getByResponsibilityCode(computeDate, hospId,reportType,responsibilityCode,responsibilityType);
        if(CollectionUtils.isEmpty(medicalProfitList)){
            throw new CostException(reportType+"未进行科室门住损益计算，请先进行损益计算");
        }
        List<ComputeMedicalDepartmentProfit> model = medicalProfitList.stream().filter(distinctByKey(ComputeMedicalDepartmentProfit::getReportId)).collect(Collectors.toList());
        List<ComputeMedicalDepartmentProfit> collect = medicalProfitList.stream().filter(f -> f.getMedicalType() == null).collect(Collectors.toList());
        if(!CollectionUtils.isEmpty(collect)){
            throw new CostException("数据类型medicalType不能为空！");
        }
        List<ReportForm> byReportType = reportFormService.getByReportType(hospId, reportType);
        if(CollectionUtils.isEmpty(byReportType)){
            throw new CostException("未找到【"+reportType+"】对应的报表项目");
        }
        Map<Long, List<ReportForm>> reportIdMap = byReportType.stream().collect(Collectors.groupingBy(ReportForm::getId));
        Map<Long,Integer> sortMap = new HashMap<>();
        Map<Long,Integer> hideMap = new HashMap<>();
        for (ReportForm reportForm : byReportType) {
            if(reportForm.getSort() != null){
                sortMap.put(reportForm.getId(), reportForm.getSort());
            }
            if(reportForm.getHide() != null){
                hideMap.put(reportForm.getId(), reportForm.getHide());
            }
        }

        if(!CollectionUtils.isEmpty(sortMap)){
            for (ComputeMedicalDepartmentProfit profit : model) {
                profit.setSort(sortMap.get(profit.getReportId()));
            }
        }
        if(!CollectionUtils.isEmpty(hideMap)){
            for (ComputeMedicalDepartmentProfit profit : model) {
                if(hideMap.get(profit.getReportId())!= null){
                    profit.setHide(hideMap.get(profit.getReportId()));
                }else{
                    profit.setHide(NumberConstant.ONE);
                }
            }
        }else {
            model.forEach(profit -> profit.setHide(NumberConstant.ONE));
        }

        Map<Integer, List<ComputeMedicalDepartmentProfit>> profitGroup = medicalProfitList.stream().collect(Collectors.groupingBy(ComputeMedicalDepartmentProfit::getMedicalType));
        Map<Integer,Map<Long, BigDecimal>> profitAmountMap = new HashMap<>();
        profitGroup.forEach((k,v)->{
            Map<Long, BigDecimal> map = new HashMap<>();
            v.forEach(f->
                    map.put(f.getReportId(), f.getAmount())
                    );
            profitAmountMap.put(k,map);
        });
        DecimalFormat df = new DecimalFormat("0.00%");
        //获取上个月数据
        String preComputeDate = ComputeDateUtils.getPreComputeDate(computeDate);
        List<ComputeMedicalDepartmentProfit> preMonthMedicalProfitList = repository.getByResponsibilityCode(preComputeDate, hospId,reportType,responsibilityCode,responsibilityType);
        Map<Integer,Map<Long, BigDecimal>> preProfitAmountMap = new HashMap<>();
        Map<Integer,Map<Long, String>> preProfitPercentMap = new HashMap<>();
        handleData(profitAmountMap, df, preProfitAmountMap, preProfitPercentMap, preMonthMedicalProfitList);
        //获取上一年数据
        Map<Integer,Map<Long, BigDecimal>> preYearProfitAmountMap = new HashMap<>();
        Map<Integer,Map<Long, String>> preYearProfitPercentMap = new HashMap<>();
        String preComputeDateYear = ComputeDateUtils.getPreComputeDateYear(computeDate);
        List<ComputeMedicalDepartmentProfit> preYearMedicalProfitList = repository.getByResponsibilityCode(preComputeDateYear, hospId,reportType,responsibilityCode,responsibilityType);
        handleData(profitAmountMap, df, preYearProfitAmountMap, preYearProfitPercentMap, preYearMedicalProfitList);
        //todo:因报表项目调整存在找不到报表项目的情况，所以以本月所有数据为准作为模版
        List<ReportFormProfitVo> reportFormProfitVos = new ArrayList<>();
        List<ComputeMedicalDepartmentProfit> departmentProfits = model.stream().filter(f -> f.getHide().equals(NumberConstant.ONE)).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(departmentProfits)){
            throw new CostException("未找到需要显示的报表项目");
        }
        for (ComputeMedicalDepartmentProfit profit : departmentProfits) {
            ReportFormProfitVo vo = new ReportFormProfitVo();
            vo.setId(profit.getReportId());
            vo.setParentId(profit.getReportParentId());
            vo.setReportName(profit.getReportName());
            vo.setNum(profit.getReportNum());
            vo.setReportType(Integer.valueOf(reportType));
            vo.setSort(profit.getSort());
            List<ReportForm> profitReportFormList = reportIdMap.get(profit.getReportId());
            if(!CollectionUtils.isEmpty(profitReportFormList)) {
                vo.setDataType(profitReportFormList.get(NumberConstant.ZERO).getDataType());
                vo.setDecimalPlace(profitReportFormList.get(NumberConstant.ZERO).getDecimalPlace());
                vo.setPermil(profitReportFormList.get(NumberConstant.ZERO).getPermil());
                vo.setFontColor(profitReportFormList.get(NumberConstant.ZERO).getFontColor());
            }else {
                vo.setDataType(NumberConstant.ONE);
                vo.setDecimalPlace(NumberConstant.TWO);
                vo.setPermil(NumberConstant.ONE);
            }
            reportFormProfitVos.add(vo);
        }


//        List<ReportFormProfitVo> reportFormProfitVos = BeanUtil.convertList(reportFormList, ReportFormProfitVo.class);
        Map<Integer,List<ReportFormProfitVo>> responses = new HashMap<>();
        profitAmountMap.forEach((medicalType,amountMap)->{
            Map<Long, BigDecimal> preMonthAmount = new HashMap<>();
            if(!CollectionUtils.isEmpty(preProfitAmountMap)){
                preMonthAmount = preProfitAmountMap.get(medicalType);
            }
            Map<Long, String> preMonthPercent = new HashMap<>();
            if(!CollectionUtils.isEmpty(preProfitPercentMap)){
                preMonthPercent = preProfitPercentMap.get(medicalType);
            }
            Map<Long, BigDecimal> preYearAmount = new HashMap<>();
            if(!CollectionUtils.isEmpty(preYearProfitAmountMap)){
                preYearAmount = preYearProfitAmountMap.get(medicalType);
            }
            Map<Long, String> preYearPercent = new HashMap<>();
            if(!CollectionUtils.isEmpty(preYearProfitPercentMap)){
                preYearPercent = preYearProfitPercentMap.get(medicalType);
            }
            for (ReportFormProfitVo reportFormProfitVo : reportFormProfitVos) {
                reportFormProfitVo.setMedicalType(medicalType);

                BigDecimal bigDecimal = amountMap.get(reportFormProfitVo.getId());
                reportFormProfitVo.setCurrentAmount(bigDecimal);
                if(!CollectionUtils.isEmpty(preMonthAmount)){
                    BigDecimal value = preMonthAmount.get(reportFormProfitVo.getId());
                    if(value != null){
                        reportFormProfitVo.setPreMonthAmount(value);
                    }
                }
                if(!CollectionUtils.isEmpty(preMonthPercent)){
                    String value = preMonthPercent.get(reportFormProfitVo.getId());
                    if(!StringUtils.isEmpty(value)){
                        reportFormProfitVo.setPreMonthPercent(value);
                    }
                }
                if(!CollectionUtils.isEmpty(preYearAmount)){
                    BigDecimal value = preYearAmount.get(reportFormProfitVo.getId());
                    if(value != null){
                        reportFormProfitVo.setPreYearAmount(value);
                    }
                }
                if(!CollectionUtils.isEmpty(preYearPercent)){
                    String value  = preYearPercent.get(reportFormProfitVo.getId());
                    if(!StringUtils.isEmpty(value)){
                        reportFormProfitVo.setPreYearPercent(value);
                    }
                }
            }
            Map<Long, List<ReportFormProfitVo>> collectGroup = reportFormProfitVos.stream().collect(Collectors.groupingBy(ReportFormProfitVo::getParentId));
            List<ReportFormProfitVo> costProfitVos = collectGroup.get(NumberConstant.ZERO_L);
            collectGroup.remove(NumberConstant.ZERO_L);
            for (ReportFormProfitVo costProfitVo : costProfitVos) {
                List<ReportFormProfitVo> costProfitVo1 = collectGroup.get(costProfitVo.getId());
                if (!CollectionUtils.isEmpty(costProfitVo1)) {
                    costProfitVo.setChildren(setChildren(costProfitVo1, collectGroup));
                }
            }
            reportFormProfitSort(costProfitVos);
            responses.put(medicalType, costProfitVos);
        });
        if(!CollectionUtils.isEmpty(responses)){
            List<MedicalProfitResponse> returnList = new ArrayList<>();
            responses.forEach((k,v)->{
                MedicalProfitResponse response = new MedicalProfitResponse();
                response.setMedicalType(k);
                if(k.equals(NumberConstant.ONE)){
                    response.setMedicalTypeName("门诊");
                } else if (k.equals(NumberConstant.TWO)) {
                    response.setMedicalTypeName("住院");
                }else {
                    response.setMedicalTypeName("总计");
                }
                response.setData(v);
                returnList.add(response);
            });
            return returnList;
        }
        return new ArrayList<>();
    }
    static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Map<Object,Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;}
    /**
     * 计算同比 环比占比
     * @param profitAmountMap 当月数据
     * @param df 占比格式
     * @param preYearProfitAmountMap  数据值
     * @param preYearProfitPercentMap 数据占比
     * @param preYearMedicalProfitList 出参
     */
    private void handleData(Map<Integer, Map<Long, BigDecimal>> profitAmountMap, DecimalFormat df, Map<Integer, Map<Long, BigDecimal>> preYearProfitAmountMap, Map<Integer, Map<Long, String>> preYearProfitPercentMap, List<ComputeMedicalDepartmentProfit> preYearMedicalProfitList) {
        if(!CollectionUtils.isEmpty(preYearMedicalProfitList)){
            Map<Integer, List<ComputeMedicalDepartmentProfit>> preYearProfitGroup = preYearMedicalProfitList.stream().collect(Collectors.groupingBy(ComputeMedicalDepartmentProfit::getMedicalType));
            preYearProfitGroup.forEach((k,v)->{
                Map<Long, BigDecimal> map = new HashMap<>();
                v.forEach(f->
                        map.put(f.getReportId(), f.getAmount())
                );
                preYearProfitAmountMap.put(k,map);
            });
            profitAmountMap.forEach((medicalType,amountMap)->{
                Map<Long, BigDecimal> map = preYearProfitAmountMap.get(medicalType);
                if(CollectionUtils.isEmpty(map)){
                    return;
                }
                Map<Long, String> percent = new HashMap<>();
                amountMap.forEach((reportId,amount)->{
                    BigDecimal bigDecimal = map.get(reportId);
                    if(bigDecimal != null){
                        if(bigDecimal.compareTo(BigDecimal.ZERO.setScale(6, RoundingMode.HALF_UP)) != 0){
                            BigDecimal divide = amount.subtract(bigDecimal).divide(bigDecimal, 4, RoundingMode.HALF_UP);
                            percent.put(reportId,df.format(divide));
                        }

                    }
                });
                preYearProfitPercentMap.put(medicalType, percent);
            });
        }
    }

    private List<ReportFormProfitVo> setChildren(List<ReportFormProfitVo> child, Map<Long, List<ReportFormProfitVo>> collect) {
        for (ReportFormProfitVo costProfitVo : child) {
            if (!CollectionUtils.isEmpty(collect.get(costProfitVo.getId()))) {
                costProfitVo.setChildren(setChildren(collect.get(costProfitVo.getId()), collect));
            }
        }
        if (!CollectionUtils.isEmpty(child)) {
            child.sort(Comparator.comparing(ReportFormProfitVo::getSort, Comparator.nullsLast(Integer::compareTo)));
        }
        return child;
    }


    /**
     * 获取责任中心列表
     *
     * @param computeDate 核算年月
     * @param hospId      院区id
     * @param reportType  报表类型
     * @return 责任中心列表
     */
    @Override
    public Object getMedicalResponsibility(String computeDate, Long hospId, String reportType) {
        //获取当月所有数据 提取核算单元数据
        List<ComputeMedicalDepartmentProfit> medicalProfitList = repository.getByComputeDate(computeDate, hospId,reportType);
        if(CollectionUtils.isEmpty(medicalProfitList)){
            throw new CostException(reportType+"未进行科室门住损益计算");
        }
        //开启数据权限流程时
        if(userResponsibilityRightService.IsUserReportRightAble()){
            //获取用户所有有权限的责任中心
            List<UserResponsibilityRight> currentUserResponsibilityRights = userResponsibilityRightService.getUserResponsibilityRight();
            if(CollectionUtils.isEmpty(currentUserResponsibilityRights)){
                throw new CostException("您没有分配责任中心权限，请联系管理员");
            }
            //如果不是分配了全部责任中心权限则过滤没有权限的责任中心
            if(!currentUserResponsibilityRights.stream().anyMatch(userResponsibilityRight->
                    userResponsibilityRight.getResponsibilityCode().equals(NumberConstant.ZERO_S))){
                //筛选出有权限的责任中心对象
                medicalProfitList = medicalProfitList.stream().filter(responsibility ->
                        currentUserResponsibilityRights.stream().anyMatch(userRight ->
                                userRight.getResponsibilityCode().equals(responsibility.getResponsibilityCode()))).collect(Collectors.toList());
                if(CollectionUtils.isEmpty(medicalProfitList)){
                    throw new CostException("您没有对应的责任中心权限，请联系管理员");
                }
            }
        }
        //获取当前院区所有的责任中心
        List<Responsibility> responsibilityAllList = responsibilityService.list(new QueryWrapper<Responsibility>().lambda()
                .eq(Responsibility::getHospId, hospId));
        Map<Long, Responsibility> responsibilityIdMap = responsibilityAllList.stream().collect(Collectors.toMap(Responsibility::getId, responsibility -> responsibility, (a, b) -> b));
        Map<String, Responsibility> responsibilityCodeMap = responsibilityAllList.stream().collect(Collectors.toMap(Responsibility::getResponsibilityCode, responsibility -> responsibility, (a, b) -> b));
        //提取责任中心数据
        List<MedicalResponsibilityVo> computeResponsibityList = new ArrayList<>();
        for (ComputeMedicalDepartmentProfit profit : medicalProfitList) {
            if(!CollectionUtils.isEmpty(computeResponsibityList)){
                boolean match = computeResponsibityList.stream().anyMatch(f -> f.getResponsibilityCode().equals(profit.getResponsibilityCode()));
                if (match){
                    continue;
                }
            }
            MedicalResponsibilityVo vo = new MedicalResponsibilityVo();
            vo.setResponsibilityCode(profit.getResponsibilityCode());
            vo.setResponsibilityName(profit.getResponsibilityName());
            vo.setResponsibilityType(profit.getResponsibilityType());
            Responsibility responsibility = responsibilityCodeMap.get(profit.getResponsibilityCode());
            if(Objects.nonNull(responsibility)){
                vo.setResponsibilityId(responsibility.getId());
                vo.setParentId(responsibility.getParentId());
                vo.setSort(responsibility.getSort());
            }
            computeResponsibityList.add(vo);
        }
        //通过查询的最下层责任中心 找到其所有父类责任中心
        List<MedicalResponsibilityVo> parentResponseList = new ArrayList<>();
        for (MedicalResponsibilityVo responsibility : computeResponsibityList) {
            log.info(String.format("查询责任中心{%s}的父级ID{%s}", responsibility.getResponsibilityCode(),responsibility.getParentId()));
            if (responsibility.getParentId().equals(NumberConstant.ZERO_L)) {
                parentResponseList.add(responsibility);
            } else {
                Responsibility responsibility1 = responsibilityIdMap.get(responsibility.getParentId());
                if (Objects.nonNull(responsibility1)) {
                    parentResponseList.add(converResponsibilityToMedicalResponsibilityVo(responsibility1));
                    if (!responsibility1.getParentId().equals(NumberConstant.ZERO_L)) {
                        getResponsibilityParent(responsibility1, responsibilityIdMap, parentResponseList);
                    }
                }
            }
        }
        //去除重复组装数据
        if (!CollectionUtils.isEmpty(parentResponseList)) {
            //去重
            List<MedicalResponsibilityVo> collect = parentResponseList.stream().distinct().collect(Collectors.toList());
            computeResponsibityList.addAll(collect);
        }
        List<MedicalResponsibilityVo> collectDistinct = computeResponsibityList.stream().distinct().collect(Collectors.toList());

        Map<Long, List<MedicalResponsibilityVo>> collect = collectDistinct.stream().collect(Collectors.groupingBy(MedicalResponsibilityVo::getParentId));
        List<MedicalResponsibilityVo> responsibilities = collect.get(NumberConstant.ZERO_L);
        collect.remove(NumberConstant.ZERO_L);
        for (MedicalResponsibilityVo responsibility : responsibilities) {
            List<MedicalResponsibilityVo> responsibilities1 = collect.get(responsibility.getResponsibilityId());
            if (!CollectionUtils.isEmpty(responsibilities1)) {
                responsibility.setChild(setResponsibilityChildren(responsibilities1, collect));
            }
        }

        responsibilitySort(responsibilities);
        return responsibilities;
    }
    private List<MedicalResponsibilityVo> setResponsibilityChildren(List<MedicalResponsibilityVo> child, Map<Long, List<MedicalResponsibilityVo>> collect) {
        for (MedicalResponsibilityVo responsibility : child) {
            if (!CollectionUtils.isEmpty(collect.get(responsibility.getResponsibilityId()))) {
                responsibility.setChild(setResponsibilityChildren(collect.get(responsibility.getResponsibilityId()), collect));
            }
        }
        return child;
    }
    private MedicalResponsibilityVo converResponsibilityToMedicalResponsibilityVo(Responsibility responsibility){
        MedicalResponsibilityVo vo = new MedicalResponsibilityVo();
        vo.setResponsibilityCode(responsibility.getResponsibilityCode());
        vo.setResponsibilityName(responsibility.getResponsibilityName());
        vo.setResponsibilityId(responsibility.getId());
        vo.setParentId(responsibility.getParentId());
        vo.setSort(responsibility.getSort());
        return vo;
    }
    private void getResponsibilityParent(Responsibility responsibility, Map<Long, Responsibility> collect, List<MedicalResponsibilityVo> addList) {
        Responsibility responsibility1 = collect.get(responsibility.getParentId());
        if (Objects.nonNull(responsibility1)) {
            addList.add(converResponsibilityToMedicalResponsibilityVo(responsibility1));
            if (!responsibility1.getParentId().equals(NumberConstant.ZERO_L)) {
                getResponsibilityParent(responsibility1, collect, addList);
            }
        }
    }

    private void responsibilitySort(List<MedicalResponsibilityVo> responsibilityList) {
        for (MedicalResponsibilityVo responsibility : responsibilityList) {
            if (!CollectionUtils.isEmpty(responsibility.getChild())) {
                responsibilitySort(responsibility.getChild());
            }
        }
        responsibilityList.sort(Comparator.comparing(MedicalResponsibilityVo::getSort, Comparator.nullsLast(Integer::compareTo)));
    }

    private void reportFormProfitSort(List<ReportFormProfitVo> T) {
        for (ReportFormProfitVo reportFormProfitVo : T) {
            if (!CollectionUtils.isEmpty(reportFormProfitVo.getChildren())) {
                reportFormProfitSort(reportFormProfitVo.getChildren());
            }
        }
        T.sort(Comparator.comparing(ReportFormProfitVo::getSort, Comparator.nullsLast(Integer::compareTo)));
    }
}
