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

import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.system.dal.dataobject.biz.*;
import cn.iocoder.yudao.module.system.dal.dataobject.ccb.CcbPayCallbackRecordDO;
import cn.iocoder.yudao.module.system.dal.dataobject.ccb.MerchantRecordDO;
import cn.iocoder.yudao.module.system.dal.mysql.biz.*;
import cn.iocoder.yudao.module.system.dal.mysql.ccb.CcbPayCallbackRecordMapper;
import cn.iocoder.yudao.module.system.dal.mysql.ccb.MerchantRecordMapper;
import cn.iocoder.yudao.module.system.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.system.enums.change.BusinessConstants;
import cn.iocoder.yudao.module.system.enums.common.BooleanEnum;
import cn.iocoder.yudao.module.system.util.pay.CcbPayConstant;
import cn.iocoder.yudao.module.system.util.pay.CcbPayUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Map;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;

@Slf4j
@Service
public class CcbBusinessService {

    @Resource
    private ExpenseOrderMapper expenseOrderMapper;

    @Resource
    private ExpenseOrderItemMapper expenseOrderItemMapper;
    @Resource
    private CcbPayCallbackRecordMapper callbackRecordMapper;

    @Resource
    private ExpensePayOrderMapper expensePayOrderMapper;
    @Resource
    private ExpensePayOrderItemMapper expensePayOrderItemMapper;

    @Autowired
    private MerchantRecordMapper merchantRecordMapper;

    @Autowired
    private ElderlyCcbPayOrderRecordMapper elderlyCcbPayOrderRecordMapper;


    public void handleOrderBusiness(Map<String, String> paramMap) throws Exception {
        //1.校验账单是否存在
        String orderId = CcbPayUtils.getMapValue(paramMap, "ORDERID");
        String callbackAmountStr = CcbPayUtils.getMapValue(paramMap, "PAYMENT");
        BigDecimal callbackAmount = new BigDecimal(callbackAmountStr);
        ExpenseOrderDO expenseOrderDO = expenseOrderMapper.selectOne(new LambdaQueryWrapperX<ExpenseOrderDO>()
                .eq(ExpenseOrderDO::getBillOrderNumber, orderId)
                .last("FOR UPDATE"));   //加悲观锁，防止并发操作导致数据不一致的问题
        if (expenseOrderDO == null) {
            String errorMessage = "订单不存在: " + orderId;
            saveCallbackRecord(paramMap, errorMessage);
            throw exception(ErrorCodeConstants.COMMON_NOT_FOUND);
        }
        // 商户信息
        MerchantRecordDO merchantRecordDO = merchantRecordMapper.selectOne(new LambdaQueryWrapperX<MerchantRecordDO>()
                .eq(MerchantRecordDO::getTenantId, expenseOrderDO.getTenantId()));
        // 2.校验商户柜台代码、分行代码（与配置匹配）
        String callbackPosId = CcbPayUtils.getMapValue(paramMap, "POSID");
        String callbackBranchId = CcbPayUtils.getMapValue(paramMap, "BRANCHID");
        if (!merchantRecordDO.getCounterNumber().equals(callbackPosId) || !CcbPayConstant.BRANCHID.equals(callbackBranchId)) {
            String errorMessage = "商户信息不匹配：POSID=" + callbackPosId + "，BRANCHID=" + callbackBranchId;
            saveCallbackRecord(paramMap, errorMessage);
            throw new Exception(errorMessage);
        }
        //3.校验账单是否已经支付
        if (expenseOrderDO.getPayStatus().equals(BooleanEnum.TRUE.getValue())) {
            String errorMessage = "订单已经支付: " + orderId;
            saveCallbackRecord(paramMap, errorMessage);
            throw exception(ErrorCodeConstants.EXPENSE_ORDER_ALREADY_PAY);
        }
        //5.校验支付金额是否跟账单金额一致
        if (expenseOrderDO.getActualAmount().compareTo(callbackAmount) != 0) {
            String errorMessage = "金额不匹配：订单金额=" + expenseOrderDO.getActualAmount() + "，回调金额=" + callbackAmount;
            saveCallbackRecord(paramMap, errorMessage);
            throw new Exception(errorMessage);
        }

        //6.校验回调支付状态
        String success = CcbPayUtils.getMapValue(paramMap, "SUCCESS");
        if ("Y".equals(success)) {
            //确认无误，修改账单状态
            updateExpenseOrderStatus(paramMap, expenseOrderDO, callbackAmount);

            //生成缴费信息
            ExpensePayOrderDO payOrder = createPayOrder(expenseOrderDO, callbackAmount);

            //处理费用项
            processExpenseOrderItems(expenseOrderDO, payOrder);
            // 记录支付记录
            saveCcbPayOrderRecord(expenseOrderDO);

            log.info("订单号{} 业务处理结果={}，支付金额={}", orderId, "成功", callbackAmount);
            //7.保存回调记录
            saveCallbackRecord(paramMap, null);
            //todo 增加幂等性表
        } else {
            log.info("订单号{} 业务处理结果={}，支付金额={}", orderId, "失败", callbackAmount);
            String errorMessage = "支付失败，支付结果：SUCCESS=" + success;
            saveCallbackRecord(paramMap, errorMessage);
        }
    }

