package com.kcim.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.kcim.common.constants.NumberConstant;
import com.kcim.common.constants.SQLParameter;
import com.kcim.common.enums.CustomSqlTypeEnum;
import com.kcim.common.enums.ItemTypeEnum;
import com.kcim.common.enums.ReportItemTypeEnum;
import com.kcim.common.exception.CostException;
import com.kcim.common.util.BeanUtil;
import com.kcim.common.util.PageUtils;
import com.kcim.dao.model.ComputeDrugCostDetail;
import com.kcim.dao.model.dto.ComputeDrugCostPageDto;
import com.kcim.dao.model.dto.PatientItemDepartmentGroupVo;
import com.kcim.dao.model.dto.SysDepartment;
import com.kcim.dao.repository.*;
import com.kcim.service.CenterService;
import com.kcim.service.DrugMaterialCalculateService;
import com.kcim.service.SqlService;
import com.kcim.vo.ResponsibilityDepartIdVO;
import com.kcim.vo.SessionUserVO;
import com.kcim.vo.VisitNoResponsibilityMap;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

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

/**
 * @program: CostAccount
 * @description:
 * @author: Wang.YS
 * @create: 2024-01-12 11:24
 **/
@Service("DrugMaterialCalculateService")
@Slf4j
@AllArgsConstructor
public class DrugMaterialCalculateServiceImpl implements DrugMaterialCalculateService {
    /**
     * 项目导入数据
     */
    ImportPatientItemRepository importPatientItemRepository;
    /**
     * 药材成本计算
     */
    ComputeDrugCostRepository repository;
    /**
     * 药材成本计算明细表
     */
    ComputeDrugCostDetailRepository detailRepository;
    /**
     * 药品成本
     */
    DrugRepository drugRepository;
    /**
     * 材料成本
     */
    MaterialRepository materialRepository;
    /**
     * 患者信息
     */
    ImportPatientInfoRepository importPatientInfoRepository;

    ResponsibilityDepartmentRepository responsibilityDepartmentRepository;

    CenterService centerService;

    SqlService sqlService;

    private static final Integer LIMIT = 5000;

    /**
     * 查询药材成本
     * @param current 当前页
     * @param pageSize 页容量
     * @param computeDate 核算年月
     * @param responsibilityCode 责任中心
     * @param filter 过滤条件
     * @return 药材成本列表
     */
    @Override
    public Object drugMaterialCostCalculateList(Integer current, Integer pageSize, String computeDate, String responsibilityCode, String filter) {
        Page<ComputeDrugCostPageDto> page = repository.getByPage(current, pageSize, computeDate, responsibilityCode, filter);
        List<ComputeDrugCostPageDto> records = page.getRecords();
        if (CollectionUtils.isEmpty(records)) {
            return new PageUtils(new ArrayList<>(), NumberConstant.ZERO, pageSize, current);
        }


        return new PageUtils(records, Math.toIntExact(page.getTotal()), pageSize, current);    }

