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

import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.yuchen.saas.park.api.dto.property.PropertyBillPayBto;
import com.yuchen.saas.park.api.dto.property.PropertyBillPayCallBackDto;
import com.yuchen.saas.park.api.dto.property.PropertyBillPayQueryDto;
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.CreateBillTypeEnum;
import com.yuchen.saas.park.api.enums.property.DepositStatusEnum;
import com.yuchen.saas.park.api.enums.property.PropertyPayTypeEnum;
import com.yuchen.saas.park.api.enums.property.YesOrNoEnum;
import com.yuchen.saas.park.api.vo.property.PropertyBillPayExcelVo;
import com.yuchen.saas.park.api.vo.property.PropertyBillPayListVo;
import com.yuchen.saas.park.api.vo.property.PropertyHousesVo;
import com.yuchen.saas.park.service.mapper.property.PropertyBillPayMapper;
import com.yuchen.saas.park.service.service.PmgBatParkInfoService;
import com.yuchen.saas.park.service.service.property.*;
import com.yuchen.saas.park.service.service.property.bill.PropertyBillService;
import com.yuchen.saas.payment.api.enums.PayStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.nest.springwrap.core.log.exception.ServiceException;
import org.nest.springwrap.core.mp.base.BaseServiceImpl;
import org.nest.springwrap.core.mp.support.Condition;
import org.nest.springwrap.core.mp.support.NestPage;
import org.nest.springwrap.core.tool.utils.BeanUtil;
import org.nest.springwrap.core.tool.utils.Func;
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.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author 蔡文强
 * @Date 2024-01-04 10:56
 * @Description 账单缴费记录 服务实现类
 */
@Slf4j
@Service
public class PropertyBillPayServiceImpl extends BaseServiceImpl<PropertyBillPayMapper, PropertyBillPay> implements IPropertyBillPayService {

    @Resource
    private PmgBatParkInfoService parkInfoService;
    @Resource
    private IPropertyCustomerArchivesService customerArchivesService;
    @Resource
    private IPropertyFeeItemService feeItemService;
    @Resource
    private PropertyHousesService housesService;
    @Resource
    private PropertyBillService propertyBillService;
    @Resource
    private IPropertyBillPayDetailService propertyBillPayDetailService;
    @Resource
    private PropertyPrepaymentService propertyPrepaymentService;
    @Resource
    private PropertyDepositService propertyDepositService;
    @Resource
    private PropertyBusinessChargesService propertyBusinessChargesService;



    @Transactional(rollbackFor = Exception.class)
    public PropertyBillPay insertBillPay(PropertyBillPayBto billPayBto) {
        log.info("insertBillPayParam{},billPayBto={}",billPayBto.getBillPayId(), JSONObject.toJSONString(billPayBto));
        if (Func.isEmpty(billPayBto.getPropertyBillIdList())) {
            throw new ServiceException("物业缴费id明细不能为空");
        }
        PropertyBill bill = propertyBillService.getById(billPayBto.getPropertyBillIdList().get(0));
        PropertyBillPay po = new PropertyBillPay();
        po.setId(billPayBto.getBillPayId());
        po.setParkId(bill.getParkId());
        po.setCustomerId(bill.getCustomerId());
        po.setHouseId(bill.getHouseId());
        po.setFeeItemId(bill.getFeeItemId());
        po.setReceivableAmtFen(billPayBto.getReceivableAmtFen());
        po.setPayAmtFen(billPayBto.getPayAmtFen());
        po.setPayReductionAmtFen(billPayBto.getPayReductionAmtFen());
        po.setOrderId(billPayBto.getOrderId());
        po.setOrderNo(billPayBto.getOrderNumber());
        po.setOrderStatus(billPayBto.getOrderStatus());
        po.setSource(billPayBto.getSource());
        po.setCreateTime(new Date());
        po.setCreateUser(billPayBto.getCreateUser());
        po.setCreateDept(billPayBto.getCreateDept());
        this.save(po);

        List<PropertyBillPayDetail> details = new ArrayList<>();
        billPayBto.getPropertyBillIdList().forEach(billId -> {
            PropertyBillPayDetail detail = new PropertyBillPayDetail();
            detail.setBillPayId(po.getId());
            detail.setBillId(billId);
            details.add(detail);
        });

        propertyBillPayDetailService.saveBatch(details);
        log.info("insertBillPaySuccess{}",billPayBto.getBillPayId());
        return po;
    }

