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

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yuchen.saas.park.api.dto.property.*;
import com.yuchen.saas.park.api.entity.park.PmgBatParkInfo;
import com.yuchen.saas.park.api.entity.property.*;
import com.yuchen.saas.park.api.enums.property.PropertyPayTypeEnum;
import com.yuchen.saas.park.api.enums.property.BillStatusEnum;
import com.yuchen.saas.park.api.enums.property.PayStatusEnum;
import com.yuchen.saas.park.api.enums.property.YesOrNoEnum;
import com.yuchen.saas.park.api.vo.property.*;
import com.yuchen.saas.park.service.mapper.property.PropertyBillMapper;
import com.yuchen.saas.park.service.service.PmgBatParkInfoService;
import com.yuchen.saas.park.service.service.property.*;
import lombok.extern.slf4j.Slf4j;
import org.nest.springwrap.core.excel.util.ExcelUtil;
import org.nest.springwrap.core.log.exception.ServiceException;
import org.nest.springwrap.core.mp.support.Condition;
import org.nest.springwrap.core.mp.support.NestPage;
import org.nest.springwrap.core.secure.ContextUser;
import org.nest.springwrap.core.secure.utils.AuthUtil;
import org.nest.springwrap.core.tool.utils.BeanUtil;
import org.nest.springwrap.core.tool.utils.Func;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static cn.hutool.core.date.DatePattern.NORM_DATE_PATTERN;

@Slf4j
@Service
public class PropertyBillService extends ServiceImpl<PropertyBillMapper, PropertyBill> {
    @Resource
    private PropertyBillSubService billSubService;
    @Resource
    private IPropertyCustomerArchivesService customerArchivesService;
    @Resource
    private PropertyHousesService housesService;
    @Resource
    private PmgBatParkInfoService pmgBatParkInfoService;
    @Resource
    private IPropertyFeeItemService feeItemService;
    @Resource
    private PropertyBuildingsService buildingsService;
    @Resource
    private PropertyFeeStandardService propertyFeeStandardService;
    @Resource
    private PropertyBillingSettingsService propertyBillingSettingsService;

    @Resource
    private ApplicationEventPublisher publisher;

    /**
     * 未出账单出账
     * @param billIdList
     */
    public void outBill(List<Long> billIdList) {
        if (billIdList == null || billIdList.size() == 0) {
            return ;
        }
        LambdaUpdateWrapper<PropertyBill> uw = new LambdaUpdateWrapper<>();
        uw.in(PropertyBill::getId,billIdList);
        uw.set(PropertyBill::getBillStatus, BillStatusEnum.YES_OUT.getValue());
        uw.set(PropertyBill::getUpdateTime, new Date());
        uw.set(PropertyBill::getOutBillTime, new Date());
        uw.set(PropertyBill::getUpdateUser, AuthUtil.getUserId());
        this.update(uw);
    }


    /**
     * 设置欠费原因
     * @param arrearsReasonDto
     */
    public void setArrearsReason(PropertyArrearsReasonDto arrearsReasonDto) {
        PropertyBill bill = this.getById(arrearsReasonDto.getId());
        if (Func.isEmpty(bill.getReceivableDate())
                || DateUtil.compare(new Date(),bill.getReceivableDate(),"yyyy-MM-dd") <= 0
                || bill.getPayAmtFen() > 0) {
            throw new ServiceException("该笔账单不满足欠费条件");
        }
        LambdaUpdateWrapper<PropertyBillSub> uw = new LambdaUpdateWrapper<>();
        uw.eq(PropertyBillSub::getBillId,arrearsReasonDto.getId());
        uw.set(PropertyBillSub::getArrearsReason,arrearsReasonDto.getArrearsReason());
        billSubService.update(uw);
    }


