package cn.iocoder.yudao.module.system.service.biz;

/**
 * @Author: jinyd
 * @CreateTime: 2024/8/19  11:42
 * @Description:
 * @Version: 4.0
 */


import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.BedChangeRecordOneReqVO;
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.BedChangeElderlyRespVO;
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.*;
import cn.iocoder.yudao.module.system.dal.dataobject.biz.*;
import cn.iocoder.yudao.module.system.dal.mysql.biz.*;
import cn.iocoder.yudao.module.system.enums.common.BooleanEnum;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exceptionCustomMsg;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.BED_CHANGE_RECORD_NOT_EXISTS;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.COMMON_ERROR;

/**
 * 床位变更记录 Service 实现类
 *
 * @author 系统管理员
 */
@Service
@Validated
public class NurseChangeRecordServiceImpl implements ElderlyChangeRecordService {

    @Resource
    private ElderlyChangeRecordMapper bedChangeRecordMapper;

    @Autowired
    private BuildService buildService;

    @Autowired
    private BuildBedMapper buildBedMapper;

    @Autowired
    private ElderlyInfoMapper elderlyInfoMapper;

    @Resource
    private ExpenseItemMapper expenseItemMapper;

    @Autowired
    private ExpenseMapper expenseMapper;

    @Resource
    private SysOverheadChargeMapper sysOverheadChargeMapper;

    @Override
    public Long createBedChangeRecord(BedChangeRecordSaveReqVO createReqVO) {
        // 插入
        ElderlyChangeRecordDO bedChangeRecord = BeanUtils.toBean(createReqVO, ElderlyChangeRecordDO.class);
        bedChangeRecordMapper.insert(bedChangeRecord);
        // 返回
        return bedChangeRecord.getId();
    }

    @Override
    public void updateBedChangeRecord(BedChangeRecordSaveReqVO updateReqVO) {
        // 校验存在
        validateBedChangeRecordExists(updateReqVO.getId());
        // 更新
        ElderlyChangeRecordDO updateObj = BeanUtils.toBean(updateReqVO, ElderlyChangeRecordDO.class);
        bedChangeRecordMapper.updateById(updateObj);
    }

    @Override
    public void deleteBedChangeRecord(Long id) {
        // 校验存在
        validateBedChangeRecordExists(id);
        // 删除
        bedChangeRecordMapper.deleteById(id);
    }

    private void validateBedChangeRecordExists(Long id) {
        if (bedChangeRecordMapper.selectById(id) == null) {
            throw exception(BED_CHANGE_RECORD_NOT_EXISTS);
        }
    }

    @Override
    public ElderlyChangeRecordDO getBedChangeRecord(Long id) {
        return bedChangeRecordMapper.selectById(id);
    }

    @Override
    public PageResult<ElderlyChangeRecordDO> getBedChangeRecordPage(BedChangeRecordPageReqVO pageReqVO) {
        return bedChangeRecordMapper.selectPage(pageReqVO);
    }

    @Override
    @TenantIgnore
    public BedChangeRecordRespVO detail(Long id, Integer status, Boolean isDetail) {
        Integer currentFlag = 0;
        if (null != status && status == 4) {
            Integer count = bedChangeRecordMapper.checkBpmBusinessFormById(id);
            if (null == count || count == 0) {
                currentFlag = 1;
            }
        }
        BedChangeRecordRespVO detail = isDetail ? bedChangeRecordMapper.getBedDetail(id, currentFlag) : bedChangeRecordMapper.createBedChangeGetDetail(id, currentFlag);
        if (null != detail) {
            detail.setBedName(buildService.getFullBedName(detail.getElderlyId()));
        }
        return detail;
    }


    @Override
    public PageResult<BedChangeElderlyRespVO> findElderlyPage(ElderlyInfoPageReqVO pageVO) {
        Page<ElderlyInfoDO> page = new Page<>(pageVO.getPageNo(), pageVO.getPageSize());
        List<BedChangeElderlyRespVO> list = null;
        if (null == pageVO.getIdentification() || 0 == pageVO.getIdentification()) {
            list = bedChangeRecordMapper.selectTransferElderlyPage(page, pageVO);
        } else {
            list = bedChangeRecordMapper.selectElderlyPage(page, pageVO);
        }
        if (null != list && list.size() > 0) {
            list.forEach(bedChangeElderlyRespVO -> {
                bedChangeElderlyRespVO.setBedName(buildService.getFullBedName(bedChangeElderlyRespVO.getId()));
                if (null != pageVO.getIdentification() && 1 == pageVO.getIdentification()) {
                    bedChangeElderlyRespVO.setOriginalId(String.valueOf(bedChangeElderlyRespVO.getBedId()));
                }
            });
        }

        return new PageResult<>(list, page.getTotal());
    }

