package com.yuchen.saas.park.service.service.property;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yuchen.saas.park.api.dto.property.*;
import com.yuchen.saas.park.api.entity.property.*;
import com.yuchen.saas.park.api.enums.property.*;
import com.yuchen.saas.park.api.vo.property.*;
import com.yuchen.saas.park.service.event.property.NotifyOfflinePaymentEvent;
import com.yuchen.saas.park.service.service.property.bill.PropertyBillFactoryService;
import com.yuchen.saas.park.service.service.property.bill.PropertyBillSearchService;
import com.yuchen.saas.park.service.service.property.bill.PropertyBillService;
import com.yuchen.saas.park.service.util.ManualValidUtil;
import lombok.extern.slf4j.Slf4j;
import org.nest.springwrap.core.log.exception.ServiceException;
import org.nest.springwrap.core.secure.utils.AuthUtil;
import org.nest.springwrap.core.tool.utils.Func;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.groups.Default;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
public class PropertyCashDeskService {
    @Resource
    private PropertyBillSearchService propertyBillSearchService;
    @Resource
    private ManualValidUtil manualValidUtil;
    @Resource
    private PropertyBillService propertyBillService;
    @Resource
    private PropertyBillSubService propertyBillSubService;
    @Resource
    private PropertyCashierOfflineService cashierOfflineService;
    @Resource
    private PropertyCashierOfflineDetailService cashierOfflineDetailService;
    @Resource
    private PropertyPaymentRefundAccountInfoService paymentRefundAccountInfoService;
    @Resource
    private ApplicationContext applicationContext;
    @Resource
    private PropertyHousesService propertyHousesService;
    @Resource
    private PropertyPrepaymentService propertyPrepaymentService;
    @Resource
    private PropertyDepositService propertyDepositService;
    @Resource
    private IPropertyHouseBindFeeService propertyHouseBindFeeService;
    @Resource
    private PropertyFeeStandardService propertyFeeStandardService;
    @Resource
    private PropertyBillFactoryService propertyBillFactoryService;
    @Resource
    private PropertyTemporaryChargesService propertyTemporaryChargesService;
    @Resource
    private PropertyTemporaryChargesDetailService propertyTemporaryChargesDetailService;
    @Resource
    private IPropertyHouseResidentService propertyHouseResidentService;


    /**
     * 收银台-查询收费账单
     *
     * @param qryDto
     * @return
     */
    public Page<PropertyBillListVo> selectChargeBillPage(PropertyBillPageQryDto qryDto) {
        if (Func.isEmpty(qryDto.getBillIdList()) && Func.isEmpty(qryDto.getHouseId())) {
           throw new ServiceException("请选择房源");
        }
        this.setPropertyBillPageQryDto(qryDto);
        return propertyBillSearchService.selectBillFullInfoPage(qryDto);
    }

    /**
     * 收银台-后台默认设置参数
     * @param qryDto
     */
    private void setPropertyBillPageQryDto(PropertyBillPageQryDto qryDto) {
        qryDto.setBillStatus(BillStatusEnum.YES_OUT.getValue());
        qryDto.setPayStatusList(PayStatusEnum.getNeedPayStatus());
        qryDto.setIsDirectReduction(YesOrNoEnum.YES.getValue());

        qryDto.setCreateBillTypeList(List.of(CreateBillTypeEnum.CYCLE.getValue()
                , CreateBillTypeEnum.METER.getValue()
                , CreateBillTypeEnum.BEFOREHAND.getValue()
                , CreateBillTypeEnum.TEMPORARY.getValue()
                , CreateBillTypeEnum.SHARE_FEE.getValue()
                , CreateBillTypeEnum.BUSINESS.getValue())); // 过虑掉押金账单
    }

    private PropertyBillPageQryDto getQueryDeskBillDto(Long houseId, List<Long> billIdList) {
        PropertyBillPageQryDto qryDto = new PropertyBillPageQryDto();
        if (Func.isNotEmpty(houseId)) {
            qryDto.setHouseId(houseId);
        } else {
            qryDto.setBillIdList(billIdList);
        }
        qryDto.setCurrent(-1);
        qryDto.setSize(-1);
        this.setPropertyBillPageQryDto(qryDto);
        return qryDto;
    }