    /**
     * 冻结账单
     * @param billId
     */
    public void freezeBill(long billId) {
        PropertyBill bill = this.getById(billId);
//        if (bill.getCreateBillType() == CreateBillTypeEnum.DEPOSIT.getValue()) {
//            // 押金账单
//            if (bill.getBillStatus() == BillStatusEnum.PAID.getValue()) {
//                throw new ServiceException("该笔账单状态已结，不支持冻结");
//            }
//        } else {
//            if (bill.getBillStatus() == BillStatusEnum.NO_OUT.getValue()) {
//                throw new ServiceException("该笔账单未出账，不能冻结账单");
//            }
//        }
        if (bill.getBillStatus() == BillStatusEnum.PAID.getValue()) {
            throw new ServiceException("账单状态已结，不支持冻结");
        }
        if (bill.getBillStatus() == BillStatusEnum.NO_OUT.getValue()) {
            throw new ServiceException("账单未出账，不能冻结账单");
        }
        if (bill.getPayAmtFen() > 0) {
            throw new ServiceException("账单有收费，不能冻结账单");
        }
        LambdaUpdateWrapper<PropertyBill> uw = new LambdaUpdateWrapper<>();
        uw.eq(PropertyBill::getId,billId);
        uw.set(PropertyBill::getUpdateTime,new Date());
        uw.set(PropertyBill::getUpdateUser,AuthUtil.getUserId());
        uw.set(PropertyBill::getBillStatus, BillStatusEnum.FREEZE.getValue());
        this.update(uw);
    }


    /**
     * 账单解冻
     * @param billId
     */
    public void unfreezeBill(long billId) {
        PropertyBill bill = this.getById(billId);
        if (bill.getBillStatus() != BillStatusEnum.FREEZE.getValue()) {
            throw new ServiceException("账单非冻结状态，不支持解冻");
        }
        LambdaUpdateWrapper<PropertyBill> uw = new LambdaUpdateWrapper<>();
//        if (bill.getCreateBillType() == CreateBillTypeEnum.DEPOSIT.getValue()
//            || bill.getCreateBillType() == CreateBillTypeEnum.BUSINESS.getValue()) {
//            // 线上收费才有解冻操作，解冻后账单状态为出账
//            uw.eq(PropertyBill::getId,billId);
//            uw.set(PropertyBill::getUpdateTime,new Date());
//            uw.set(PropertyBill::getUpdateUser,AuthUtil.getUserId());
//            uw.set(PropertyBill::getBillStatus, BillStatusEnum.YES_OUT.getValue());
//            this.update(uw);
//        }
        uw.eq(PropertyBill::getId,billId);
        uw.set(PropertyBill::getUpdateTime,new Date());
        uw.set(PropertyBill::getUpdateUser,AuthUtil.getUserId());
        uw.set(PropertyBill::getBillStatus, BillStatusEnum.YES_OUT.getValue());
        this.update(uw);
    }


    /**
     * 变更为未出账单
     * @param billId
     */
    public void changeNoOUt(long billId) {
        PropertyBill bill = this.getById(billId);
        if (bill.getBillStatus() != BillStatusEnum.YES_OUT.getValue()) {
            throw new ServiceException("当前账单状态不能变更为未出账单");
        }
        if (bill.getPayAmtFen() > 0) {
            throw new ServiceException("账单有收费，不支持当前操作");
        }
//        if (bill.getPayStatus() != PayStatusEnum.NO_PAY.getValue()) {
//            throw new ServiceException("该笔账单不能删除");
//        }
        LambdaUpdateWrapper<PropertyBill> uw = new LambdaUpdateWrapper<>();
        uw.eq(PropertyBill::getId,billId);
        uw.set(PropertyBill::getBillStatus, BillStatusEnum.NO_OUT.getValue());
        this.update(uw);
    }

    /**
     * 账单删除
     * @param billId
     */
    public void removeBill(long billId) {
        PropertyBill bill = this.getById(billId);
        if (bill.getPayAmtFen() > 0) {
            throw new ServiceException("账单有收费，不能删除账单");
        }
        if (bill.getBillStatus() == BillStatusEnum.PAID.getValue()) {
            throw new ServiceException("账单状态已结，不能删除账单");
        }
        if (bill.getPayStatus() == PayStatusEnum.PAID.getValue()) {
            throw new ServiceException("账单已收费，不能删除账单");
        }
        this.removeById(billId);
    }