    /**
     * 计算药材成本
     * @param computeDate 核算年月
     */
    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
    public void drugMaterialCostCalculate(String computeDate) {
//        SessionUserVO currentUser = UserContext.getCurrentUser();

        Map<String,String> sqlParameter = new HashMap<>();
        sqlParameter.put(SQLParameter.COMPUTE_DATE_CODE,computeDate);
        sqlService.autoExecuteSql(CustomSqlTypeEnum.DRG_COST_CALC.getCode(),sqlParameter);
/*  目前更换成自定义sql 下面代码保存留用


        long l = System.currentTimeMillis();


        //获取收费项目数据（药材）
        long itemSize = importPatientItemRepository.getDrugCount(computeDate, currentUser);
        if (Objects.equals(itemSize, NumberConstant.ZERO_L)) {
            log.error("当前核算年月【{}】未导入收费项目数据，计算中止", computeDate);
            throw new CostException("当前核算年月【" + computeDate + "】未导入收费项目数据，计算中止");
        }
        //把当月需要计算数据置成0
//        importPatientItemRepository.updateCalculateFlag(computeDate, currentUser);
        //作废上次计算记录
        repository.removeByComputeDate(computeDate, currentUser);
        detailRepository.removeByComputeDate(computeDate, currentUser);

        //获到科室与责任中心对照
        List<VisitNoResponsibilityMap> patientResponsibility = getPatientResponsibility( currentUser);
        Map<String,String> responsibilityCodeMap = new HashMap<>();
        Map<String,String> responsibilityNameMap = new HashMap<>();

        patientResponsibility.forEach(visitNoResponsibilityMap -> {
            responsibilityCodeMap.put(visitNoResponsibilityMap.getDepartmentCode(), visitNoResponsibilityMap.getResponsibilityCode());
            responsibilityNameMap.put(visitNoResponsibilityMap.getDepartmentCode(), visitNoResponsibilityMap.getResponsibilityName());
        });


        //药品成本
        Map<String, BigDecimal> drugCostMap = drugRepository.getCostMap(currentUser);
        //材料成本
        Map<String, BigDecimal> materialCostMap = materialRepository.getCostMap(currentUser);

        //主表公共对象
        ComputeDrugCost computeDrugCost = new ComputeDrugCost();
        computeDrugCost.setComputeDate(computeDate);
        computeDrugCost.setHospId(currentUser.getHospId());
        computeDrugCost.setCreateUser(String.valueOf(currentUser.getId()));
        computeDrugCost.setCreateTime(new Date());
        //明细表公共对象
        ComputeDrugCostDetail costDetail = new ComputeDrugCostDetail();
        costDetail.setHospId(currentUser.getHospId());
        costDetail.setComputeDate(computeDate);
        costDetail.setCreateUser(String.valueOf(currentUser.getId()));
        costDetail.setCreateTime(new Date());
        long count = (itemSize + LIMIT - 1) / LIMIT;
//        long l = 0;
        for (int j = 1; j <= count; j++) {
            List<PatientItemDepartmentGroupVo> items = importPatientItemRepository.getByNoCalculateComputeDateDrugMaterial(j, LIMIT, computeDate, currentUser);
            List<ComputeDrugCostDetail> saveDetails = new ArrayList<>();
            List<ComputeDrugCost> saveCost = new ArrayList<>();
            if (!CollectionUtils.isEmpty(items)) {
                for (int i = 0; i < items.size(); i++) {
                    PatientItemDepartmentGroupVo item = items.get(i);
                    ComputeDrugCost itemCost = BeanUtil.convertObj(computeDrugCost, ComputeDrugCost.class);
                    itemCost.setVisitNo(item.getVisitNo());
                    itemCost.setCode(item.getItemCode());
                    itemCost.setName(item.getItemName());
                    String typeCode = item.getItemTypeCode();
                    itemCost.setItemTypeCode(typeCode);
                    itemCost.setItemType(item.getItemType());
                    itemCost.setResponsibilityCode(responsibilityCodeMap.get(item.getExecuteDepartmentCode()));
                    itemCost.setResponsibilityName(responsibilityNameMap.get(item.getExecuteDepartmentCode()));
                    itemCost.setIndex(i);
                    saveCost.add(itemCost);
                    costDetail.setIndex(i);
                    List<ComputeDrugCostDetail> details = setDetails(drugCostMap, materialCostMap,  costDetail, item, typeCode);
                    if (!CollectionUtils.isEmpty(details)) {
                        saveDetails.addAll(details);
                    }
                }
                if (!CollectionUtils.isEmpty(saveCost)) {

                    repository.saveBatch(saveCost, 1000);
                    if (!CollectionUtils.isEmpty(saveDetails)) {
                        Map<Integer, Integer> indexId = saveCost.stream().collect(Collectors.toMap(ComputeDrugCost::getIndex, ComputeDrugCost::getId, (a, b) -> b));
                        Map<Integer, List<ComputeDrugCostDetail>> collect = saveDetails.stream().collect(Collectors.groupingBy(ComputeDrugCostDetail::getIndex));
                        List<ComputeDrugCostDetail> saveCostDetails = new ArrayList<>();
                        collect.keySet().forEach(integer -> {
                            Integer costId = indexId.get(integer);
                            List<ComputeDrugCostDetail> details = collect.get(integer);
                            details.forEach(detail -> detail.setDrugCostId(costId));
                            saveCostDetails.addAll(details);
                        });
                        detailRepository.saveBatch(saveCostDetails, 1000);
                    }
                }
                //保存完把已计算过的置1
//                importPatientItemRepository.updateCalculateFlag(items);

            }
        }
        long end = System.currentTimeMillis();
        log.info("{}ms", end - l);
*/
    }

    private List<VisitNoResponsibilityMap> getPatientResponsibility(SessionUserVO currentUser) {
        return getVisitNoResponsibilityMaps(currentUser, responsibilityDepartmentRepository, centerService, log);
    }