    /**
     * 收费详情信息
     *
     * @param dto
     */
    public PropertyChargeFeesViewVo chargeFeesView(PropertyChargeFeesViewDto dto) {
        if (Func.isEmpty(dto.getHouseId()) && Func.isEmpty(dto.getBillIdList())) {
            throw new ServiceException("房源id和账单id参数不能同时为空");
        }
        PropertyBillPageQryDto qryDto = this.getQueryDeskBillDto(dto.getHouseId(),dto.getBillIdList());
        Page<PropertyBillListVo> page = this.selectChargeBillPage(qryDto);
        List<PropertyBillListVo> billListVoList = page.getRecords();

        PropertyChargeFeesViewVo viewVo = new PropertyChargeFeesViewVo();
        viewVo.setHouseId(dto.getHouseId());
        viewVo.setBillIdList(dto.getBillIdList());
        viewVo.setBillListVoList(billListVoList);

        log.info("-----------------------chargeFeesView--------------------------");
        long noPayAmtFen = 0;
        for (PropertyBillListVo vo : billListVoList) {
            log.info(vo.getId() +" = " + vo.getNoPayAmtFen());
            noPayAmtFen += vo.getNoPayAmtFen();
        }
        log.info(" total = " + noPayAmtFen);
        log.info("-----------------------chargeFeesView--------------------------");
        viewVo.setNoPayAmtFen(noPayAmtFen);

        return viewVo;
    }


    /**
     * 收银台-线下收费提交
     *
     * @param dto
     */
    @Transactional(rollbackFor = Exception.class)
    public void submitChargeFeesOffline(PropertyChargeFeesOfflineDto dto) {
        this.validSubmitChargeFeesOffline(dto);
        List<PropertyBill> billList = this.getCashDeskBillList(dto.getHouseId(), dto.getBillIdList());
        log.info("-----------------------submitChargeFeesOffline--------------------------");
        long noPayAmtFen = 0;
        for (PropertyBill bill : billList) {
            log.info(bill.getId() + " = " + bill.getNoPayAmtFen());
            noPayAmtFen += bill.getNoPayAmtFen();
        }
        log.info("total = " + noPayAmtFen);
        log.info("-----------------------submitChargeFeesOffline--------------------------");
        if (dto.getPayAmtFen() < noPayAmtFen) {
            throw new ServiceException("实收金额不能小于费用合计金额");
        }

        PropertyCashierOffline po = new PropertyCashierOffline();
        po.setParkId(billList.get(0).getParkId());
        po.setHouseId(billList.get(0).getHouseId());
        po.setPayAmtFen(dto.getPayAmtFen());
        po.setRecordTime(dto.getRecordTime());
        po.setCreateUser(AuthUtil.getUserId());
        po.setCreateTime(new Date());
        cashierOfflineService.save(po);

        List<PropertyCashierOfflineDetail> detailList = new ArrayList<>();
        List<Long> billIdList = new ArrayList<>();
        billList.forEach(bill -> {
            PropertyCashierOfflineDetail detail = new PropertyCashierOfflineDetail();
            detail.setCashierOfflineId(po.getId());
            detail.setBillId(bill.getId());
            detail.setNoPayAmtFen(bill.getNoPayAmtFen());
            detail.setCreateTime(new Date());
            detailList.add(detail);
            billIdList.add(bill.getId());
        });

        cashierOfflineDetailService.saveBatch(detailList);

        // 收款账户信息
        PropertyPaymentRefundAccountInfo accountInfo = BeanUtil.copyProperties(dto, PropertyPaymentRefundAccountInfo.class);
        accountInfo.setRetraceId(po.getId());
        accountInfo.setAccountBizType(AccountBizTypeEnum.CASHIER_OFFLINE.getValue());
        accountInfo.setRecordType(RecordTypeEnum.INCOME.getValue());
        accountInfo.setCreateTime(new Date());
        accountInfo.setCreateUser(AuthUtil.getUserId());

        paymentRefundAccountInfoService.save(accountInfo);

        // 线下收费审核单
        PropertyOfflinePaymentRecordDto recordDto = BeanUtil.copyProperties(accountInfo, PropertyOfflinePaymentRecordDto.class);
        recordDto.setRetraceId(po.getId());
        recordDto.setLockProcessBillIdList(billIdList);;
        recordDto.setParkId(po.getParkId());
        recordDto.setHouseId(po.getHouseId());
        recordDto.setCustomerId(billList.get(0).getCustomerId());
        recordDto.setReceivableAmtFen(noPayAmtFen);
        recordDto.setPayAmtFen(dto.getPayAmtFen());
        applicationContext.publishEvent(new NotifyOfflinePaymentEvent(recordDto));

    }