    /**
     * 物业缴费回调
     * @param dto
     */
    @Transactional(rollbackFor = Exception.class)
    public void propertyFeePaySuccessCallBack(PropertyBillPayCallBackDto dto) {
        log.info("PropertyBillPayServiceImpl::propertyFeePaySuccessCallBack={}",dto.getBillSysCode());
        LambdaUpdateWrapper<PropertyBillPay> uw = new LambdaUpdateWrapper<>();
        uw.eq(PropertyBillPay::getId,dto.getBillSysCode());
        uw.set(PropertyBillPay::getOrderStatus, PayStatusEnum.SUCCESS.getValue());
        uw.set(PropertyBillPay::getPayTime,new Date());
        uw.set(PropertyBillPay::getPayType,dto.getPropertyPayTypeEnum().getValue());
        uw.set(PropertyBillPay::getPayMethod,dto.getPropertyPayMethodEnum().getValue());
        this.update(uw);

        List<Long> billIdList = propertyBillPayDetailService.listBillIds(Long.parseLong(dto.getBillSysCode()));
        if (Func.isEmpty(billIdList)) {
            return;
        }
        List<PropertyBill> billList = propertyBillService.listByIds(billIdList);
        Set<Long> depositBillIdSet = new HashSet<>(); // 押金账单
        Set<Long> businessBillIdSet = new HashSet<>(); // 经营性账单
        billList.forEach(bill -> {
            try {
                propertyBillService.pay(bill.getId(), bill.getNoPayAmtFen());
                if (bill.getCreateBillType() == CreateBillTypeEnum.DEPOSIT.getValue()) {
                    if (Func.isNotEmpty(bill.getRetraceId())) {
                        depositBillIdSet.add(bill.getRetraceId());
                    }
                }
                if (bill.getCreateBillType() == CreateBillTypeEnum.BUSINESS.getValue()) {
                    if (Func.isNotEmpty(bill.getRetraceId())) {
                        businessBillIdSet.add(bill.getRetraceId());
                    }
                }
            } catch (Exception e) {
                log.info("propertyFeePaySuccessCallBackBillId=" + bill.getId());
                log.error("propertyFeePaySuccessCallBack error", e);
            }
        });

        depositBillIdSet.forEach(depositId -> {
            LambdaUpdateWrapper<PropertyDeposit> depUw = new LambdaUpdateWrapper<>();
            depUw.eq(PropertyDeposit::getId,depositId);
            depUw.set(PropertyDeposit::getDepositStatus, DepositStatusEnum.PAID.getValue());
            propertyDepositService.update(depUw);
        });
        businessBillIdSet.forEach(businessId -> {
            LambdaUpdateWrapper<PropertyBusinessCharges> busUw = new LambdaUpdateWrapper<>();
            busUw.eq(PropertyBusinessCharges::getId,businessId);
            busUw.set(PropertyBusinessCharges::getStatus, DepositStatusEnum.PAID.getValue());
            propertyBusinessChargesService.update(busUw);
        });


    }

    /**
     * 预存款充值回调
     * @param dto
     */
    @Transactional(rollbackFor = Exception.class)
    public void propertyPrepaymentFeePaySuccessCallBack(PropertyBillPayCallBackDto dto) {
        log.info("PropertyBillPayServiceImpl::propertyPrepaymentFeePaySuccessCallBack={}",dto.getBillSysCode());
        LambdaUpdateWrapper<PropertyPrepayment> uw = new LambdaUpdateWrapper<>();
        uw.eq(PropertyPrepayment::getId,dto.getBillSysCode());
        uw.set(PropertyPrepayment::getStatus, YesOrNoEnum.YES.getValue());
        propertyPrepaymentService.update(uw);
    }