    /**
     * 滞纳金金额计算
     * @param dto
     */
    @Transactional(rollbackFor = Exception.class)
    public void calcBreachAmt(PropertyCalcBreachAmtDto dto) {
        List<PropertyBill> billList = this.listByIds(dto.getBillIdList());
        List<Long> feeIdList = billList.stream().map(PropertyBill::getFeeId).collect(Collectors.toList());
        List<PropertyFeeStandard> feeList = propertyFeeStandardService.listByIds(feeIdList);
        List<PropertyBill> breachBillList = new ArrayList<>();
        for (PropertyBill bill : billList) {
            if (bill.getBreachDate() != null) {
                for (PropertyFeeStandard fee : feeList) {
                    if (Func.isNotEmpty(fee.getBreachStatue())
                            && fee.getBreachStatue() == YesOrNoEnum.YES.getValue()
                            && Func.isNotEmpty(fee.getBreachRate())
                            && DateUtil.compare(dto.getCalcBreachDate(),bill.getBreachDate()) >= 0) {
                        if (bill.getFeeId().equals(fee.getId())) {
                            long days = DateUtil.between(bill.getBreachDate(), dto.getCalcBreachDate(), DateUnit.DAY) + 1; // 违约天数
                            long breachAmtFen;
                            BigDecimal breachAmtFenBig;
                            BigDecimal ReceivableAmtFenBig = new BigDecimal(bill.getReceivableAmtFen()); // 应收金额
                            BigDecimal daysBig = new BigDecimal(days);
                            BigDecimal breachRate = fee.getBreachRate(); // 违约率
                            if (fee.getBreachCalcType() == 2) { // 违约金计算方式：1-计算方式，2-利滚利
                                // 利滚利:滞纳金 = 应收金额 * (1 + 违约率) ^ 违约天数次方 - 应收金额
                                double rate = Double.parseDouble(breachRate.add(new BigDecimal(1)).toString()); // (1 + 违约率)
                                double rateDays = Math.pow(rate, days); // (1 + 违约率) ^ 违约天数次方
                                breachAmtFenBig = ReceivableAmtFenBig.multiply(new BigDecimal(rateDays)).subtract(ReceivableAmtFenBig);
                            } else {
                                // 单利:滞纳金 = 应收金额 * 违约天数 * 违约率
                                breachAmtFenBig = ReceivableAmtFenBig.multiply(daysBig).multiply(breachRate);
                            }
                            breachAmtFenBig = breachAmtFenBig.setScale(0, RoundingMode.HALF_UP);

                            breachAmtFen = propertyBillingSettingsService.getBillAmtByFen(bill.getParkId(),Long.parseLong(breachAmtFenBig.toString()));
                            bill.setBreachAmtFen(breachAmtFen);
                            bill.setUpdateTime(new Date());
                            bill.setUpdateUser(AuthUtil.getUserId());
                            breachBillList.add(bill);
                        }
                    }
                }
            }
        }

        if (breachBillList.size() > 0) {
            this.updateBatchById(breachBillList);
            List<Long> billIdList = breachBillList.stream().map(PropertyBill::getId).collect(Collectors.toList());
            LambdaUpdateWrapper<PropertyBillSub> uw = new LambdaUpdateWrapper<>();
            uw.in(PropertyBillSub::getBillId,billIdList);
            uw.set(PropertyBillSub::getCalcBreachDate,dto.getCalcBreachDate());
            billSubService.update(uw);
        }

    }






    /**
     * 账单费用承担人修改
     * @param billId
     * @param customerId
     */
    public void updateBillCustomer(long billId,long customerId) {
        LambdaUpdateWrapper<PropertyBill> uw = new LambdaUpdateWrapper<>();
        uw.eq(PropertyBill::getId,billId);
        uw.set(PropertyBill::getCustomerId,customerId);
        uw.set(PropertyBill::getUpdateUser,AuthUtil.getUserId());
        this.update(uw);
    }


    /**
     * 账单退款
     * @param billId
     * @param refundAmtFen
     */
    public void refund(long billId, long refundAmtFen) {
        PropertyBill bill = this.getById(billId);
        log.info("billServiceRefund id=" + billId);
        if (bill.getPayStatus() != PayStatusEnum.PAID.getValue()
            && bill.getBillStatus() != BillStatusEnum.PAID.getValue()) {
            throw new ServiceException("该比账单状态不支持退款");
        }
        if (refundAmtFen > bill.getPayAmtFen()) {
            throw new ServiceException("退款金额大于已缴金额，不允许退款");
        }
        bill.setBillStatus(PayStatusEnum.REFUND.getValue());
        bill.setRefundAmtFen(refundAmtFen);
        bill.setUpdateUser(AuthUtil.getUserId());
        bill.setUpdateTime(new Date());
        this.updateById(bill);
    }