    /**
     * 参数校验
     *
     * @param dto
     */
    private void validSubmitChargeFeesOffline(PropertyChargeFeesOfflineDto dto) {
        if (Func.isEmpty(dto.getHouseId()) && Func.isEmpty(dto.getBillIdList())) {
            throw new ServiceException("房源id和账单id参数不能同时为空");
        }
        List<Class> groupList = CollUtil.newArrayList(Default.class);
        switch (dto.getPayType()) {
            case 1:
                groupList.add(PropertyChargeFeesOfflineDto.Cash.class);
                break;
            case 2:
                groupList.add(PropertyChargeFeesOfflineDto.Third.class);
                break;
            case 3:
                groupList.add(PropertyChargeFeesOfflineDto.Bank.class);
                break;
        }
        manualValidUtil.valid(dto, groupList);
    }

    private void validCashDeskBaseDto(PropertyCashDeskBaseDto cashDeskBaseDto) {
        if (Func.isEmpty(cashDeskBaseDto.getHouseId()) && Func.isEmpty(cashDeskBaseDto.getBillIdList())) {
            throw new ServiceException("房源id和账单id参数不能同时为空");
        }
    }


    /**
     * 收银台-获取收费详情账单
     *
     * @param houseId
     * @param billIdList
     * @return
     */
    private List<PropertyBill> getCashDeskBillList(Long houseId, List<Long> billIdList) {
        return propertyBillSearchService.selectBillBaseInfoPage(this.getQueryDeskBillDto(houseId,billIdList)).getRecords();
    }

    private List<PropertyBillListVo> getCashDeskBillFullInfoList(Long houseId, List<Long> billIdList) {
        return propertyBillSearchService.selectBillFullInfoPage(this.getQueryDeskBillDto(houseId,billIdList)).getRecords();
    }


    /**
     * 根据房源统计需缴费的账单信息
     *
     * @param houseId
     * @return
     */
    public PropertyCashDeskHouseInfoVo cashDeskHouseInfo(Long houseId) {
        PropertyCashDeskHouseInfoVo vo = new PropertyCashDeskHouseInfoVo();
        PropertyHouses houses = propertyHousesService.getById(houseId);
        if (houses == null) {
            return vo;
        }
        vo.setHouseId(houseId);
        vo.setResourceCode(houses.getResourceCode());
        vo.setConstructionArea(houses.getConstructionArea());
        vo.setUseArea(houses.getUseArea());
        PropertyTenantOwnerVo toVo = propertyHouseResidentService.getTenantOrOwnerInfo(houseId);
        if (toVo != null) {
            vo.setCustomerId(toVo.getCustomerArchivesId());
            vo.setCustomerName(toVo.getCustomerName());
            vo.setPhone(toVo.getPhone());
            vo.setStartDate(toVo.getStartDate());
        }
        return vo;
    }