    @Override
    public NestPage<PropertyBillPayListVo> getBillPayList(PropertyBillPayQueryDto queryDto) {
        LambdaQueryWrapper<PropertyBillPay> queryWrapper = Wrappers.lambdaQuery();
        if (Func.isNotEmpty(queryDto.getType())) {
            if (queryDto.getType() == 1) {
                queryWrapper.in(PropertyBillPay::getPayType, PropertyPayTypeEnum.BACK_DEDUCT.getValue(), PropertyPayTypeEnum.AUTO_DEDUCT.getValue(), PropertyPayTypeEnum.USER_PAY.getValue());
            } else if (queryDto.getType() == 2) {
                queryWrapper.eq(PropertyBillPay::getPayType, PropertyPayTypeEnum.OFFLINE_PAY.getValue());
            }
        }
        queryWrapper.eq(Func.isNotEmpty(queryDto.getParkId()), PropertyBillPay::getParkId, queryDto.getParkId());
        queryWrapper.eq(Func.isNotEmpty(queryDto.getHouseId()), PropertyBillPay::getHouseId, queryDto.getHouseId());
        queryWrapper.eq(Func.isNotEmpty(queryDto.getCustomerId()), PropertyBillPay::getCustomerId, queryDto.getCustomerId());
        queryWrapper.like(Func.isNotEmpty(queryDto.getContractNum()), PropertyBillPay::getContractNum, queryDto.getContractNum());
        queryWrapper.eq(Func.isNotEmpty(queryDto.getInvoiceStatus()), PropertyBillPay::getInvoiceStatus, queryDto.getInvoiceStatus());
        queryWrapper.eq(Func.isNotEmpty(queryDto.getPayType()), PropertyBillPay::getPayType, queryDto.getPayType());
        queryWrapper.eq(Func.isNotEmpty(queryDto.getPayMethod()), PropertyBillPay::getPayMethod, queryDto.getPayMethod());
        queryWrapper.eq(Func.isNotEmpty(queryDto.getPayTime()), PropertyBillPay::getPayTime, queryDto.getPayTime());
        queryWrapper.like(Func.isNotEmpty(queryDto.getOrderNo()), PropertyBillPay::getOrderNo, queryDto.getOrderNo());
        queryWrapper.ge(Func.isNotEmpty(queryDto.getPayStartTime()), PropertyBillPay::getPayTime, queryDto.getPayStartTime());
        queryWrapper.le(Func.isNotEmpty(queryDto.getPayEndTime()), PropertyBillPay::getPayTime, queryDto.getPayEndTime());
        queryWrapper.orderByDesc(PropertyBillPay::getCreateTime);
        IPage<PropertyBillPay> page = this.page(Condition.getPage(queryDto), queryWrapper);
        IPage<PropertyBillPayListVo> result = new Page<>(queryDto.getCurrent(), queryDto.getSize());
        List<PropertyBillPay> recordList = page.getRecords();
        List<PropertyBillPayListVo> list = BeanUtil.copyProperties(recordList, PropertyBillPayListVo.class);
        Map<Long, String> parkMap = getParkMap();
        Map<Long, String> CustomerArchivesMap = getCustomerArchivesMap();
        Map<Long, String> feeItemMap = getFeeItemMap();
        Map<Long, PropertyHousesVo> houseInfoMap = getHouseInfoMap();
        for(PropertyBillPayListVo vo : list) {
            vo.setParkName(parkMap.get(vo.getParkId()));
            vo.setCustomerName(CustomerArchivesMap.get(vo.getCustomerId()));
            vo.setHouseCode(houseInfoMap.get(vo.getHouseId()).getResourceCode());
            vo.setFeeItemName(feeItemMap.get(vo.getFeeItemId()));
        }
        result.setRecords(list);
        result.setTotal(page.getTotal());
        return NestPage.of(result);
    }