    @Override
    @TenantIgnore
    public BedChangeElderlyRespVO getElderlyById(Long elderId) {
        if (elderId == null) {
            return null;
        }
        ElderlyInfoPageReqVO pageVO = new ElderlyInfoPageReqVO();
        pageVO.setIdentification(1);
        Page<ElderlyInfoDO> page = new Page<>(1, 1);
        List<BedChangeElderlyRespVO> list = bedChangeRecordMapper.selectElderlyById(page, pageVO, elderId);
        if (CollectionUtil.isEmpty(list)) {
            return null;
        }
        BedChangeElderlyRespVO respVO = list.get(0);
        respVO.setBedName(buildService.getFullBedName(respVO.getId()));
        respVO.setOriginalId(String.valueOf(respVO.getBedId()));
        return respVO;
    }

    @Override
    @TenantIgnore
    public Boolean checkPrivateRoom(Long bedId, Long elderId) {
        if (bedId == null || elderId == null) {
            return Boolean.FALSE;
        }
        BuildBedDO buildBedDO = buildBedMapper.selectById(bedId);
        if (null == buildBedDO) {
            return Boolean.FALSE;
        }
        List<BuildBedDO> buildBedDOList = buildBedMapper.selectList(new LambdaQueryWrapperX<BuildBedDO>()
                .eq(BuildBedDO::getRoomId, buildBedDO.getRoomId()));
        if (null != buildBedDOList && buildBedDOList.size() > 1) {
            List<BuildBedDO> usedBedList = buildBedDOList.stream()
                    .filter(item -> item.getStatus() != null && item.getStatus() == 1)
                    .collect(Collectors.toList());
            if (usedBedList.size() > 1) {
                return Boolean.FALSE;
            }
            if (usedBedList.size() == 1) {
                // 房间只有一个长者时，必须是申请长者本人在目标床位，才允许包房
                BuildBedDO usedBed = usedBedList.get(0);
                if (!bedId.equals(usedBed.getId())) {
                    return Boolean.FALSE;
                }
                ElderlyInfoDO elderlyInfoDO = elderlyInfoMapper.selectById(elderId);
                if (elderlyInfoDO == null || !bedId.equals(elderlyInfoDO.getBedId())) {
                    return Boolean.FALSE;
                }
            }
            List<Long> idList = buildBedDOList.stream().map(BuildBedDO::getId).collect(Collectors.toList());
            List<ElderlyChangeRecordDO> list = bedChangeRecordMapper.selectList(new LambdaQueryWrapperX<ElderlyChangeRecordDO>()
                    .in(ElderlyChangeRecordDO::getExpectId, idList)
                    .eq(ElderlyChangeRecordDO::getCurrentFlag, 0)
                    .eq(ElderlyChangeRecordDO::getChangeType, 0)
                    .orderByDesc(ElderlyChangeRecordDO::getCreateTime));
            if (null != list && !list.isEmpty()) {
                // 若存在当前申请床位之外的最新床位记录，说明房间关联到其他变更，不能包房
                List<ElderlyChangeRecordDO> otherBedRecordList = list.stream()
                        .filter(item -> !bedId.equals(item.getExpectId()))
                        .collect(Collectors.toList());
                if (!otherBedRecordList.isEmpty()) {
                    ElderlyChangeRecordDO elderlyChangeRecordDO = otherBedRecordList.get(0);
                    if (elderlyInfoMapper.selectById(elderlyChangeRecordDO.getElderlyId()).getInStatus() == 1) {
                        return Boolean.TRUE;
                    }
                    return Boolean.FALSE;
                }
            }
        }


        return Boolean.TRUE;
    }

    @Override
    @TenantIgnore
    public List<NurseChangeyEvaluationRespVO> getEvaluationByElderId(Long elderId) {
        return bedChangeRecordMapper.getEvaluationByElderId(elderId);
    }