    /**
     * 根据房源统计需缴费的账单信息
     *
     * @param dto
     * @return
     */
    public PropertyCashDeskInfoVo cashDeskBillInfo(PropertyCashDeskBaseDto dto) {
        this.validCashDeskBaseDto(dto);
        PropertyCashDeskInfoVo vo = new PropertyCashDeskInfoVo();
        List<PropertyBill> billList = this.getCashDeskBillList(dto.getHouseId(), dto.getBillIdList());
        long prepaymentAmtFen = 0;
        long depositAmtFen = 0;
        long totalArrearsAmtFen = 0;
        long totalReceivableAmtFen = 0;
        long totalDirectReductionAmtFen = 0;
        long totalNoPayAmtFen = 0;
        long totalBreachAmtFen = 0;
        long totalReductionBreachAmtFen = 0;
        long totalReadjustAmtFen = 0;
        for (PropertyBill bill : billList) {
            totalReceivableAmtFen += bill.getReceivableAmtFen();
            totalDirectReductionAmtFen += bill.getDirectReductionAmtFen();
            totalNoPayAmtFen += bill.getNoPayAmtFen();
            totalBreachAmtFen += bill.getBreachAmtFen();
            totalReductionBreachAmtFen += bill.getSignedReductionBreachAmtFen();
            totalReadjustAmtFen += bill.getSignedReadjustAmtFen();
        }

        Long houseId = dto.getHouseId() != null ? dto.getHouseId() : billList.get(0).getHouseId();
        List<PropertyBill> billList2 = this.getCashDeskBillList(houseId, null);
        for (PropertyBill bill : billList2) {
            if (bill.getPayStatus() == PayStatusEnum.ARREARS.getValue()) {
                totalArrearsAmtFen += bill.getNoPayAmtFen();
            }
        }

        PropertyPrepaymentQryDto qryDto = new PropertyPrepaymentQryDto();
        qryDto.setCurrent(1);
        qryDto.setSize(Integer.MAX_VALUE);
        qryDto.setHouseId(houseId);
        List<PropertyPrepaymentBalanceVo> balanceVos = propertyPrepaymentService.selectPage(qryDto).getRecords();
        for (PropertyPrepaymentBalanceVo balance : balanceVos) {
            prepaymentAmtFen += balance.getBalanceAmtFen();
        }

        depositAmtFen = propertyDepositService.getDepositBalanceFen(houseId);

        vo.setPrepaymentAmtFen(prepaymentAmtFen);
        vo.setDepositAmtFen(depositAmtFen);
        vo.setTotalArrearsAmtFen(totalArrearsAmtFen);
        vo.setTotalReceivableAmtFen(totalReceivableAmtFen);
        vo.setTotalDirectReductionAmtFen(totalDirectReductionAmtFen);
        vo.setTotalNoPayAmtFen(totalNoPayAmtFen);
        vo.setTotalBreachAmtFen(totalBreachAmtFen);
        vo.setTotalReductionBreachAmtFen(totalReductionBreachAmtFen);
        vo.setTotalReadjustAmtFen(totalReadjustAmtFen);
        return vo;

    }


    /**
     * 费用调整详情
     *
     * @param cashDeskBaseDto
     * @return
     */
    public List<PropertyBillListVo> billReadjustDetail(PropertyCashDeskBaseDto cashDeskBaseDto) {
        this.validCashDeskBaseDto(cashDeskBaseDto);
        return this.getCashDeskBillFullInfoList(cashDeskBaseDto.getHouseId(), cashDeskBaseDto.getBillIdList());
    }


    /**
     * 费用调整
     *
     * @param dto
     * @return
     */
    public void billReadjust(PropertyBillReadjustDto dto) {
        this.validCashDeskBaseDto(dto);
        List<PropertyBill> billList = this.getCashDeskBillList(dto.getHouseId(), dto.getBillIdList());
        long totalAmtFen = dto.getReadjustAmtFen();
        List<Long> billIdList = new ArrayList<>();
        for (PropertyBill bill : billList) {
            if (bill.getIsProcess() == YesOrNoEnum.YES.getValue()) {
                throw new ServiceException("账单存在线下收款待审核状态，不允许费用调整");
            }
            bill.setUpdateUser(AuthUtil.getUserId());
            billIdList.add(bill.getId());

            // TODO tucq 以下代码阅读性差，待优化
            if (dto.getIsTotal() == YesOrNoEnum.NO.getValue()) { // 每一笔调整
                if (dto.getIsReduce() == YesOrNoEnum.YES.getValue()) {
                    if (dto.getReadJustType() == ReadJustTypeEnum.ARREARS.getValue()) {
                        bill.setIsReduceReadjust(YesOrNoEnum.YES.getValue());
                        bill.setReadjustAmtFen(Math.min(bill.getNoPayAmtFen(), dto.getReadjustAmtFen()));
                    } else {
                        bill.setIsReduceBreach(YesOrNoEnum.YES.getValue());
                        bill.setReductionBreachAmtFen(Math.min(bill.getBreachAmtFen(), dto.getReadjustAmtFen()));
                    }
                } else {
                    if (dto.getReadJustType() == ReadJustTypeEnum.ARREARS.getValue()) {
                        bill.setIsReduceReadjust(YesOrNoEnum.NO.getValue());
                        bill.setReadjustAmtFen(dto.getReadjustAmtFen());
                    } else {
                        bill.setIsReduceBreach(YesOrNoEnum.NO.getValue());
                        bill.setReductionBreachAmtFen(dto.getReadjustAmtFen());
                    }
                }
            } else { // 总额调整
                if (dto.getIsReduce() == YesOrNoEnum.YES.getValue()) {
                    if (dto.getReadJustType() == ReadJustTypeEnum.ARREARS.getValue()) {
                        bill.setIsReduceReadjust(YesOrNoEnum.YES.getValue());
                        if (bill.getNoPayAmtFen() >= totalAmtFen) {
                            bill.setReadjustAmtFen(totalAmtFen);
                            break;
                        } else {
                            bill.setReadjustAmtFen(bill.getNoPayAmtFen());
                            totalAmtFen = totalAmtFen - bill.getNoPayAmtFen();
                        }
                    } else {
                        bill.setIsReduceBreach(YesOrNoEnum.YES.getValue());
                        if (bill.getBreachAmtFen() >= totalAmtFen) {
                            bill.setReductionBreachAmtFen(totalAmtFen);
                            break;
                        } else {
                            bill.setReductionBreachAmtFen(bill.getBreachAmtFen());
                            totalAmtFen = totalAmtFen - bill.getBreachAmtFen();
                        }
                    }
                } else {
                    // 只增加在第一笔
                    if (dto.getReadJustType() == ReadJustTypeEnum.ARREARS.getValue()) {
                        bill.setIsReduceReadjust(YesOrNoEnum.NO.getValue());
                        bill.setReadjustAmtFen(dto.getReadjustAmtFen());
                    } else {
                        bill.setIsReduceBreach(YesOrNoEnum.NO.getValue());
                        bill.setReductionBreachAmtFen(dto.getReadjustAmtFen());
                    }

                    break;

                }
            }

        }

        propertyBillService.updateBatchById(billList);

        LambdaUpdateWrapper<PropertyBillSub> uw = new LambdaUpdateWrapper<>();
        uw.in(PropertyBillSub::getBillId, billIdList);
        uw.set(PropertyBillSub::getReadjustReason, dto.getReadjustReason());
        propertyBillSubService.update(uw);

    }


