package com.propertyManagement.business.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import com.propertyManagement.business.domain.WxLiveBill;
import com.propertyManagement.business.domain.WxProprietorLedger;
import com.propertyManagement.business.domain.WxRepairs;
import com.propertyManagement.business.domain.vo.LoginWxUser;
import com.propertyManagement.business.enums.PayBizEnum;
import com.propertyManagement.business.mapper.WxLiveBillMapper;
import com.propertyManagement.business.mapper.WxProprietorLedgerMapper;
import com.propertyManagement.business.mapper.WxRepairsMapper;
import com.propertyManagement.business.support.util.AuthUtil;
import com.propertyManagement.common.core.domain.BaseEntity;
import com.propertyManagement.common.enums.LiveBillState;
import com.propertyManagement.common.enums.LiveBillType;
import com.propertyManagement.common.enums.PayState;
import com.propertyManagement.common.enums.RepairsState;
import com.propertyManagement.common.utils.StringUtils;
import com.propertyManagement.common.core.page.TableDataInfo;
import com.propertyManagement.common.core.domain.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.propertyManagement.common.utils.ip.IpUtils;
import com.propertyManagement.pay.support.pay.CmsUnipayProvider;
import com.propertyManagement.pay.support.pay.PayReqModel;
import com.siashan.toolkit.core.enums.EnumUtil;
import com.siashan.toolkit.core.wrapper.AjaxResult;
import com.siashan.unipay4j.core.Result;
import com.siashan.unipay4j.core.TradeStateEnum;
import com.siashan.unipay4j.core.pay.IPayment;
import com.siashan.unipay4j.core.pay.PayModel;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import com.propertyManagement.business.domain.bo.WxPayRecordBo;
import com.propertyManagement.business.domain.vo.WxPayRecordVo;
import com.propertyManagement.business.domain.WxPayRecord;
import com.propertyManagement.business.mapper.WxPayRecordMapper;
import com.propertyManagement.business.service.IWxPayRecordService;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Collection;

/**
 * 缴费记录Service业务层处理
 *
 * @author liushuai
 * @date 2025-05-14
 */
@RequiredArgsConstructor
@Service
public class WxPayRecordServiceImpl implements IWxPayRecordService {

    private final WxPayRecordMapper baseMapper;
    private final WxLiveBillMapper liveBillMapper;
    private final WxRepairsMapper repairsMapper;
    private final WxProprietorLedgerMapper proprietorLedgerMapper;

    /**
     * 查询缴费记录
     */
    @Override
    public WxPayRecordVo queryById(Long recordId){
        return baseMapper.selectVoById(recordId);
    }