    /**
     * 缴费
     * @param billId
     * @param payAmtFen
     */
    public void pay(long billId, long payAmtFen) {
        PropertyBill bill = this.getById(billId);
        log.info("billServicePayId=" + billId);
        if (!(bill.getPayStatus() == PayStatusEnum.NO_PAY.getValue()
                || bill.getPayStatus() == PayStatusEnum.PART_PAY.getValue()
                || bill.getPayStatus() == PayStatusEnum.ARREARS.getValue())
                && bill.getBillStatus() != BillStatusEnum.YES_OUT.getValue()) {
            throw new ServiceException("该比账单状态异常，缴费失败");
        }

        if (bill.getNoPayAmtFen() > payAmtFen) { // 部分缴费
            if (bill.getPayStatus() == PayStatusEnum.NO_PAY.getValue()) {
                bill.setPayStatus(PayStatusEnum.PART_PAY.getValue());
            }
        } else { // 全额缴费
            bill.setBillStatus(BillStatusEnum.PAID.getValue());
            bill.setPayStatus(PayStatusEnum.PAID.getValue());
        }
        bill.setPayAmtFen(bill.getPayAmtFen() + payAmtFen);
        bill.setUpdateUser(AuthUtil.getUserId());
        bill.setUpdateTime(new Date());

        this.updateById(bill);
    }

    /**
     * 线下收费审核单审核过后，解除账单审核状态
     * @param billIdList
     */
    public void releaseProcess(List<Long> billIdList) {
        LambdaUpdateWrapper<PropertyBill> uw = new LambdaUpdateWrapper<>();
        uw.in(PropertyBill::getId,billIdList);
        uw.set(PropertyBill::getIsProcess, YesOrNoEnum.NO.getValue());
        this.update(uw);
    }

    /**
     * 线下收费发起审核单后，账单锁定审核状态
     * @param billIdList
     */
    public void lockProcess(List<Long> billIdList) {
        LambdaUpdateWrapper<PropertyBill> uw = new LambdaUpdateWrapper<>();
        uw.in(PropertyBill::getId,billIdList);
        uw.set(PropertyBill::getIsProcess, YesOrNoEnum.YES.getValue());
        this.update(uw);
    }


    /**
     * 收银台-费用减免
     * @param queryDto
     * @return
     */
    public NestPage<PropertyFeeReductionListVo> getFeeReductionList(PropertyFeeReductionListQueryDto queryDto) {
        LambdaQueryWrapper<PropertyBill> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(Func.isNotEmpty(queryDto.getHouseId()), PropertyBill::getHouseId, queryDto.getHouseId());
        queryWrapper.eq(Func.isNotEmpty(queryDto.getFeeStandardId()), PropertyBill::getFeeId, queryDto.getFeeStandardId());
        //年月查询
        //queryWrapper.eq(Func.isNotEmpty(queryDto.getFeeYearMonth()), PropertyBill::getUpdateTime, queryDto.getFeeYearMonth());
        queryWrapper.gt(PropertyBill::getReadjustAmtFen, 0);
        queryWrapper.gt(PropertyBill::getReductionBreachAmtFen, 0);
        IPage<PropertyBill> page = this.page(Condition.getPage(queryDto), queryWrapper);
        List<PropertyFeeReductionListVo> list = BeanUtil.copyProperties(page.getRecords(), PropertyFeeReductionListVo.class);
        list.forEach(vo -> {
            PropertyCustomerArchives customerArchives = customerArchivesService.getById(vo.getCustomerId());
            vo.setCustomerName(customerArchives.getCustomerName());
            PropertyHouses houses = housesService.getById(vo.getHouseId());
            vo.setHouseCode(houses.getResourceCode());
        });
        IPage<PropertyFeeReductionListVo> result = new Page<>(queryDto.getCurrent(), queryDto.getSize(), list.size());
        result.setRecords(list);
        return NestPage.of(result);
    }

    public Page<PropertyBillReductionListVo> billReductionPage(PropertyBillReductionPageQryDto param){
        Page<PropertyBillReductionListVo> page = new Page<>(param.getCurrent(), param.getSize());
        Page<PropertyBillReductionListVo> propertyFeeStandardListVoPage = baseMapper.billReductionPage(page, getPageQueryParam(param));
        if(Func.isNotEmpty(propertyFeeStandardListVoPage.getRecords())){
            for (PropertyBillReductionListVo a:propertyFeeStandardListVoPage.getRecords()) {
                PropertyFeeStandard feeStandard = propertyFeeStandardService.getById(a.getFeeId());
                if(Func.isNotEmpty(feeStandard)){
                    a.setFeeName(feeStandard.getFeeName());
                }
                PropertyBuildings buildings = buildingsService.getById(a.getBuildingId());
                if(Func.isNotEmpty(buildings)){
                    a.setBuildingName(buildings.getBuildingName());
                }
            }
        }
        return propertyFeeStandardListVoPage;
    }