    @Override
    @TenantIgnore
    public NurseChangeDetailRespVO getNurseDetail(Long id, Boolean isDetail) {
        NurseChangeDetailRespVO detail = isDetail ? bedChangeRecordMapper.getNurseDetail(id) : bedChangeRecordMapper.createNurseChangeGetDetail(id);
        detail.setBedName(buildService.getFullBedName(detail.getElderlyId()));
        return detail;
    }

    @Override
    @TenantIgnore
    public CateringChangeDetailRespVO getCateringDetail(Long id, Boolean isDetail) {
        CateringChangeDetailRespVO detail = isDetail ? bedChangeRecordMapper.getCateringDetail(id) : bedChangeRecordMapper.createCateringChangeGetDetail(id);
        detail.setBedName(buildService.getFullBedName(detail.getElderlyId()));
        return detail;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void swapBed(BedSwapReqVO reqVO) {
        if (reqVO.getElderIdA() == null || reqVO.getElderIdB() == null) {
            throw exceptionCustomMsg(COMMON_ERROR, "长者ID不能为空");
        }
        if (reqVO.getElderIdA().equals(reqVO.getElderIdB())) {
            throw exceptionCustomMsg(COMMON_ERROR, "两位长者不能相同");
        }
        LocalDate startDate = reqVO.getStartDate();
        if (startDate == null) {
            throw exceptionCustomMsg(COMMON_ERROR, "开始日期不能为空");
        }

        ElderlyInfoDO elderA = elderlyInfoMapper.selectById(reqVO.getElderIdA());
        ElderlyInfoDO elderB = elderlyInfoMapper.selectById(reqVO.getElderIdB());
        if (elderA == null || elderB == null) {
            throw exceptionCustomMsg(COMMON_ERROR, "长者信息不存在");
        }

        validateExpenseParams(reqVO);

        BuildBedDO bedA = buildBedMapper.selectById(elderA.getBedId());
        BuildBedDO bedB = buildBedMapper.selectById(elderB.getBedId());
        if (bedA == null || bedB == null) {
            throw exceptionCustomMsg(COMMON_ERROR, "床位信息不存在");
        }

        elderA.setBedId(bedB.getId());
        elderA.setBedName(bedB.getBedName());
        elderA.setRoomId(bedB.getRoomId());
        elderA.setFloorId(bedB.getFloorId());
        elderA.setBuildId(bedB.getBuildId());
        elderA.setIsPrivateRoom(reqVO.getIsPrivateA());

        elderB.setBedId(bedA.getId());
        elderB.setBedName(bedA.getBedName());
        elderB.setRoomId(bedA.getRoomId());
        elderB.setFloorId(bedA.getFloorId());
        elderB.setBuildId(bedA.getBuildId());
        elderB.setIsPrivateRoom(reqVO.getIsPrivateB());

        elderlyInfoMapper.updateById(elderA);
        elderlyInfoMapper.updateById(elderB);

        if (reqVO.getNewItemIdA() != null) {
            handleExpenseItemSwap(reqVO, true);
        }
        if (reqVO.getNewItemIdB() != null) {
            handleExpenseItemSwap(reqVO, false);
        }
    }

    private void validateExpenseParams(BedSwapReqVO reqVO) {
        boolean changeA = reqVO.getNewItemIdA() != null;
        boolean changeB = reqVO.getNewItemIdB() != null;
        if (changeA != changeB) {
            throw exceptionCustomMsg(COMMON_ERROR, "费用变更需同时提交A/B两位长者的信息");
        }
        if (!changeA) {
            return;
        }
        validateExpenseParamsForElder(reqVO, true);
        validateExpenseParamsForElder(reqVO, false);
    }

    private void validateExpenseParamsForElder(BedSwapReqVO reqVO, boolean isElderA) {
        String label = isElderA ? "长者A" : "长者B";
        Long originalExpenseItemId = isElderA ? reqVO.getOriginalExpenseItemIdA() : reqVO.getOriginalExpenseItemIdB();
        if (originalExpenseItemId == null) {
            throw exceptionCustomMsg(COMMON_ERROR, label + "原费用项目ID不能为空");
        }
        Integer isDiscount = isElderA ? reqVO.getIsDiscountA() : reqVO.getIsDiscountB();
        BigDecimal discountAmount = isElderA ? reqVO.getDiscountAmountA() : reqVO.getDiscountAmountB();
        BigDecimal actualAmount = isElderA ? reqVO.getActualAmountA() : reqVO.getActualAmountB();
        if (actualAmount != null) {
            return;
        }
        if (isDiscount != null && isDiscount == 1 && discountAmount == null) {
            throw exceptionCustomMsg(COMMON_ERROR, label + "打折时折扣金额不能为空");
        }
    }

    private void handleExpenseItemSwap(BedSwapReqVO reqVO, boolean isElderA) {
        Long originalExpenseItemId = isElderA ? reqVO.getOriginalExpenseItemIdA() : reqVO.getOriginalExpenseItemIdB();
        Long elderId = isElderA ? reqVO.getElderIdA() : reqVO.getElderIdB();
        if (originalExpenseItemId == null) {
            throw exceptionCustomMsg(COMMON_ERROR, (isElderA ? "长者A" : "长者B") + "原费用项目ID不能为空");
        }
        ExpenseItemDO originalItem = expenseItemMapper.selectById(originalExpenseItemId);
        if (originalItem == null) {
            throw exceptionCustomMsg(COMMON_ERROR, (isElderA ? "长者A" : "长者B") + "原费用项目不存在");
        }
        List<ExpenseItemDO> expenseItemDOS = expenseItemMapper.selectList(new LambdaQueryWrapperX<ExpenseItemDO>()
                .eq(ExpenseItemDO::getExpenseId, originalItem.getExpenseId()));
        List<ExpenseItemDO> newExpenseItemList = new ArrayList<>();
        for (ExpenseItemDO expenseItemDO : expenseItemDOS) {
            expenseItemDO.setChangeEndDate(reqVO.getStartDate().plusDays(-1));
            if(!Objects.equals(expenseItemDO.getId(), originalItem.getId())){
                newExpenseItemList.add(expenseItemDO);
            }
        }
        expenseItemMapper.updateBatch(expenseItemDOS);

        Long newItemId = isElderA ? reqVO.getNewItemIdA() : reqVO.getNewItemIdB();
        OverheadChargeDO newCharge = sysOverheadChargeMapper.selectById(newItemId);
        if (newCharge == null) {
            throw exceptionCustomMsg(COMMON_ERROR, (isElderA ? "长者A" : "长者B") + "新费用项目不存在");
        }

        BigDecimal amount = BigDecimal.valueOf(newCharge.getPrice());
        Integer isDiscount = isElderA ? reqVO.getIsDiscountA() : reqVO.getIsDiscountB();
        BigDecimal discountAmount = isElderA ? reqVO.getDiscountAmountA() : reqVO.getDiscountAmountB();
        BigDecimal actualAmount = isElderA ? reqVO.getActualAmountA() : reqVO.getActualAmountB();
        int count = originalItem.getCount() == null ? 1 : originalItem.getCount();
        BigDecimal totalAmount = actualAmount.multiply(BigDecimal.valueOf(count));

        ExpenseItemDO newItem = new ExpenseItemDO();
        BeanUtils.copyProperties(originalItem, newItem);
        newItem.setId(null);
        newItem.setItemId(newCharge.getId());
        newItem.setItemName(newCharge.getChargeName());
        newItem.setAmount(amount);
        newItem.setActualAmount(actualAmount);
        newItem.setTotalAmount(totalAmount);
        newItem.setIsDiscount(isDiscount);
        newItem.setDiscountAmount(discountAmount);
        newItem.setChangeStartDate(reqVO.getStartDate());
        newItem.setChangeEndDate(null);
        newItem.setTenantId(originalItem.getTenantId() == null ? TenantContextHolder.getTenantId() : originalItem.getTenantId());
        newExpenseItemList.add(newItem);
        BigDecimal total = newExpenseItemList.stream().map(ExpenseItemDO::getActualAmount).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
        ExpenseDO expenseDO = new ExpenseDO();
        expenseDO.setElderId(elderId);
        expenseDO.setTotalAmount(total);
        expenseMapper.insert(expenseDO);
        newExpenseItemList = newExpenseItemList.stream().map(e->{
            e.setId(null);
            e.setExpenseId(expenseDO.getId());
            return e;
        }).collect(Collectors.toList());
        expenseItemMapper.insertBatch(newExpenseItemList);
    }
}