    /**
     * 查询缴费记录列表
     */
    @Override
    public TableDataInfo<WxPayRecordVo> queryPageList(WxPayRecordBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<WxPayRecord> lqw = buildQueryWrapper(bo);
        Page<WxPayRecordVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询缴费记录列表
     */
    @Override
    public List<WxPayRecordVo> queryList(WxPayRecordBo bo) {
        LambdaQueryWrapper<WxPayRecord> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    /**
     * 支付
     *
     * @param reqModel
     */
    @Override
    public Result<Object> pay(PayReqModel reqModel) {
        PayBizEnum payBiz = EnumUtil.getEnum(PayBizEnum.class, reqModel.getPayBizCode());
        Long orderId = null;
        BigDecimal payAmount = BigDecimal.ZERO;
        Integer orderType = null;
        Date startTime = null;
        Date endTime = null;
        switch (payBiz) {
            case PRESTORE:
                WxLiveBill wxLiveBill = liveBillMapper.selectById(reqModel.getOrderId());
                if (null == wxLiveBill) {
                    throw new RuntimeException("订单信息不存在，请刷新后重试");
                }
                if (LiveBillState.YES.getCode().equals(wxLiveBill.getState())) {
                    throw new RuntimeException("订单已支付，无需重新支付！");
                }
                if (reqModel.getAmount().doubleValue() != wxLiveBill.getMoney().doubleValue()) {
                    throw new RuntimeException("支付金额不匹配");
                }
                orderId = wxLiveBill.getBillId();
                payAmount = wxLiveBill.getMoney();
                orderType = wxLiveBill.getType();
                startTime = wxLiveBill.getStartTime();
                endTime = wxLiveBill.getEndTime();
                break;
            case MAINTAIN:
                WxRepairs wxRepairs = repairsMapper.selectById(reqModel.getOrderId());
                if (null == wxRepairs) {
                    throw new RuntimeException("订单信息不存在，请刷新后重试");
                }
                if (!RepairsState.TWO.getCode().equals(wxRepairs.getState())) {
                    throw new RuntimeException("订单状态错误，请刷新后重试！");
                }
                if (reqModel.getAmount().doubleValue() != wxRepairs.getMoney().doubleValue()) {
                    throw new RuntimeException("支付金额不匹配");
                }
                orderId = wxRepairs.getRepairsId();
                payAmount = wxRepairs.getMoney();
                orderType = LiveBillType.maintain.getCode();
                break;
            default:
                throw new RuntimeException("支付失败，类型错误");
        }
        WxPayRecord record = createPayRecord(orderId, payAmount, orderType, startTime, endTime);
        IPayment payment = CmsUnipayProvider.getPayment(orderId, "WX", "1717814777", "LILONG", reqModel.getPayBizCode());
        PayModel payModel = getPayModel(record);
        return payment.pay(payModel);
    }

    /**
     * @param orderId
     * @param payChannelCode
     * @param payMchCode
     * @param payAppCode
     * @param type 1报修支付 2预存支付
     */
    @Override
    public void payCallback(String orderId, String payChannelCode, String payMchCode, String payAppCode, String type) {
        /**
         * 更新支付记录状态
         */
        LambdaQueryWrapper<WxPayRecord> recordLambdaQueryWrapper = Wrappers.lambdaQuery();
        recordLambdaQueryWrapper.eq(WxPayRecord::getOrderId, orderId);
        WxPayRecord record = baseMapper.selectOne(recordLambdaQueryWrapper);
        record.setPayState(PayState.YES.getCode());
        record.setPayChannelCode(payChannelCode);
        record.setPayMchCode(payMchCode);
        record.setPayAppCode(payAppCode);
        baseMapper.updateById(record);
        /**
         * 更新订单状态
         */
        if (type.equals(PayBizEnum.MAINTAIN.getCode())) {
            WxRepairs wxRepairs = repairsMapper.selectById(orderId);
            wxRepairs.setState(RepairsState.THREE.getCode());
            wxRepairs.setCompleteTime(new Date());
            repairsMapper.updateById(wxRepairs);
        }else {
            WxLiveBill wxLiveBill = liveBillMapper.selectById(orderId);
            wxLiveBill.setState(LiveBillState.YES.getCode());
            liveBillMapper.updateById(wxLiveBill);

            WxProprietorLedger proprietorLedger = proprietorLedgerMapper.selectById(wxLiveBill.getProprietorId());
            switch (record.getOrderType()){
                case 1:
                    proprietorLedger.setWaterPrepaidMoney(proprietorLedger.getWaterPrepaidMoney().add(record.getPayAmount()));
                    break;
                case 2:
                    proprietorLedger.setElectricityPrepaidMoney(proprietorLedger.getElectricityPrepaidMoney().add(record.getPayAmount()));
                    break;
                case 3:
                    proprietorLedger.setPropertyPrepaidMoney(proprietorLedger.getPropertyPrepaidMoney().add(record.getPayAmount()));
                    break;
                case 4:
                    proprietorLedger.setCarportManagePrestoreMoney(proprietorLedger.getCarportManagePrestoreMoney().add(record.getPayAmount()));
                    break;
                case 5:
                    proprietorLedger.setCarportLeasePrestoreMoney(proprietorLedger.getCarportLeasePrestoreMoney().add(record.getPayAmount()));
                    break;
                case 6:
                    proprietorLedger.setStoreroomLeasePrestoreMoney(proprietorLedger.getStoreroomLeasePrestoreMoney().add(record.getPayAmount()));
                    break;
                case 7:
                    proprietorLedger.setOtherPrestoreMoney(proprietorLedger.getOtherPrestoreMoney().add(record.getPayAmount()));
                    break;
                default:
                    throw new RuntimeException("支付回调异常===========================================>");
            }
            proprietorLedgerMapper.updateById(proprietorLedger);
        }
    }

    private WxPayRecord createPayRecord(Long orderId, BigDecimal payAmount, Integer orderType, Date startTime, Date endTime) {
        LoginWxUser wxUser = AuthUtil.getWxUser();
        WxPayRecord record = new WxPayRecord();
        record.setOrderId(orderId);
        record.setPayAmount(payAmount);
        record.setPayState(PayState.WAIT.getCode());
        record.setUserId(wxUser.getId());
        record.setUserName(wxUser.getNickName());
        record.setProprietorId(wxUser.getProprietorId());
        record.setProprietorName(wxUser.getName());
        record.setRoom(wxUser.getRoom());
        record.setCommunityId(wxUser.getCommunityId());
        record.setCommunityName(wxUser.getCommunityName());
        record.setOrderType(orderType);
        if (null != startTime && null != endTime) {
            record.setStartTime(startTime);
            record.setEndTime(endTime);
        }
        /**
         * 生成外部订单号
         */
        String memStr = String.valueOf(wxUser.getId());
        if (memStr.length() < 4) {
            memStr = String.format("%04d", wxUser.getId());
        } else {
            memStr = memStr.substring(memStr.length() - 4);
        }
        record.setOutTradeNo("OR" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN) + memStr + RandomUtil.randomString("123456789", 4));
        baseMapper.insert(record);
        return record;
    }

    private PayModel getPayModel(WxPayRecord record) {
        LoginWxUser wxUser = AuthUtil.getWxUser();
        PayModel payModel = new PayModel();
        payModel.setPrice(record.getPayAmount());
        payModel.setOpenid(wxUser.getOpenId());
        payModel.setOutTradeNo(record.getOutTradeNo());
        payModel.setBody(record.getOrderType().equals(LiveBillType.maintain.getCode()) ? "维修费用支付" : "预存支付");
        payModel.setSubject(record.getOrderType().equals(LiveBillType.maintain.getCode()) ? "维修费用支付" : "预存支付");
        payModel.setSpbillCreateIp(IpUtils.getIpAddr());
        return payModel;
    }

    private LambdaQueryWrapper<WxPayRecord> buildQueryWrapper(WxPayRecordBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<WxPayRecord> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getCommunityId() != null, WxPayRecord::getCommunityId, bo.getCommunityId());
        lqw.eq(bo.getUserId() != null, WxPayRecord::getUserId, bo.getUserId());
        lqw.like(StringUtils.isNotBlank(bo.getUserName()), WxPayRecord::getUserName, bo.getUserName());
        lqw.like(bo.getCommunityName() != null, WxPayRecord::getCommunityName, bo.getCommunityName());
        lqw.like(StringUtils.isNotBlank(bo.getProprietorName()), WxPayRecord::getProprietorName, bo.getProprietorName());
        lqw.eq(StringUtils.isNotBlank(bo.getRoom()), WxPayRecord::getRoom, bo.getRoom());
        lqw.eq(StringUtils.isNotBlank(bo.getPlateNumber()), WxPayRecord::getPlateNumber, bo.getPlateNumber());
        lqw.eq(bo.getOrderId() != null, WxPayRecord::getOrderId, bo.getOrderId());
        lqw.eq(bo.getOrderType() != null, WxPayRecord::getOrderType, bo.getOrderType());
        lqw.eq(StringUtils.isNotBlank(bo.getPayChannelCode()), WxPayRecord::getPayChannelCode, bo.getPayChannelCode());
        lqw.eq(StringUtils.isNotBlank(bo.getPayMchCode()), WxPayRecord::getPayMchCode, bo.getPayMchCode());
        lqw.eq(StringUtils.isNotBlank(bo.getPayTradeType()), WxPayRecord::getPayTradeType, bo.getPayTradeType());
        lqw.eq(StringUtils.isNotBlank(bo.getPayAppCode()), WxPayRecord::getPayAppCode, bo.getPayAppCode());
        lqw.eq(StringUtils.isNotBlank(bo.getPayBizCode()), WxPayRecord::getPayBizCode, bo.getPayBizCode());
        lqw.eq(bo.getPayAmount() != null, WxPayRecord::getPayAmount, bo.getPayAmount());
        lqw.eq(bo.getPayState() != null, WxPayRecord::getPayState, bo.getPayState());
        lqw.eq(StringUtils.isNotBlank(bo.getOutTradeNo()), WxPayRecord::getOutTradeNo, bo.getOutTradeNo());
        lqw.between(bo.getCreateStartTime() != null, BaseEntity::getCreateTime, bo.getCreateStartTime(), bo.getCreateEndTime());
        lqw.orderByDesc(WxPayRecord::getCreateTime);
        return lqw;
    }

    /**
     * 新增缴费记录
     */
    @Override
    public Boolean insertByBo(WxPayRecordBo bo) {
        WxPayRecord add = BeanUtil.toBean(bo, WxPayRecord.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setRecordId(add.getRecordId());
        }
        return flag;
    }

    /**
     * 修改缴费记录
     */
    @Override
    public Boolean updateByBo(WxPayRecordBo bo) {
        WxPayRecord update = BeanUtil.toBean(bo, WxPayRecord.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

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

    /**
     * 批量删除缴费记录
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }
}
