package org.dromara.web.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.enums.biz.ActionStatus;
import org.dromara.common.core.enums.biz.FeeType;
import org.dromara.common.core.enums.biz.PaymentStatus;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.domain.SysDept;
import org.dromara.system.domain.SysUser;
import org.dromara.system.mapper.SysDeptMapper;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.web.domain.*;
import org.dromara.web.domain.bo.ChargeRecordBo;
import org.dromara.web.domain.vo.*;
import org.dromara.web.mapper.*;
import org.dromara.web.service.IChargeRecordService;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 收费记录Service业务层处理
 *
 * @author Lion Li
 * @date 2025-08-06
 */

@Slf4j
@RequiredArgsConstructor
@Service
public class ChargeRecordServiceImpl implements IChargeRecordService {

    private final ChargeRecordMapper baseMapper;

    private final SysDeptMapper deptMapper;

    private final SysUserMapper userMapper;

    private final SettlementMapper settlementMapper;

    private final ChargeRecordLogMapper logMapper;

    private final EnteralNutritionMapper enteralNutritionMapper;

    private final NutritionEvaluationMapper evaluationMapper;

    private final NutritionScreeningMapper screeningMapper;

    private final ProductNutritionMapper productNutritionMapper;

    private final HospitalMealPlanMapper mealPlanMapper;

    private final HospitalMealRecipeMapper mealRecipeMapper;

    /**
     * 查询收费记录
     *
     * @param id 主键
     * @return 收费记录
     */
    @Override
    public ChargeRecordVo queryById(Long id) {
        ChargeRecordVo chargeRecordVo = baseMapper.selectVoById(id);
        List<SysUser> userList = userMapper.selectList(Wrappers.lambdaQuery(SysUser.class).select(SysUser::getUserId, SysUser::getUserName));
        Map<Long, String> userMap = userList.stream().collect(Collectors.toMap(k1 -> k1.getUserId(), k2 -> String.valueOf(k2.getUserName()), (k1, k2) -> k1));
        if (null != chargeRecordVo) {
            SettlementVo settlementVo = settlementMapper.selectVoById(chargeRecordVo.getSettlementId());
            if (null != settlementVo) {
                String chargeType = settlementVo.getChargeType();
                List<EnteralNutritionVo> enteralNutritionVoList = enteralNutritionMapper.selectVoList(new LambdaQueryWrapper<EnteralNutrition>().eq(EnteralNutrition::getSettlementId, settlementVo.getId()));

                if (CollUtil.isNotEmpty(enteralNutritionVoList)) {
                    // 提获取所有需要的产品营养信息
                    Set<Long> productIds = enteralNutritionVoList.stream().map(EnteralNutritionVo::getNutritionProductId).collect(Collectors.toSet());
                    Map<Long, ProductNutrition> productNutritionMap = productNutritionMapper.selectBatchIds(productIds).stream().collect(Collectors.toMap(ProductNutrition::getId, Function.identity()));

                    // 处理价格设置
                    for (EnteralNutritionVo nutritionVo : enteralNutritionVoList) {
                        nutritionVo.setCreateByName(userMap.get(nutritionVo.getCreateBy()));
                        ProductNutrition productNutrition = productNutritionMap.get(nutritionVo.getNutritionProductId());

                        if (productNutrition != null) {
                            BigDecimal price = FeeType.PRE_PACKAGED_PRESCRIPTION.getCode().equals(chargeType) ? productNutrition.getPackagePrice() : productNutrition.getConfigSalePrice();
                            nutritionVo.setSalePrice(price);
                        }
                    }

                    // 处理处方费用计算
                    if (FeeType.CONFIGURE_PRESCRIPTION.getCode().equals(chargeType) || FeeType.LONG_TERM_PRESCRIPTION.getCode().equals(chargeType)) {
                        int lastGroupNo = Integer.valueOf(enteralNutritionVoList.get(enteralNutritionVoList.size() - 1).getGroupNo());
                        chargeRecordVo.setPrescriptionFee(new BigDecimal(lastGroupNo + 1));
                    }
                }

                chargeRecordVo.setEnteralNutritionList(enteralNutritionVoList);
                List<NutritionScreeningVo> nutritionScreeningVos = screeningMapper.selectVoList(new LambdaQueryWrapper<NutritionScreening>().eq(NutritionScreening::getSettlementId, settlementVo.getId()));
                nutritionScreeningVos.forEach(v -> {
                    v.setCreateByName(userMap.get(v.getCreateBy()));
                });
                chargeRecordVo.setNutritionScreeningList(nutritionScreeningVos);
                List<NutritionEvaluationVo> nutritionEvaluationVos = evaluationMapper.selectVoList(new LambdaQueryWrapper<NutritionEvaluation>().eq(NutritionEvaluation::getSettlementId, settlementVo.getId()));
                nutritionEvaluationVos.forEach(v -> {
                    v.setCreateByName(userMap.get(v.getCreateBy()));
                });
                chargeRecordVo.setNutritionEvaluationList(nutritionEvaluationVos);

                List<HospitalMealPlanVo> hospitalMealPlanVos = mealPlanMapper.selectVoList(new LambdaQueryWrapper<HospitalMealPlan>().eq(HospitalMealPlan::getSettlementId, settlementVo.getId()));
                if (CollUtil.isNotEmpty(hospitalMealPlanVos)) {
                    List<HospitalMealRecipeVo> hospitalMealRecipeVos = mealRecipeMapper.selectVoList(new LambdaQueryWrapper<HospitalMealRecipe>().eq(HospitalMealRecipe::getPlanId, hospitalMealPlanVos.get(0).getId()));
                    hospitalMealRecipeVos.forEach(v -> {
                        v.setCreateByName(userMap.get(v.getCreateBy()));
                    });
                    chargeRecordVo.setHospitalMealRecipeList(hospitalMealRecipeVos);
                }
            }
            List<ChargeRecordLogVo> chargeRecordLogs = logMapper.selectVoList(new LambdaQueryWrapper<ChargeRecordLog>().eq(ChargeRecordLog::getChargeRecordId, chargeRecordVo.getId()));
            chargeRecordLogs.forEach(v -> {
                v.setCreateByName(userMap.get(v.getCreateBy()));
            });
            chargeRecordVo.setRecordLogList(chargeRecordLogs);
        }
        return chargeRecordVo;
    }