    public void exportBillReduction(PropertyBillReductionPageQryDto param, HttpServletResponse response){
        param.setCurrent(-1);
        param.setSize(-1);
        Page<PropertyBillReductionListVo> propertyFeeStandardListVoPage = billReductionPage(param);
        if(Func.isNotEmpty(propertyFeeStandardListVoPage.getRecords())){
            List<PropertyBillReductionExcelVo> list=Func.copy(propertyFeeStandardListVoPage.getRecords(),PropertyBillReductionExcelVo.class);
            ExcelUtil.export(response, "减免指定金额统计", "减免指定金额统计", list, PropertyBillReductionExcelVo.class);
        }

    }

    @Transactional(rollbackFor = Exception.class)
    public boolean reminder(List<Long> ids){
        List<PropertyBill> propertyBills = listByIds(ids);
        Date now =new Date();
        List<PropertyBillReminderMessageSendDTO> list=new ArrayList<>();
        for (PropertyBill item:propertyBills) {
            PropertyBillSub byBillId = billSubService.getByBillId(item.getId());
            if(Func.isEmpty(byBillId)){
                continue;
            }
            byBillId.setLastCallTime(now);
            billSubService.updateById(byBillId);

            PropertyCustomerArchives customerArchives = customerArchivesService.getById(item.getCustomerId());
            if(Func.isEmpty(customerArchives)){
                continue;
            }
            list.add(PropertyBillReminderMessageSendDTO.builder()
                    .phone(customerArchives.getLinkPhoneOne())
                    .parkId(item.getParkId())
                    .createBillType(item.getCreateBillType())
                    .receivableAmtFen(item.getReceivableAmtFen())
                    .periodicCharge(DateUtil.format(item.getStartDate(),NORM_DATE_PATTERN)+"~"+DateUtil.format(item.getEndDate(),NORM_DATE_PATTERN)).build());
        }
        publisher.publishEvent(PropertyBillReminderMessageDTO.builder().messageSendDTOS(list).build());
        return true;
    }

    private QueryWrapper<String> getPageQueryParam(PropertyBillReductionPageQryDto param) {
        QueryWrapper<String> qw = new QueryWrapper<>();
        qw.eq(Func.isNotEmpty(param.getParkId()),"a.park_id",param.getParkId())
                .eq(Func.isNotEmpty(param.getBuildingId()),"a.building_id",param.getBuildingId())
                .eq(Func.isNotEmpty(param.getHouseId()),"a.house_id",param.getHouseId())
                .eq(Func.isNotEmpty(param.getCustomerName()),"e.customer_name",param.getCustomerName())
                .eq(Func.isNotEmpty(param.getFeeId()),"a.fee_id",param.getFeeId())
                .eq(Func.isNotEmpty(param.getFeeItemId()),"a.fee_item_id",param.getFeeItemId())
                .gt("a.house_id",0)
                .eq("a.is_deleted",0);
        return qw;
    }