    @Override
    public void billPayListExport(PropertyBillPayQueryDto queryDto, HttpServletResponse response) {
        LambdaQueryWrapper<PropertyBillPay> queryWrapper = Wrappers.lambdaQuery();
        if (Func.isNotEmpty(queryDto.getType())) {
            if (queryDto.getType() == 1) {
                queryWrapper.in(PropertyBillPay::getPayType, PropertyPayTypeEnum.BACK_DEDUCT.getValue(), PropertyPayTypeEnum.AUTO_DEDUCT.getValue(), PropertyPayTypeEnum.USER_PAY.getValue());
            } else if (queryDto.getType() == 2) {
                queryWrapper.eq(PropertyBillPay::getPayType, PropertyPayTypeEnum.OFFLINE_PAY.getValue());
            }
        }
        queryWrapper.eq(Func.isNotEmpty(queryDto.getParkId()), PropertyBillPay::getParkId, queryDto.getParkId());
        queryWrapper.eq(Func.isNotEmpty(queryDto.getHouseId()), PropertyBillPay::getHouseId, queryDto.getHouseId());
        queryWrapper.eq(Func.isNotEmpty(queryDto.getCustomerId()), PropertyBillPay::getCustomerId, queryDto.getCustomerId());
        queryWrapper.like(Func.isNotEmpty(queryDto.getContractNum()), PropertyBillPay::getContractNum, queryDto.getContractNum());
        queryWrapper.eq(Func.isNotEmpty(queryDto.getInvoiceStatus()), PropertyBillPay::getInvoiceStatus, queryDto.getInvoiceStatus());
        queryWrapper.eq(Func.isNotEmpty(queryDto.getPayType()), PropertyBillPay::getPayType, queryDto.getPayType());
        queryWrapper.eq(Func.isNotEmpty(queryDto.getPayMethod()), PropertyBillPay::getPayMethod, queryDto.getPayMethod());
        queryWrapper.eq(Func.isNotEmpty(queryDto.getPayTime()), PropertyBillPay::getPayTime, queryDto.getPayTime());
        queryWrapper.eq(PropertyBillPay::getOrderStatus, 2);
        queryWrapper.orderByDesc(PropertyBillPay::getCreateTime);
        List<PropertyBillPay> list = this.list(queryWrapper);
        List<PropertyBillPayExcelVo> excelVoList = BeanUtil.copyProperties(list, PropertyBillPayExcelVo.class);
        Map<Long, String> parkMap = getParkMap();
        Map<Long, String> CustomerArchivesMap = getCustomerArchivesMap();
        Map<Long, String> feeItemMap = getFeeItemMap();
        Map<Long, PropertyHousesVo> houseInfoMap = getHouseInfoMap();
        for(PropertyBillPayExcelVo vo : excelVoList) {
            vo.setParkName(parkMap.get(vo.getParkId()));
            vo.setCustomerName(CustomerArchivesMap.get(vo.getCustomerId()));
            vo.setHouseCode(houseInfoMap.get(vo.getHouseId()).getResourceCode());
            vo.setFeeItemName(feeItemMap.get(vo.getFeeItemId()));
            //todo 枚举状态转换
        }

        try {
            String fileName = URLEncoder.encode("账单缴费记录表", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            EasyExcel.write(response.getOutputStream(), PropertyBillPayExcelVo.class)
                    .sheet("账单缴费记录表")
                    .doWrite(excelVoList); //最终处理好的数据结果集list
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }

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

    /**
     * @description 获取客户档案Map
     */
    private Map<Long, String> getCustomerArchivesMap() {
        List<PropertyCustomerArchives> customerArchivesList = customerArchivesService.list();
        return customerArchivesList.stream().collect(Collectors.toMap(PropertyCustomerArchives::getId, PropertyCustomerArchives::getCustomerName));
    }

    /**
     * @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, PropertyHousesVo> getHouseInfoMap() {
        List<PropertyHouses> housesList = housesService.list();
        List<PropertyHousesVo> resultList = BeanUtil.copyProperties(housesList, PropertyHousesVo.class);
        for (PropertyHousesVo vo : resultList) {
            vo.setBuildingName(housesService.getPropertyHousesDetail(vo.getId()).getBuildingName());
        }
        return resultList.stream().collect(Collectors.toMap(PropertyHouses::getId, a -> a));
    }

}