    /**
     * 分页查询收费记录列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 收费记录分页列表
     */
    @Override
    public TableDataInfo<ChargeRecordVo> queryPageList(ChargeRecordBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<ChargeRecord> lqw = buildQueryWrapper(bo);
        Page<ChargeRecordVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        List<ChargeRecordVo> records = result.getRecords();
        if (CollUtil.isNotEmpty(records)) {
            Set<Long> deptList = CollUtil.newHashSet();
            records.forEach(v -> {
                deptList.add(v.getDoorId());
            });
            Map<Long, String> deptMap = deptMapper.selectList(Wrappers.lambdaQuery(SysDept.class).select(SysDept::getDeptId, SysDept::getDeptName).in(SysDept::getDeptId, deptList)).stream().collect(Collectors.toMap(k1 -> k1.getDeptId(), k2 -> k2.getDeptName(), (k1, k2) -> k1));
            records.forEach(v -> {
                v.setPatientDepartment(deptMap.get(v.getDoorId()));
            });
        }
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的收费记录列表
     *
     * @param bo 查询条件
     * @return 收费记录列表
     */
    @Override
    public List<ChargeRecordVo> queryList(ChargeRecordBo bo) {
        LambdaQueryWrapper<ChargeRecord> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<ChargeRecord> buildQueryWrapper(ChargeRecordBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<ChargeRecord> lqw = Wrappers.lambdaQuery();
        lqw.orderByAsc(ChargeRecord::getId);
        lqw.eq(bo.getSettlementId() != null, ChargeRecord::getSettlementId, bo.getSettlementId());
        lqw.eq(bo.getOrderTime() != null, ChargeRecord::getOrderTime, bo.getOrderTime());
        lqw.eq(StringUtils.isNotBlank(bo.getVisitType()), ChargeRecord::getVisitType, bo.getVisitType());
        lqw.eq(StringUtils.isNotBlank(bo.getChargeType()), ChargeRecord::getChargeType, bo.getChargeType());
        lqw.eq(bo.getPatientId() != null, ChargeRecord::getPatientId, bo.getPatientId());
        lqw.like(StringUtils.isNotBlank(bo.getPatientName()), ChargeRecord::getPatientName, bo.getPatientName());
        lqw.eq(bo.getDoorId() != null, ChargeRecord::getDoorId, bo.getDoorId());
        lqw.eq(StringUtils.isNotBlank(bo.getPatientNo()), ChargeRecord::getPatientNo, bo.getPatientNo());
        lqw.eq(StringUtils.isNotBlank(bo.getPhone()), ChargeRecord::getPhone, bo.getPhone());
        lqw.eq(StringUtils.isNotBlank(bo.getIdCard()), ChargeRecord::getIdCard, bo.getIdCard());
        lqw.eq(bo.getReceivableAmount() != null, ChargeRecord::getReceivableAmount, bo.getReceivableAmount());
        lqw.eq(bo.getFundsReceived() != null, ChargeRecord::getFundsReceived, bo.getFundsReceived());
        lqw.eq(bo.getRefundAmount() != null, ChargeRecord::getRefundAmount, bo.getRefundAmount());
        lqw.eq(StringUtils.isNotBlank(bo.getPaymentStatus()), ChargeRecord::getPaymentStatus, bo.getPaymentStatus());
        lqw.eq(StringUtils.isNotBlank(bo.getPaymentMethod()), ChargeRecord::getPaymentMethod, bo.getPaymentMethod());
        lqw.eq(StringUtils.isNotBlank(bo.getBedNo()), ChargeRecord::getBedNo, bo.getBedNo());
        lqw.eq(bo.getRefundTime() != null, ChargeRecord::getRefundTime, bo.getRefundTime());
        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), ChargeRecord::getStatus, bo.getStatus());
        lqw.eq(bo.getWardId() != null, ChargeRecord::getWardId, bo.getWardId());
        lqw.like(StringUtils.isNotBlank(bo.getWardName()), ChargeRecord::getWardName, bo.getWardName());
        lqw.eq(StringUtils.isNotBlank(bo.getTreatNum()), ChargeRecord::getTreatNum, bo.getTreatNum());
        return lqw;
    }