    private void saveCallbackRecord(Map<String, String> paramMap, String errorMessage) {
        CcbPayCallbackRecordDO record = new CcbPayCallbackRecordDO();
        record.setOrderId(CcbPayUtils.getMapValue(paramMap, "ORDERID"));
        record.setPosId(CcbPayUtils.getMapValue(paramMap, "POSID"));
        record.setBranchId(CcbPayUtils.getMapValue(paramMap, "BRANCHID"));
        // 金额转换为BigDecimal，避免精度问题
        record.setPayment(new BigDecimal(CcbPayUtils.getMapValue(paramMap, "PAYMENT")));
        record.setCurCode(CcbPayUtils.getMapValue(paramMap, "CURCODE"));
        record.setSuccess(CcbPayUtils.getMapValue(paramMap, "SUCCESS"));
        record.setAccType(CcbPayUtils.getMapValue(paramMap, "ACC_TYPE"));
        record.setAccDate(CcbPayUtils.getMapValue(paramMap, "ACCDATE"));
        record.setClientIp(CcbPayUtils.getMapValue(paramMap, "CLIENTIP"));
        record.setReferer(CcbPayUtils.getMapValue(paramMap, "REFERER"));
        record.setRemark1(CcbPayUtils.getMapValue(paramMap, "REMARK1"));
        record.setRemark2(CcbPayUtils.getMapValue(paramMap, "REMARK2"));
        record.setType(CcbPayUtils.getMapValue(paramMap, "TYPE"));
        record.setSign(CcbPayUtils.getMapValue(paramMap, "SIGN"));
        record.setCallbackTime(LocalDateTime.now()); // 系统接收回调时间
        record.setErrorMessage(errorMessage);
        // 插入数据库（若订单已存在则更新，避免重复）
        callbackRecordMapper.insertOrUpdate(record);
    }

    private void saveCcbPayOrderRecord(ExpenseOrderDO expenseOrderDO) {
        ElderlyCcbPayOrderRecordDO record = new ElderlyCcbPayOrderRecordDO();
        record.setExpenseOrderId(expenseOrderDO.getId());
        record.setPayTime(expenseOrderDO.getPayTime());
        record.setPayAmount(expenseOrderDO.getPayableAmount());
        record.setBillingMonth(expenseOrderDO.getBillingMonth());
        record.setTenantId(expenseOrderDO.getTenantId());
        elderlyCcbPayOrderRecordMapper.insert(record);
    }

    private void updateExpenseOrderStatus(Map<String, String> paramMap, ExpenseOrderDO expenseOrderDO, BigDecimal callbackAmount) {
        expenseOrderDO.setPayStatus(1);
        expenseOrderDO.setPayTime(new Date());
        expenseOrderDO.setPayeeName("");  //暂时无法获取
        String accType = CcbPayUtils.getMapValue(paramMap, "ACC_TYPE");
        if ("WX".equals(accType)) {
            expenseOrderDO.setPayType("8");  //微信支付
        }
        expenseOrderDO.setPayableAmount(callbackAmount);
        expenseOrderMapper.updateById(expenseOrderDO);
    }

    private ExpensePayOrderDO createPayOrder(ExpenseOrderDO expenseOrderDO, BigDecimal callbackAmount) {
        ExpensePayOrderDO payOrder = new ExpensePayOrderDO();
        payOrder.setPayAmount(callbackAmount);
        payOrder.setRoundPayAmount(callbackAmount.setScale(0, RoundingMode.HALF_UP));
        payOrder.setRoundPayTwoDecimalAmount(callbackAmount.setScale(2, RoundingMode.HALF_UP));
        payOrder.setExpenseOrderId(expenseOrderDO.getId());
        payOrder.setCreatedTime(new Date());
        payOrder.setType(BusinessConstants.EXPENSE_PAY_ORDER_MONTH);
        payOrder.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
        payOrder.setTenantId(expenseOrderDO.getTenantId());
        payOrder.setRemarks("亲属微信支付缴费");
        expensePayOrderMapper.insert(payOrder);
        return payOrder;
    }

    private void processExpenseOrderItems(ExpenseOrderDO expenseOrderDO, ExpensePayOrderDO payOrder) {
        List<ExpenseOrderItemDO> items = expenseOrderItemMapper.selectList(new LambdaQueryWrapperX<ExpenseOrderItemDO>()
                .eq(ExpenseOrderItemDO::getExpenseOrderId, expenseOrderDO.getId()));

        for (ExpenseOrderItemDO item : items) {
            item.setPayStatus(1);
            item.setPayTime(new Date());
            item.setPayOrderId(payOrder.getId());
            expenseOrderItemMapper.updateById(item);

            ExpensePayOrderItemDO payOrderItem = new ExpensePayOrderItemDO();
            payOrderItem.setPayType(String.valueOf(8));
            payOrderItem.setPayOrderId(payOrder.getId());
            payOrderItem.setPayAmount(item.getTotalAmount());
            payOrderItem.setRoundPayAmount(item.getTotalAmount().setScale(0, RoundingMode.HALF_UP));
            payOrderItem.setRoundPayTwoDecimalAmount(item.getTotalAmount().setScale(2, RoundingMode.HALF_UP));
            payOrderItem.setCreatedTime(new Date());
            payOrderItem.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
            payOrderItem.setTenantId(expenseOrderDO.getTenantId());
            expensePayOrderItemMapper.insert(payOrderItem);
        }
    }
}