    /**
     * 收银台，临时收费预览
     *
     * @param houseId
     * @return
     */
    public List<PropertyBillTempBillViewVo> tempBillView(Long houseId) {
        List<PropertyHouseBindFee> bindFeeList = propertyHouseBindFeeService.lambdaQuery().eq(PropertyHouseBindFee::getHouseId, houseId).list();
        if (Func.isEmpty(bindFeeList)) {
            return new ArrayList<>();
        }
        List<PropertyFeeStandard> feeStandardList = propertyFeeStandardService
                .listByIds(bindFeeList.stream().map(PropertyHouseBindFee::getFeeStandardId).collect(Collectors.toList()));

        List<PropertyBillTempBillViewVo> viewVoList = new ArrayList<>();
        PropertyHouses houses = propertyHousesService.getById(houseId);
        bindFeeList.forEach(bind -> {
            PropertyBillTempBillViewVo vo = new PropertyBillTempBillViewVo();
            feeStandardList.forEach(fee -> {
                if (bind.getFeeStandardId().equals(fee.getId())) {
                    BeanUtil.copyProperties(fee, vo);
                    vo.setFeeId(fee.getId());
                    vo.setBuildingId(houses.getBuildingId());
                    vo.setHouseId(houseId);
                }
            });
            vo.setResourceCode(houses.getResourceCode());
            viewVoList.add(vo);
        });
        return viewVoList;
    }


    /**
     * 收银台临时收费
     *
     * @param dto
     */
    @Transactional(rollbackFor = Exception.class)
    public void tempBillSave(PropertyCashDeskTempBillDto dto) {
        this.validTempParam(dto);
        if (dto.getIsPay() == YesOrNoEnum.YES.getValue()) {
            // 生成线下收费审核单
            PropertyTemporaryCharges po = BeanUtil.copyProperties(dto, PropertyTemporaryCharges.class);
            po.setCreateUser(AuthUtil.getUserId());
            po.setCreateTime(new Date());
            propertyTemporaryChargesService.save(po);

            List<PropertyTemporaryChargesDetail> detailList = BeanUtil.copyToList(dto.getTempBillList(), PropertyTemporaryChargesDetail.class);
            detailList.forEach(detail -> {
                detail.setTemporaryChargesId(po.getId());
                detail.setCreateTime(new Date());
            });
            propertyTemporaryChargesDetailService.saveBatch(detailList);

            PropertyPaymentRefundAccountInfo accountInfo = BeanUtil.copyProperties(dto, PropertyPaymentRefundAccountInfo.class);
            accountInfo.setAccountBizType(AccountBizTypeEnum.TEMPORARY.getValue());
            accountInfo.setRecordType(RecordTypeEnum.INCOME.getValue());
            accountInfo.setRetraceId(po.getId());
            accountInfo.setCreateUser(AuthUtil.getUserId());
            accountInfo.setCreateTime(new Date());
            paymentRefundAccountInfoService.save(accountInfo);

            // 线下收费审核单
            PropertyOfflinePaymentRecordDto recordDto = BeanUtil.copyProperties(accountInfo, PropertyOfflinePaymentRecordDto.class);
            recordDto.setRetraceId(po.getId());
            recordDto.setParkId(po.getParkId());
            recordDto.setHouseId(po.getHouseId());
            recordDto.setCustomerId(po.getCustomerId());
            recordDto.setReceivableAmtFen(po.getPayAmtFen());
            recordDto.setPayAmtFen(dto.getPayAmtFen());
            applicationContext.publishEvent(new NotifyOfflinePaymentEvent(recordDto));

        } else {
            CreateBillDto createBillDto = new CreateBillDto();
            createBillDto.setTempBillCreateDtoList(dto.getTempBillList());
            createBillDto.setCreateBillTypeEnum(CreateBillTypeEnum.TEMPORARY);
            propertyBillFactoryService.createBill(createBillDto);
        }

    }