    public void feeReductionListExport(PropertyFeeReductionListQueryDto queryDto, HttpServletResponse response) {
        LambdaQueryWrapper<PropertyBill> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(Func.isNotEmpty(queryDto.getHouseId()), PropertyBill::getHouseId, queryDto.getHouseId());
        queryWrapper.eq(Func.isNotEmpty(queryDto.getFeeStandardId()), PropertyBill::getFeeId, queryDto.getFeeStandardId());
        //年月查询
        //queryWrapper.eq(Func.isNotEmpty(queryDto.getFeeYearMonth()), PropertyBill::getUpdateTime, queryDto.getFeeYearMonth());
        queryWrapper.gt(PropertyBill::getReadjustAmtFen, 0);
        queryWrapper.gt(PropertyBill::getReductionBreachAmtFen, 0);
        List<PropertyBill> billList = this.list(queryWrapper);
        List<PropertyFeeReductionExcelVo> excelVoList = BeanUtil.copyProperties(billList, PropertyFeeReductionExcelVo.class);
        excelVoList.forEach(vo -> {
            PropertyCustomerArchives customerArchives = customerArchivesService.getById(vo.getCustomerId());
            vo.setCustomerName(customerArchives.getCustomerName());
            PropertyHouses houses = housesService.getById(vo.getHouseId());
            vo.setHouseCode(houses.getResourceCode());
        });
        try {
            String fileName = URLEncoder.encode("费用减免列表", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            EasyExcel.write(response.getOutputStream(), PropertyFeeReductionExcelVo.class)
                    .sheet("费用减免列表")
                    .doWrite(excelVoList); //最终处理好的数据结果集list
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }


    /**
     * 查询物业收费统计数据
     *
     * @param queryDto 物业收费统计数据查询参数
     * @return 物业收费统计数据Vo
     */
    public PropertyFeeStatisticsVo getFeeStatistics(PropertyFeeStatisticsQueryDto queryDto) {
        if (Func.isEmpty(queryDto.getParkIdList())) {
            ContextUser user = AuthUtil.getUser();
            Long customerManageId = user.getCustomerManageId();
            log.info("customerManageId: {}", customerManageId);
            List<Long> parkIdList = pmgBatParkInfoService.getAllByOperator(customerManageId).stream().map(PmgBatParkInfo::getId).collect(Collectors.toList());
            queryDto.setParkIdList(parkIdList);
        }
        PropertyFeeStatisticsVo feeStatisticsVo = new PropertyFeeStatisticsVo();
        feeStatisticsVo.setParkCount(queryDto.getParkIdList().size());
        List<Long> parkIdList = queryDto.getParkIdList();
        //主营业收入 todo 后续还要减掉退款金额
        List<Long> feeItemIdList = feeItemService.lambdaQuery().eq(PropertyFeeItem::getFeeSubject, 1)
                .in(PropertyFeeItem::getParkId, parkIdList).list().stream()
                .map(PropertyFeeItem::getId).collect(Collectors.toList());
        log.info("feeItemIdList: {}", feeItemIdList);
        List<PropertyBill> propertyBillList = new ArrayList<>();
        if (Func.isNotEmpty(feeItemIdList)) {
             propertyBillList = this.lambdaQuery().in(PropertyBill::getFeeItemId, feeItemIdList)
                    .in(PropertyBill::getParkId, parkIdList)
                    .in(PropertyBill::getPayStatus, 2, 4).list();
            Long mainIncome = propertyBillList.stream().mapToLong(PropertyBill::getPayAmtFen).sum();
            feeStatisticsVo.setMainIncome(mainIncome);
        } else {
            feeStatisticsVo.setMainIncome(0l);
        }
        //营业外收入 todo 后续还要减掉退款金额
        feeItemIdList = feeItemService.lambdaQuery().eq(PropertyFeeItem::getFeeSubject, 2)
                .in(PropertyFeeItem::getParkId, parkIdList).list().stream()
                .map(PropertyFeeItem::getId).collect(Collectors.toList());
        log.info("feeItemIdList: {}", feeItemIdList);
        if (Func.isNotEmpty(feeItemIdList)) {
            propertyBillList = this.lambdaQuery().in(PropertyBill::getFeeItemId, feeItemIdList)
                    .in(PropertyBill::getParkId, parkIdList)
                    .in(PropertyBill::getPayStatus, 2, 4).list();
            Long extraIncome = propertyBillList.stream().mapToLong(PropertyBill::getPayAmtFen).sum();
            feeStatisticsVo.setExtraIncome(extraIncome);
        } else {
            feeStatisticsVo.setExtraIncome(0l);
        }
        //应收金额(单位分)
        propertyBillList = this.lambdaQuery().in(PropertyBill::getParkId, parkIdList)
                .in(PropertyBill::getPayStatus, 2, 4).list().stream().filter(bill -> Func.isNotEmpty(bill.getPayTime()) && DateUtil.format(bill.getPayTime(), "yyyy-MM").equals(queryDto.getYearMonth())).collect(Collectors.toList());
        Long receivableAmount = propertyBillList.stream().mapToLong(PropertyBill::getReceivableAmtFen).sum();
        feeStatisticsVo.setReceivableAmount(receivableAmount);
        //减免&优惠金额(单位分) direct_reduction_amt_fen + pay_reduction_amt_fen + coupon_amt_fen + reduction_breach_amt_fen（有正负） + readjust_amt_fen（有正负）
        Long reductionAmount = propertyBillList.stream().mapToLong(PropertyBill::getDirectReductionAmtFen).sum() +
                propertyBillList.stream().mapToLong(PropertyBill::getPayReductionAmtFen).sum() +
                propertyBillList.stream().mapToLong(PropertyBill::getCouponAmtFen).sum() +
                propertyBillList.stream().mapToLong(PropertyBill::getReductionBreachAmtFen).sum() +
                propertyBillList.stream().mapToLong(PropertyBill::getReadjustAmtFen).sum();
        feeStatisticsVo.setReductionAmount(reductionAmount);
        // 实收金额(单位分)
        Long actualAmount = propertyBillList.stream().mapToLong(PropertyBill::getPayAmtFen).sum();
        feeStatisticsVo.setActualAmount(actualAmount);
        //预存金额(单位分) pay_amt_fen + （create_bill_type=4）（如果扣除退款的话减去 refund_amt_fen）
        Long prestoreAmount = propertyBillList.stream().filter(bill -> bill.getCreateBillType() == 4).mapToLong(PropertyBill::getPayAmtFen).sum()
                - propertyBillList.stream().filter(bill -> bill.getCreateBillType() == 4).mapToLong(PropertyBill::getRefundAmtFen).sum();
        feeStatisticsVo.setPrestoreAmount(prestoreAmount);
        // 押金金额(单位分) pay_amt_fen + （create_bill_type=3）（如果扣除退款的话减去 refund_amt_fen）
        Long depositAmount = propertyBillList.stream().filter(bill -> bill.getCreateBillType() == 3).mapToLong(PropertyBill::getPayAmtFen).sum()
                - propertyBillList.stream().filter(bill -> bill.getCreateBillType() == 3).mapToLong(PropertyBill::getRefundAmtFen).sum();
        feeStatisticsVo.setDepositAmount(depositAmount);
        // 年度缴费信息
        List<PropertyFeeStatisticsVo.PropertyAnnualPayInfo> annualPayInfoList = new ArrayList<>();
        for (Integer i = 1; i <= 12; i++) {
            PropertyFeeStatisticsVo.PropertyAnnualPayInfo annualPayInfo = new PropertyFeeStatisticsVo.PropertyAnnualPayInfo();
            annualPayInfo.setMonth(i);
            String month = i < 10 ? "0" + i : i + "";
            String yearMonth = queryDto.getYearMonth().substring(0, 5) + month;
            propertyBillList = this.lambdaQuery().in(PropertyBill::getParkId, parkIdList)
                    .in(PropertyBill::getPayStatus, 2, 4).list()
                    .stream().filter(bill -> Func.isNotEmpty(bill.getPayTime()) && DateUtil.format(bill.getPayTime(), "yyyy-MM").equals(yearMonth)).collect(Collectors.toList());
            Long amount = propertyBillList.stream().mapToLong(PropertyBill::getPayAmtFen).sum();
            annualPayInfo.setAmount(amount);
            annualPayInfoList.add(annualPayInfo);
        }
        feeStatisticsVo.setAnnualPayInfoList(annualPayInfoList);

        //楼栋收缴信息
        List<PropertyFeeStatisticsVo.PropertyBuildingPayInfo> buildingPayInfoList = new ArrayList<>();
        propertyBillList = this.lambdaQuery().in(PropertyBill::getParkId, parkIdList)
                .list()
                .stream().filter(bill -> Func.isNotEmpty(bill.getPayTime()) && DateUtil.format(bill.getPayTime(), "yyyy-MM").equals(queryDto.getYearMonth()) && bill.getBuildingId() != 0).collect(Collectors.toList());

        Map<Long, PropertyBuildings> buildingInfoMap = getBuildingMap();
        Map<Long, List<PropertyBill>> buildinglistMap = propertyBillList.stream().collect(Collectors.groupingBy(PropertyBill::getBuildingId));
        Map<Long, String> parkMap = getParkMap();
        buildinglistMap.forEach((buildingId, list) -> {
            PropertyFeeStatisticsVo.PropertyBuildingPayInfo buildingPayInfo = new PropertyFeeStatisticsVo.PropertyBuildingPayInfo();
            buildingPayInfo.setBuildingName(buildingInfoMap.get(buildingId).getBuildingName());
            buildingPayInfo.setParkId(buildingInfoMap.get(buildingId).getParkId());
            buildingPayInfo.setParkName(parkMap.get(buildingInfoMap.get(buildingId).getParkId()));
            buildingPayInfo.setPaidCount(list.stream().filter(bill -> bill.getPayAmtFen() > 0).collect(Collectors.toList()).size());
            buildingPayInfo.setUnpaidCount(list.stream().filter(bill -> bill.getPayAmtFen() == 0).collect(Collectors.toList()).size());
            buildingPayInfoList.add(buildingPayInfo);
        });
        feeStatisticsVo.setBuildingPayInfoList(buildingPayInfoList);
        //缴费方式占比信息
        PropertyFeeStatisticsVo.PropertyPayMethodInfo payMethodInfo = new PropertyFeeStatisticsVo.PropertyPayMethodInfo();
        propertyBillList = this.lambdaQuery().in(PropertyBill::getParkId, parkIdList)
                .list().stream().filter(bill ->  Func.isNotEmpty(bill.getPayTime()) && DateUtil.format(bill.getPayTime(), "yyyy-MM")
                        .equals(queryDto.getYearMonth())).collect(Collectors.toList());

        //在线缴费金额(单位分)
        Long onlinePayAmount = propertyBillList.stream().filter(bill -> bill.getPayType() == PropertyPayTypeEnum.USER_PAY.getValue()).mapToLong(PropertyBill::getPayAmtFen).sum();
        payMethodInfo.setOnlinePayAmount(onlinePayAmount);
        //线下缴费金额(单位分)
        Long offlinePayAmount = propertyBillList.stream().filter(bill -> bill.getPayType() == PropertyPayTypeEnum.OFFLINE_PAY.getValue()).mapToLong(PropertyBill::getPayAmtFen).sum();
        payMethodInfo.setOfflinePayAmount(offlinePayAmount);
        //预存抵扣金额(单位分)
        Long preAmount = propertyBillList.stream().filter(bill -> bill.getPayType() == PropertyPayTypeEnum.BACK_DEDUCT.getValue() || bill.getPayType() == PropertyPayTypeEnum.AUTO_DEDUCT.getValue()).mapToLong(PropertyBill::getPayAmtFen).sum();
        payMethodInfo.setPrestoreAmount(preAmount);
        feeStatisticsVo.setPayMethodInfo(payMethodInfo);
        //缴费项目信息
        List<PropertyFeeStatisticsVo.PropertyPayItemInfo> payItemInfoList = new ArrayList<>();
        propertyBillList = this.lambdaQuery().in(PropertyBill::getParkId, parkIdList)
                .in(PropertyBill::getPayStatus, 2, 4).list().stream().filter(bill ->  Func.isNotEmpty(bill.getPayTime()) && DateUtil.format(bill.getPayTime(), "yyyy-MM")
                        .equals(queryDto.getYearMonth())).collect(Collectors.toList());

        Map<Long, String> feeItemMap = getFeeItemMap();
        Map<Long, List<PropertyBill>> listMap = propertyBillList.stream().collect(Collectors.groupingBy(PropertyBill::getFeeItemId));
        listMap.forEach((feeItemId, list) -> {
            PropertyFeeStatisticsVo.PropertyPayItemInfo payItemInfo = new PropertyFeeStatisticsVo.PropertyPayItemInfo();
            payItemInfo.setFeeItemId(feeItemId);
            payItemInfo.setFeeItemName(feeItemMap.get(feeItemId));
            Long feeItemAmount = list.stream().mapToLong(PropertyBill::getPayAmtFen).sum();
            payItemInfo.setFeeItemAmount(feeItemAmount);
            payItemInfoList.add(payItemInfo);
        });

        feeStatisticsVo.setPayItemInfoList(payItemInfoList);
        return feeStatisticsVo;
    }

    /**
     * @description 获取费用项目列表Map
     */
    private Map<Long, String> getFeeItemMap() {
        List<PropertyFeeItem> feeItemList = feeItemService.list();
        return feeItemList.stream().collect(Collectors.toMap(PropertyFeeItem::getId, PropertyFeeItem::getFeeItemName));
    }

    /**
     * @description 获取园区信息列表Map
     */
    private Map<Long, String> getParkMap() {
        List<PmgBatParkInfo> parkList = pmgBatParkInfoService.getAllPark();
        return parkList.stream().collect(Collectors.toMap(PmgBatParkInfo::getId, PmgBatParkInfo::getParkName));
    }

    /**
     * @description 获取房源信息列表Map
     */
    private Map<Long, PropertyBuildings> getBuildingMap() {
        List<PropertyBuildings> buildingsList = buildingsService.list();
        return buildingsList.stream().collect(Collectors.toMap(PropertyBuildings::getId, a -> a));
    }

    public int countByPayStatusAndHouseId(PayStatusEnum payStatusEnum,Long houseId){
        LambdaQueryWrapper<PropertyBill> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(PropertyBill::getPayStatus,payStatusEnum.getValue())
                .eq(PropertyBill::getHouseId,houseId);
        return count(queryWrapper);
    }



}