    /**
     * 新增收费记录
     *
     * @param bo 收费记录
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(ChargeRecordBo bo) {
        bo.setId(null);
        bo.setPaymentStatus(PaymentStatus.PAID.getCode());
        bo.setCreateDept(LoginHelper.getDeptId());
        ChargeRecord add = MapstructUtils.convert(bo, ChargeRecord.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
            SettlementVo settlementVo = settlementMapper.selectVoById(bo.getSettlementId());
            settlementVo.setPaymentStatus(PaymentStatus.PAID.getCode());
            settlementMapper.updateById(MapstructUtils.convert(settlementVo, Settlement.class));
            String productStr = null;
            if (FeeType.PRE_PACKAGED_PRESCRIPTION.getCode().equals(settlementVo.getChargeType())
                || FeeType.CONFIGURE_PRESCRIPTION.getCode().equals(settlementVo.getChargeType())
                || FeeType.LONG_TERM_PRESCRIPTION.getCode().equals(settlementVo.getChargeType())) {

                // 查询肠内营养列表
                List<EnteralNutritionVo> enteralNutritionVoList = enteralNutritionMapper.selectVoList(
                    new LambdaQueryWrapper<EnteralNutrition>()
                        .eq(EnteralNutrition::getSettlementId, settlementVo.getId()));

                if (CollUtil.isNotEmpty(enteralNutritionVoList)) {
                    // 获取所有产品ID并拼接成字符串
                    productStr = enteralNutritionVoList.stream()
                        .map(vo -> String.valueOf(vo.getNutritionProduct()))
                        .collect(Collectors.joining(","));
                }
            } else if (FeeType.NUTRITION_SCREENING.getCode().equals(settlementVo.getChargeType())) {
                productStr = FeeType.NUTRITION_SCREENING.getRemark();
            } else if (FeeType.NUTRITION_EVALUATION.getCode().equals(settlementVo.getChargeType())) {
                productStr = FeeType.NUTRITION_EVALUATION.getRemark();
            }
            ChargeRecordLog recordLog = new ChargeRecordLog();
            recordLog.setChargeRecordId(add.getId());
            recordLog.setSettlementId(add.getSettlementId());
            recordLog.setReceivableAmount(add.getReceivableAmount());
            recordLog.setStatus(ActionStatus.SUCCESS.getCode());
            recordLog.setPaymentMethod(add.getPaymentMethod());
            recordLog.setProductName(productStr);
            logMapper.insert(recordLog);
        }
        return flag;
    }

    /**
     * 修改收费记录
     *
     * @param bo 收费记录
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(ChargeRecordBo bo) {
        ChargeRecord update = MapstructUtils.convert(bo, ChargeRecord.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(ChargeRecord entity) {
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 校验并批量删除收费记录信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteByIds(ids) > 0;
    }

    @Override
    public TableDataInfo<ChargeRecordVo> queryInvoicePageList(PageQuery pageQuery) {
        LambdaQueryWrapper<ChargeRecord> wrapper = buildInvoiceQueryWrapper();
        IPage<ChargeRecordVo> page = baseMapper.selectVoPage(pageQuery.build(), wrapper);
        return TableDataInfo.build(page);
    }

    @Override
    public R totalize() {
        return R.ok(baseMapper.totalize());
    }

    private LambdaQueryWrapper<ChargeRecord> buildInvoiceQueryWrapper() {
        LambdaQueryWrapper<ChargeRecord> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByAsc(ChargeRecord::getId)
            .eq(ChargeRecord::getWhetherInvoice, 0);
        return wrapper;
    }
}