    /**
     * 临时收费参数校验
     *
     * @param dto
     */
    private void validTempParam(PropertyCashDeskTempBillDto dto) {
        List<Class> groupList = CollUtil.newArrayList(Default.class);
        if (dto.getIsPay() == YesOrNoEnum.YES.getValue()) {
            if (Func.isEmpty(dto.getPayType())) {
                throw new ServiceException("付款方式不能为空");
            }
            groupList.add(PropertyCashDeskTempBillDto.Pay.class);
            groupList.add(PropertyPaymentRefundAccountDto.Default.class);
            switch (dto.getPayType()) {
                case 1:
                    groupList.add(PropertyPaymentRefundAccountDto.Cash.class);
                    break;
                case 2:
                    groupList.add(PropertyPaymentRefundAccountDto.Third.class);
                    break;
                case 3:
                    groupList.add(PropertyPaymentRefundAccountDto.Bank.class);
                    break;
            }
            manualValidUtil.valid(dto, groupList);
        }
        dto.getTempBillList().forEach(item -> {
            manualValidUtil.valid(item, groupList);
        });
    }


    /**
     * 账单拆分
     *
     * @param dto
     */
    @Transactional(rollbackFor = Exception.class)
    public void billSplit(PropertyBillSplitDto dto) {
        PropertyBill originalBill = propertyBillService.getById(dto.getBillId());
        List<PropertyBillSplitDto.SplitBill> splitBillList = dto.getSplitBillList();
        this.verifyBillSplit(originalBill, splitBillList);

        PropertyBillSub billSub = propertyBillSubService.lambdaQuery().eq(PropertyBillSub::getBillId,originalBill.getId()).one();
        List<PropertyBill> billList = new ArrayList<>();
        List<PropertyBillSub> subList = new ArrayList<>();
        splitBillList.forEach(splitBill -> {
            PropertyBill bill = BeanUtil.copyProperties(originalBill, PropertyBill.class);
            BeanUtil.copyProperties(splitBill,bill);
            bill.setId(null);

            if (bill.getReadjustAmtFen() < 0) {
                bill.setIsReduceReadjust(YesOrNoEnum.YES.getValue());
            } else {
                bill.setIsReduceReadjust(YesOrNoEnum.NO.getValue());
            }

            if (bill.getReductionBreachAmtFen() < 0) {
                bill.setIsReduceBreach(YesOrNoEnum.YES.getValue());
            } else {
                bill.setIsReduceBreach(YesOrNoEnum.NO.getValue());
            }

            bill.setReadjustAmtFen(Math.abs(bill.getReadjustAmtFen()));
            bill.setReductionBreachAmtFen(Math.abs(bill.getReductionBreachAmtFen()));
            bill.setDirectReductionAmtFen(Math.abs(bill.getDirectReductionAmtFen()));
            bill.setBreachAmtFen(Math.abs(bill.getBreachAmtFen()));
            bill.setReceivableAmtFen(Math.abs(bill.getReceivableAmtFen()));
            billList.add(bill);

            PropertyBillSub sub = BeanUtil.copyProperties(billSub, PropertyBillSub.class);
            sub.setId(null);
            sub.setSlipOriginalBillId(originalBill.getId());
            subList.add(sub);
        });

        CreateBillDto createBillDto = new CreateBillDto();
        createBillDto.setCreateBillTypeEnum(CreateBillTypeEnum.BILL_SPLIT);
        PropertySlipCreateBillDto slipCreateBillDto = new PropertySlipCreateBillDto();
        slipCreateBillDto.setBillList(billList);
        slipCreateBillDto.setBillSubList(subList);
        createBillDto.setSlipCreateBillDto(slipCreateBillDto);
        propertyBillFactoryService.createBill(createBillDto);

        propertyBillService.removeById(originalBill.getId());
    }