    static List<VisitNoResponsibilityMap> getVisitNoResponsibilityMaps(SessionUserVO currentUser, ResponsibilityDepartmentRepository responsibilityDepartmentRepository, CenterService centerService, Logger log) {
        List<ResponsibilityDepartIdVO> responsibility = responsibilityDepartmentRepository.getResponsibility(currentUser.getHospId());
        Map<Long,ResponsibilityDepartIdVO> responsibilityMap = responsibility.stream().collect(Collectors.toMap(ResponsibilityDepartIdVO::getDepartmentId, vo -> vo, (a, b) -> b));


        List<SysDepartment> departmentFilter = centerService.getDepartmentFilter(null);
        if(CollectionUtils.isEmpty(departmentFilter)){
            log.error("未获取到中台科室信息，计算中止");
            throw new CostException("未获取到中台科室信息，计算中止");
        }
        List<VisitNoResponsibilityMap> departmentList = new ArrayList<>();
        for (SysDepartment sysDepartment : departmentFilter) {
            VisitNoResponsibilityMap map = new VisitNoResponsibilityMap();
            map.setDepartmentCode(sysDepartment.getCode());
            map.setDepartmentName(sysDepartment.getName());
            map.setDepartmentId(Long.valueOf(sysDepartment.getId()));
            ResponsibilityDepartIdVO departIdVO = responsibilityMap.get(Long.valueOf(sysDepartment.getId()));
            if(Objects.nonNull(departIdVO)){
                map.setResponsibilityCode(departIdVO.getResponsibilityCode());
                map.setResponsibilityName(departIdVO.getResponsibilityName());
            }
            departmentList.add(map);
        }
        return departmentList;
    }

    private List<ComputeDrugCostDetail> setDetails(Map<String, BigDecimal> drugCostMap, Map<String, BigDecimal> materialCostMap,
                                                   ComputeDrugCostDetail costDetail,
                                                   PatientItemDepartmentGroupVo item, String typeCode ) {
        List<ComputeDrugCostDetail> details = new ArrayList<>();
        if (typeCode.equals(ItemTypeEnum.DRUG.getCode())) {
            // 药品  取药品收入 药品成本
            //取药品收入
            ComputeDrugCostDetail income = getIncome(costDetail, item);
            income.setType(ReportItemTypeEnum.DRUG_INCOME.getCode());
            details.add(income);
            //药品成本
            ComputeDrugCostDetail cost = getCost(costDetail, item, drugCostMap);
            cost.setType(ReportItemTypeEnum.DRUG_COST.getCode());
            details.add(cost);
        } else if (typeCode.equals(ItemTypeEnum.MATERIAL.getCode())) {
            //材料  取材料收入 材料成本
            //取材料收入
            ComputeDrugCostDetail income = getIncome(costDetail, item);
            income.setType(ReportItemTypeEnum.MATERIAL_INCOME.getCode());
            details.add(income);
            //材料成本
            ComputeDrugCostDetail cost = getCost(costDetail, item, materialCostMap);
            cost.setType(ReportItemTypeEnum.MATERIAL_COST.getCode());
            details.add(cost);
        }


        return details;
    }

    /**
     * 获取收入对象
     *
     * @param detail 公用明细
     * @param item   导入项目
     * @return 收入对象
     */
    private ComputeDrugCostDetail getIncome(ComputeDrugCostDetail detail, PatientItemDepartmentGroupVo item) {
        ComputeDrugCostDetail result = BeanUtil.convertObj(detail, ComputeDrugCostDetail.class);
        result.setComputeResult(item.getAmount());
        return result;
    }

    /**
     * 获取成本对象
     *
     * @param detail  公用明细
     * @param item    导入项目
     * @param costMap 成本字典
     * @return 成本对象
     */
    private ComputeDrugCostDetail getCost(ComputeDrugCostDetail detail, PatientItemDepartmentGroupVo item, Map<String, BigDecimal> costMap) {
        ComputeDrugCostDetail result = BeanUtil.convertObj(detail, ComputeDrugCostDetail.class);
        BigDecimal bigDecimal = costMap.get(item.getItemCode());
        BigDecimal num = item.getNum();
        if (bigDecimal != null && num != null) {
            result.setComputeResult(bigDecimal.multiply(num));
        } else {
            log.info("【{}】未找到成本信息，默认为 0", item.getItemName());
            result.setComputeResult(BigDecimal.ZERO);
        }
        return result;
    }
}
