package com.yc.ship.module.trade.service.refund.impl;



import cn.hutool.core.map.MapUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.yc.ship.framework.common.lang.Asserts;
import com.yc.ship.framework.tenant.core.aop.TenantIgnore;
import com.yc.ship.module.trade.dal.dataobject.order.*;
import com.yc.ship.module.trade.dal.dataobject.refund.*;
import com.yc.ship.module.trade.dal.mysql.order.*;
import com.yc.ship.module.trade.dal.mysql.refund.*;
import com.yc.ship.module.trade.dal.mysql.supplier.SupplierRefundMapper;
import com.yc.ship.module.trade.enums.RefundStatusEnum;
import com.yc.ship.module.trade.enums.TradeOrderStatusEnum;
import com.yc.ship.module.trade.enums.VoucherStatusEnum;
import com.yc.ship.module.trade.service.order.bo.TradeOrderDetailBaseBO;
import com.yc.ship.module.trade.service.refund.TradeRefundRepositoryService;
import com.yc.ship.module.trade.service.refund.bo.GuideRefundBO;
import com.yc.ship.module.trade.service.refund.bo.TradeRefundBO;
import com.yc.ship.module.trade.service.refund.bo.TradeRefundDetailBO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.yc.ship.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yc.ship.module.trade.enums.ApiConstants.*;
import static com.yc.ship.module.trade.enums.ErrorCodeConstants.REFUND_SAVE_EXCEPTION;

/**
 * &#064;Description ：退款数据服务
 * &#064;Author ：qsl
 * &#064;Date ：2025/7/22 16:18
 */
@Service
@Slf4j
public class TradeRefundRepositoryServiceImpl implements TradeRefundRepositoryService {