    private void verifyBillSplit(PropertyBill originalBill, List<PropertyBillSplitDto.SplitBill> splitBillList) {
        Date minStartDate = null;
        Date maxEndDate = null;
        long receivableAmtFen = 0, readjustAmtFen = 0, directReductionAmtFen = 0, breachAmtFen = 0, reductionBreachAmtFen = 0, noPayAmtFen = 0;
        for (PropertyBillSplitDto.SplitBill splitBill : splitBillList) {
            manualValidUtil.valid(splitBill, CollUtil.newArrayList(Default.class)); // 列表参数校验
            if (DateUtil.compare(splitBill.getEndDate(), splitBill.getStartDate()) < 0) {
                throw new ServiceException("拆分后的结束日期不能小于开始日期");
            }
            if (minStartDate == null) {
                minStartDate = splitBill.getStartDate();
            } else {
                if (DateUtil.compare(splitBill.getStartDate(), minStartDate) < 0) {
                    minStartDate = splitBill.getStartDate();
                }
            }
            if (maxEndDate == null) {
                maxEndDate = splitBill.getEndDate();
            } else {
                if (DateUtil.compare(splitBill.getEndDate(), maxEndDate) > 0) {
                    maxEndDate = splitBill.getEndDate();
                }
            }
            receivableAmtFen += splitBill.getReceivableAmtFen();
            readjustAmtFen += splitBill.getReadjustAmtFen();
            directReductionAmtFen += splitBill.getDirectReductionAmtFen();
            breachAmtFen += splitBill.getBreachAmtFen();
            reductionBreachAmtFen += splitBill.getReductionBreachAmtFen();
            noPayAmtFen += splitBill.getNoPayAmtFen();
        }
        if (receivableAmtFen != originalBill.getReceivableAmtFen()) {
            throw new ServiceException("拆分后应收金额之和与原账单不相等");
        }
        if (readjustAmtFen != originalBill.getReadjustAmtFen()) {
            throw new ServiceException("拆分后调整金额之和与原账单不相等");
        }
        if (directReductionAmtFen != originalBill.getDirectReductionAmtFen()) {
            throw new ServiceException("拆分后直接减免金额之和与原账单不相等");
        }
        if (breachAmtFen != originalBill.getBreachAmtFen()) {
            throw new ServiceException("拆分后滞纳金之和与原账单不相等");
        }
        if (reductionBreachAmtFen != originalBill.getReductionBreachAmtFen()) {
            throw new ServiceException("拆分后调整滞纳金之和与原账单不相等");
        }
        if (noPayAmtFen != originalBill.getNoPayAmtFen()) {
            throw new ServiceException("拆分后实际应收金额之和与原账单不相等");
        }
        if (DateUtil.compare(minStartDate, originalBill.getStartDate()) != 0) {
            throw new ServiceException("拆分后账单最小开始时间与原账单开始时间不同");
        }
        if (DateUtil.compare(maxEndDate, originalBill.getEndDate()) != 0) {
            throw new ServiceException("拆分后账单最大结束时间与原账单结束时间不同");
        }
    }

    public List<PropertyBeforeFeeListVo> getBeforeFeeList(Long parkId, Long houseId,Long feeItemId, Integer num) {
        PropertyFeeStandardQryDto qryDto = new PropertyFeeStandardQryDto();
        qryDto.setParkId(parkId);
        qryDto.setFeeItemId(feeItemId);
        qryDto.setFeeMarkList(List.of(FeeMarkEnum.CYCLE.getValue()));
        List<PropertyFeeStandardListVo> feeList = propertyFeeStandardService.listPageVo(qryDto);
        // TODO tucq
        return null;
    }


    @Transactional(rollbackFor = Exception.class)
    public void billBeforehand(PropertyBillBeforehandDto dto) {
        // TODO tucq
    }

}