    @Resource
    private RefundMapper refundMapper;
    @Resource
    private RefundDetailMapper refundDetailMapper;
    @Resource
    private RefundDetailBaseMapper refundDetailBaseMapper;
    @Resource
    private TradeDetailMapper tradeDetailMapper;
    @Resource
    private TradeDetailBaseMapper tradeDetailBaseMapper;
    @Resource
    private TradeOrderMapper tradeOrderMapper;
    @Resource
    private RefundAuditMapper refundAuditMapper;
    @Resource
    private RefundRecordMapper refundRecordMapper;
    @Resource
    private TradeOrderPayMapper tradeOrderPayMapper;
    @Resource
    private TradeVisitorMapper tradeVisitorMapper;
    @Resource
    private RefundAmountApplyMapper refundAmountApplyMapper;
    @Resource
    private TradeDetailBizMapper tradeDetailBizMapper;
    @Resource
    private SupplierRefundMapper supplierRefundMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveRefund(TradeRefundBO tradeRefundBO) {
        try {

            refundMapper.insert(tradeRefundBO.getRefundDO());
            List<RefundDetailDO> refundDetailDOList = new ArrayList<>();
            List<RefundDetailBaseDO> refundDetailBaseDOList = new ArrayList<>();
            tradeRefundBO.getTradeRefundDetailBOList().forEach(tradeRefundDetailBO -> {
                refundDetailDOList.add(tradeRefundDetailBO.getRefundDetailDO());
                refundDetailBaseDOList.addAll(tradeRefundDetailBO.getRefundDetailBaseDOList());
            });
            refundDetailMapper.insertBatch(refundDetailDOList);
            refundDetailBaseMapper.insertBatch(refundDetailBaseDOList);

        }catch (Exception e){
            log.error("退单保存异常",e);
            throw exception(REFUND_SAVE_EXCEPTION);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateOrderRefunding(TradeRefundBO tradeRefundBO) {
        List<TradeDetailDO> tradeDetailDOList = new ArrayList<>();
        List<TradeDetailBaseDO> tradeDetailBaseDOList = new ArrayList<>();
        List<TradeRefundDetailBO> tradeRefundDetailBOList = tradeRefundBO.getTradeRefundDetailBOList();
        tradeRefundDetailBOList.stream().forEach(tradeRefundDetailBO -> {
            TradeDetailDO tradeDetailDO = new TradeDetailDO();
            tradeDetailDO.setId(tradeRefundDetailBO.getRefundDetailDO().getTradeDetailId());
            tradeDetailDO.setVoucherStatus(tradeRefundDetailBO.getRefundDetailDO().getVoucherStatus());
            tradeDetailDOList.add(tradeDetailDO);
            tradeRefundDetailBO.getRefundDetailBaseDOList().forEach(refundDetailBaseDO -> {
                TradeDetailBaseDO tradeDetailBaseDO = new TradeDetailBaseDO();
                tradeDetailBaseDO.setId(refundDetailBaseDO.getTradeDetailBaseId());
                tradeDetailBaseDO.setUseStatus(refundDetailBaseDO.getUseStatus());
                tradeDetailBaseDOList.add(tradeDetailBaseDO);
            });
        });

        tradeDetailDOList.forEach(tradeDetailDO -> {
            LambdaUpdateWrapper<TradeDetailDO> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(TradeDetailDO::getVoucherStatus, VoucherStatusEnum.REFUNDING.getStatus());
            updateWrapper.eq(TradeDetailDO::getId,tradeDetailDO.getId());
            updateWrapper.eq(TradeDetailDO::getVoucherStatus,tradeDetailDO.getVoucherStatus());
            //用乐观锁更新，如果状态发生变更，将不更新
            int updateCount = tradeDetailMapper.update(updateWrapper);
            Asserts.isTrue(updateCount == 1,"订单明细状态发生变更，退款失败，请刷新后再提交");
        });
        tradeDetailBaseDOList.forEach(tradeDetailBaseDO -> {
            LambdaUpdateWrapper<TradeDetailBaseDO> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(TradeDetailBaseDO::getUseStatus,VoucherStatusEnum.REFUNDING.getStatus());
            updateWrapper.eq(TradeDetailBaseDO::getId,tradeDetailBaseDO.getId());
            updateWrapper.eq(TradeDetailBaseDO::getUseStatus,tradeDetailBaseDO.getUseStatus());
            //用乐观锁更新，如果状态发生变更，将不更新
            int updateBaseCount = tradeDetailBaseMapper.update(updateWrapper);
            Asserts.isTrue(updateBaseCount == 1,"订单明细基础状态发生变更，退款失败，请刷新后再提交");
        });

        TradeOrderDO tradeOrderDO = new TradeOrderDO();
        tradeOrderDO.setId(tradeRefundBO.getRefundDO().getOrderId());
        tradeOrderDO.setOrderStatus(TradeOrderStatusEnum.REFUNDING.getStatus());
        tradeOrderMapper.updateById(tradeOrderDO);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateRefundStatus(Long refundId, RefundStatusEnum refundStatusEnum) {
        RefundDO refundDO = new RefundDO();
        refundDO.setRefundStatus(refundStatusEnum.getStatus());
        if(refundStatusEnum == RefundStatusEnum.REFUND){
            refundDO.setRefundTime(LocalDateTime.now());
        }
        refundDO.setId(refundId);
        refundMapper.updateById(refundDO);

        updateRefundDetailStatus(refundId,refundStatusEnum);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateRefundStatusAndAmount(Long refundId,BigDecimal refundAmount,RefundStatusEnum refundStatusEnum) {
        RefundDO refundDO = new RefundDO();
        refundDO.setRefundStatus(refundStatusEnum.getStatus());
        refundDO.setRefundAmount(refundAmount);
        if(refundStatusEnum == RefundStatusEnum.REFUND){
            refundDO.setRefundTime(LocalDateTime.now());
        }
        refundDO.setId(refundId);
        refundMapper.updateById(refundDO);

        updateRefundDetailStatus(refundId,refundStatusEnum);

    }
    @Transactional(rollbackFor = Exception.class)
    public void updateRefundDetailStatus(Long refundId, RefundStatusEnum refundStatusEnum){
        LambdaUpdateWrapper<RefundDetailDO> detailUpdateWrapper = new LambdaUpdateWrapper<>();
        detailUpdateWrapper.set(RefundDetailDO::getRefundStatus,refundStatusEnum.getStatus());
        detailUpdateWrapper.eq(RefundDetailDO::getRefundId,refundId);
        if(refundStatusEnum == RefundStatusEnum.REFUND){
            detailUpdateWrapper.set(RefundDetailDO::getRefundTime,LocalDateTime.now());
        }
        refundDetailMapper.update(detailUpdateWrapper);

        LambdaUpdateWrapper<RefundDetailBaseDO> detailBaseUpdateWrapper = new LambdaUpdateWrapper<>();
        detailBaseUpdateWrapper.set(RefundDetailBaseDO::getRefundStatus,refundStatusEnum.getStatus());
        detailBaseUpdateWrapper.eq(RefundDetailBaseDO::getRefundId,refundId);
        refundDetailBaseMapper.update(detailBaseUpdateWrapper);
    }

    @Override
    public RefundDO getById(Long refundId) {
        return refundMapper.selectById(refundId);
    }

    @Override
    public void saveRefundAudit(RefundAuditDO refundAuditDO) {
        refundAuditMapper.insert(refundAuditDO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void recoveryStatus(RefundDO refundDO) {
        TradeOrderDO tradeOrderDO = new TradeOrderDO();
        tradeOrderDO.setOrderStatus(refundDO.getOrderStatus());
        tradeOrderDO.setId(refundDO.getOrderId());
        tradeOrderMapper.updateById(tradeOrderDO);

        refundDetailMapper.recoveryStatusByRefundId(refundDO.getId());
        refundDetailBaseMapper.recoveryStatusByRefundId(refundDO.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveRefundRecordAndUpdatePayOrder(List<RefundRecordDO> recordDOList, List<TradeOrderPayDO> updatePayList) {
        refundRecordMapper.insertBatch(recordDOList);
        List<TradeOrderPayDO> payList = updatePayList.stream().map(tradeOrderPayDO -> {
            TradeOrderPayDO updatePayDO = new TradeOrderPayDO();
            updatePayDO.setId(tradeOrderPayDO.getId());
            updatePayDO.setRefundAmount(tradeOrderPayDO.getRefundAmount());
            return updatePayDO;
        }).collect(Collectors.toList());
        tradeOrderPayMapper.updateBatch(payList);
    }

    @Override
    public List<RefundDetailDO> getRefundDetailList(Long refundId) {
        return refundDetailMapper.selectList(RefundDetailDO::getRefundId,refundId);
    }

    @Override
    public List<RefundDetailBaseDO> getRefundDetailBaseList(Long refundDetailId) {
        return refundDetailBaseMapper.selectList(RefundDetailBaseDO::getRefundDetailId,refundDetailId);
    }

    @Override
    @TenantIgnore
    public void updateRefundRecord(RefundRecordDO refundRecordDO) {
        refundRecordMapper.updateById(refundRecordDO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateOrderDetailStatusByRefundId(Long refundId,VoucherStatusEnum refundStatus) {
        refundDetailMapper.updateStatusByRefundId(refundId,refundStatus.getStatus());
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateOrderDetailBaseStatusByRefundId(Long refundId, VoucherStatusEnum refundStatus) {
        refundDetailBaseMapper.updateStatusByRefundId(refundId,refundStatus.getStatus());
    }

    @Override
    public BigDecimal getRefundRecordAmount(Long refundId){
        Map<String, Object> dataMap = refundRecordMapper.selectRefundRecordTotalAmount(refundId);
        return MapUtil.get(dataMap,"totalRefundAmount",BigDecimal.class,BigDecimal.ZERO);
    }

    @Override
    public RefundRecordDO getRecordById(Long recordId) {
        return refundRecordMapper.selectById(recordId);
    }

    @Override
    public Map<String, Long> getRefundRecordFailCount(Long refundId) {
        return refundRecordMapper.selectRefundRecordFailCount(refundId);
    }

    @Override
    public void saveRefundRecord(RefundRecordDO refundRecordDO) {
        refundRecordMapper.insert(refundRecordDO);
    }


    @Override
    public RefundDO getRefundByOatNoAndOrder(String otaRefundNo, Long orderId) {
        return refundMapper.selectOne(RefundDO::getOtaRefundNo, otaRefundNo, RefundDO::getOrderId, orderId);
    }

    @Override
    @TenantIgnore
    public RefundRecordDO getRecordByIdIgnoreTenant(Long recordId) {
        return refundRecordMapper.selectById(recordId);
    }

    @Override
    public void updateRefundSupplierStatus(Long refundId, RefundStatusEnum refundSupplierStatusEnum) {
        RefundDO refundDO = new RefundDO();
        refundDO.setSupplierRefundStatus(refundSupplierStatusEnum.getStatus());
        refundDO.setId(refundId);
        refundMapper.updateById(refundDO);
    }


    @Override
    public RefundAuditDO getRefundAuditById(Long auditId) {
        return refundAuditMapper.selectById(auditId);
    }

    @Override
    public List<Map<String, Object>> getRefundOrderProduct(Long refundId) {
        return refundDetailMapper.selectRefundOrderProduct(refundId);
    }

    @Override
    public List<Map<String, Object>> getRefundOrderProductV2(Long refundId, List<String> noContainSpecList) {
        return refundDetailMapper.selectRefundOrderProductV2(refundId,noContainSpecList);
    }

    @Override
    public BigDecimal getOrderRefundAmount(Long orderId) {
        Map<String,Object> data = refundDetailMapper.selectOrderRefundAmount(orderId);
        return MapUtil.get(data,"totalRefundAmount",BigDecimal.class);
    }

    @Override
    public BigDecimal getOrderRefundFee(Long orderId) {
        Map<String,Object> data = refundDetailMapper.selectOrderRefundFee(orderId);
        return MapUtil.get(data,"totalFee",BigDecimal.class);
    }

    @Override
    public List<TradeOrderDetailBaseBO> getProductBaseByRefund(Long refundId) {
        return refundDetailMapper.selectProductBaseByRefund(refundId);
    }

    @Override
    public List<TradeOrderDetailBaseBO> getProjectCountByRefund(Long refundId,Long orderId) {
        return refundDetailMapper.selectProjectCountByRefund(refundId,orderId);
    }

    @Override
    public void updateRefundStatusById(Long id, Integer refundStatus) {
        LambdaUpdateWrapper<RefundDO> updateWrapper = new LambdaUpdateWrapper<>();
        if (!Objects.equals(RefundStatusEnum.REFUND.getStatus(), refundStatus)){
            updateWrapper.set(RefundDO::getRefundStatus,RefundStatusEnum.REFUND.getStatus());
            updateWrapper.eq(RefundDO::getId,id);
            refundMapper.update(updateWrapper);
        }
    }

    @Override
    public BigDecimal getOrderApplyRefundAmount(Long orderId) {
        Map<String,Object> data = refundDetailMapper.selectOrderApplyRefundAmount(orderId);
        return MapUtil.get(data,"totalRefundAmount",BigDecimal.class,BigDecimal.ZERO);
    }

    @Override
    public BigDecimal getMarketRefundAmount(Long orderId) {
        return refundRecordMapper.selectMarketRefundAmount(orderId,null);
    }

    @Override
    public RefundDO getChangeOrderRefund(Long orderId) {
        LambdaQueryWrapper<RefundDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RefundDO::getOrderId,orderId);
        queryWrapper.eq(RefundDO::getRefundMethod,REFUND_METHOD_CHANGE_ORDER);
        queryWrapper.eq(RefundDO::getRefundStatus,RefundStatusEnum.WAIT_REFUND.getStatus());
        return refundMapper.selectOne(queryWrapper);
    }

    @Override
    public List<TradeVisitorDO> getBoatVisitorListByRefund(Long refundId, Date useDate) {
        return tradeVisitorMapper.selectBoatVisitorListByRefund(refundId,useDate);
    }

    @Override
    public BigDecimal getMarketRefundAmount(Long orderId, Long changeOrderId) {
        return refundRecordMapper.selectMarketRefundAmount(orderId,changeOrderId);
    }

    @Override
    public List<Map<String,Object>> getTradeDetailStatusByRefund(Long refundId) {
       return refundDetailMapper.selectTradeDetailStatusByRefund(refundId);
    }

    @Override
    public BigDecimal getOrderOriginRefundAmount(Long orderId) {
        Map<String,Object> data = refundDetailMapper.selectOrderOriginRefundAmount(orderId);
        return MapUtil.get(data,"totalOriginRefundAmount",BigDecimal.class);
    }

    @Override
    public void saveRefundAmountApply(RefundAmountApplyDO refundAmountApplyDO){
        refundAmountApplyMapper.insert(refundAmountApplyDO);
    }

    @Override
    public void updateRefundAmountApply(Long id, RefundStatusEnum refundStatusEnum){
        LambdaUpdateWrapper<RefundAmountApplyDO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(RefundAmountApplyDO::getId,id);
        updateWrapper.set(RefundAmountApplyDO::getRefundStatus,refundStatusEnum.getStatus());
        refundAmountApplyMapper.update(updateWrapper);
    }

    @Override
    public RefundAmountApplyDO getRefundAmountApply(Long id){
       return refundAmountApplyMapper.selectById(id);
    }

    @Override
    public List<RefundRecordDO> getMarketRefundRecordList(Long orderId, Long changeOrderId){
        return refundRecordMapper.selectMarketRefundRecordList(orderId,changeOrderId);
    }

    @Override
    public List<RefundRecordDO> getRefundRecordList(Long refundId){
        LambdaQueryWrapper<RefundRecordDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RefundRecordDO::getRefundId,refundId);
        queryWrapper.eq(RefundRecordDO::getRefundStatus,RefundStatusEnum.REFUND.getStatus());
        return refundRecordMapper.selectList(queryWrapper);
    }

    @Override
    public BigDecimal getRefundAmountByOriginOrderNo(String originOrderNo) {
        return BigDecimal.ZERO;
    }

    @Override
    public List<RefundDO> getByOrderId(Long orderId){
        LambdaQueryWrapper<RefundDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RefundDO::getOrderId,orderId);
        queryWrapper.eq(RefundDO::getRefundStatus,RefundStatusEnum.REFUND.getStatus());
        return refundMapper.selectList(queryWrapper);
    }

    @Override
    public List<RefundDO> getAllByOrderId(Long orderId){
        LambdaQueryWrapper<RefundDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RefundDO::getOrderId,orderId);
        return refundMapper.selectList(queryWrapper);
    }

    @Override
    public BigDecimal getRefundAmountFromOrderDetail(Long orderId) {
        Map<String,Object> data = refundDetailMapper.selectRefundAmountFromOrderDetail(orderId);
        return MapUtil.get(data,"totalRefundAmount",BigDecimal.class,BigDecimal.ZERO);
    }

    @Override
    public Map<String, Long> getSupplierRefundCountBySupplierOrderId(Long supplierOrderId) {
        return supplierRefundMapper.selectSupplierRefundCountBySupplierOrderId(supplierOrderId);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void justUpdateRefundStatus(Long refundId, RefundStatusEnum refundStatusEnum) {
        RefundDO refundDO = new RefundDO();
        refundDO.setRefundStatus(refundStatusEnum.getStatus());
        refundDO.setId(refundId);
        refundMapper.updateById(refundDO);
    }

    @Override
    public TradeOrderDO getOrderByGuideRefundId(Long refundId){
        return tradeDetailBizMapper.selectTradeOrderByGuideRefund(refundId.toString());
    }


    @Override
    public boolean hasGuideRefundInnerOrder(String orderNo, List<Long> detailIdList){
        Integer count = tradeDetailBizMapper.selectInnerGuideRefundCount(orderNo,detailIdList);
        return count > 0;
    }

    @Override
    public boolean hasGuideRefundOrder(Long orderId, List<Long> detailIdList){
        Integer count = tradeDetailBizMapper.selectGuideRefundCount(orderId,detailIdList);
        return count > 0;
    }

    @Override
    public Map<String, Long> getSupplierRefundCount(Long refundId) {
        return supplierRefundMapper.selectSupplierRefundCount(refundId);
    }
}
