package com.gxwebsoft.glt.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.common.core.enums.ShopDealerCapitalUpdateEnum;
import com.gxwebsoft.common.core.enums.ShopDealerTypeEnum;
import com.gxwebsoft.common.core.exception.BusinessException;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.common.system.mapper.UserMapper;
import com.gxwebsoft.common.system.redis.OrderNoUtils;
import com.gxwebsoft.glt.entity.GltTicketOrder;
import com.gxwebsoft.glt.entity.GltUserTicket;
import com.gxwebsoft.glt.entity.GltUserTicketLog;
import com.gxwebsoft.glt.mapper.GltTicketOrderMapper;
import com.gxwebsoft.glt.mapper.GltUserTicketMapper;
import com.gxwebsoft.glt.param.GltTicketOrderParam;
import com.gxwebsoft.glt.service.GltTicketOrderService;
import com.gxwebsoft.glt.service.GltUserTicketLogService;
import com.gxwebsoft.glt.service.GltUserTicketService;
import com.gxwebsoft.shop.dto.ShopDealerUserReduceDto;
import com.gxwebsoft.shop.entity.*;
import com.gxwebsoft.shop.mapper.ShopUserAddressMapper;
import com.gxwebsoft.shop.service.ShopDealerCapitalService;
import com.gxwebsoft.shop.service.ShopDealerUserService;
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
import com.gxwebsoft.shop.service.ShopOrderService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 送水订单Service实现
 *
 * @author 科技小王子
 * @since 2026-02-05 18:50:20
 */
@Slf4j
@Service
public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper, GltTicketOrder> implements GltTicketOrderService {

    public static final int CHANGE_TYPE_WRITE_OFF = 20;
    private static final BigDecimal RIDER_UNIT_COMMISSION = new BigDecimal("0.10");
    private static final int RIDER_COMMISSION_SCALE = 2;
    private static final int TENANT_ID_10584 = 10584;
    private static final DateTimeFormatter SEND_TIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Resource
    private GltUserTicketMapper gltUserTicketMapper;

    @Resource
    private GltUserTicketService gltUserTicketService;

    @Resource
    private GltUserTicketLogService gltUserTicketLogService;

    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    private ShopDealerUserService shopDealerUserService;

    @Resource
    private ShopDealerCapitalService shopDealerCapitalService;

    @Resource
    private UserMapper userMapper;

    @Resource
    private ShopOrderService shopOrderService;

    @Resource
    private ShopOrderGoodsService shopOrderGoodsService;

    @Resource
    private ShopUserAddressMapper shopUserAddressMapper;

    @Resource
    private OrderNoUtils orderNoUtils;

    @Override
    public PageResult<GltTicketOrder> pageRel(GltTicketOrderParam param) {
        PageParam<GltTicketOrder, GltTicketOrderParam> page = new PageParam<>(param);
        page.setDefaultOrder("sort_number asc, create_time desc");
        List<GltTicketOrder> list = baseMapper.selectPageRel(page, param);
        if(CollectionUtils.isNotEmpty(list)){
            List<Integer> addressIdList = list.stream().map(GltTicketOrder::getAddressId).distinct().collect(Collectors.toList());
            List<ShopUserAddress> userAddressList = shopUserAddressMapper.selectBatchIds(addressIdList);
            if(CollectionUtils.isNotEmpty(userAddressList)){
                list.forEach(ticketOrder ->{
                    ShopUserAddress shopUserAddress = userAddressList.stream().filter(address -> ticketOrder.getAddressId().equals(address.getId())).findFirst().orElse(null);
                    if(shopUserAddress != null){
                        ticketOrder.setFullAddress(shopUserAddress.getFullAddress());
                    }
                });
            }
        }
        return new PageResult<>(list, page.getTotal());
    }

    @Override
    public List<GltTicketOrder> listRel(GltTicketOrderParam param) {
        List<GltTicketOrder> list = baseMapper.selectListRel(param);
        // 排序
        PageParam<GltTicketOrder, GltTicketOrderParam> page = new PageParam<>();
        page.setDefaultOrder("sort_number asc, create_time desc");
        return page.sortRecords(list);
    }

    @Override
    public GltTicketOrder getByIdRel(Integer id) {
        GltTicketOrderParam param = new GltTicketOrderParam();
        param.setId(id);
        return param.getOne(baseMapper.selectListRel(param));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public GltTicketOrder createWithWriteOff(GltTicketOrder gltTicketOrder, Integer userId, Integer tenantId) {
        if (gltTicketOrder == null) {
            throw new BusinessException("订单参数不能为空");
        }
        if (userId == null) {
            throw new BusinessException("请先登录");
        }

        String no = orderNoUtils.generate("S");
        gltTicketOrder.setNo(no);

        Integer userTicketId = gltTicketOrder.getUserTicketId();
        if (userTicketId == null) {
            throw new BusinessException("userTicketId不能为空");
        }
        int totalNum = gltTicketOrder.getTotalNum() == null ? 0 : gltTicketOrder.getTotalNum();
        if (totalNum <= 0) {
            throw new BusinessException("totalNum必须大于0");
        }
        if (tenantId == null) {
            throw new BusinessException("租户信息缺失");
        }

        // 1) 校验水票归属当前用户 + 正常状态，并锁定记录，避免并发扣减导致日志不准确
        GltUserTicket userTicket = gltUserTicketMapper.selectByIdForUpdate(userTicketId, userId, tenantId);
        if (userTicket == null) {
            throw new BusinessException("水票不存在、已失效或无权限");
        }
        int availableQty = userTicket.getAvailableQty() == null ? 0 : userTicket.getAvailableQty();
        int usedQty = userTicket.getUsedQty() == null ? 0 : userTicket.getUsedQty();
        if (availableQty < totalNum) {
            throw new BusinessException("水票数量不足");
        }

        // 2) 更新 glt_user_ticket: availableQty -= totalNum, usedQty += totalNum
        LocalDateTime now = LocalDateTime.now();
        int availableAfter = availableQty - totalNum;
        int usedAfter = usedQty + totalNum;
        userTicket.setAvailableQty(availableAfter);
        userTicket.setUsedQty(usedAfter);
        userTicket.setUpdateTime(now);
        if (!gltUserTicketService.updateById(userTicket)) {
            throw new BusinessException("扣减水票失败");
        }

        // 4) 插入 glt_ticket_order（storeId/addressId/totalNum/buyerRemarks…）
        gltTicketOrder.setUserId(userId);
        // 订单基础字段由后端兜底，避免前端误传/恶意传参
        gltTicketOrder.setStatus(0);
        gltTicketOrder.setDeleted(0);
        gltTicketOrder.setTenantId(tenantId);
        // 关联商城订单号（用于后台/对账/追踪）；优先取水票上的 orderNo，缺失则按 orderId/orderGoodsId 兜底反查。
        gltTicketOrder.setOrderNo(resolveShopOrderNo(userTicket, tenantId));
        if (gltTicketOrder.getDeliveryStatus() == null) {
            gltTicketOrder.setDeliveryStatus(DELIVERY_STATUS_WAITING);
        }
        if (gltTicketOrder.getSortNumber() == null) {
            gltTicketOrder.setSortNumber(0);
        }
        if (gltTicketOrder.getCreateTime() == null) {
            gltTicketOrder.setCreateTime(now);
        }
        // “立刻送水”下单场景不再需要前端选择配送时间；若未传则默认当前时间，便于排序与派单。
        if (!StringUtils.hasText(gltTicketOrder.getSendTime())) {
            gltTicketOrder.setSendTime(now.format(SEND_TIME_FMT));
        }
        gltTicketOrder.setUpdateTime(now);
        if (!this.save(gltTicketOrder)) {
            throw new BusinessException("创建订单失败");
        }

        // 3) 插入 glt_user_ticket_log（核销记录）
        GltUserTicketLog log = new GltUserTicketLog();
        log.setUserTicketId(userTicketId);
        log.setChangeType(CHANGE_TYPE_WRITE_OFF);
        log.setChangeAvailable(-totalNum);
        log.setChangeFrozen(0);
        log.setChangeUsed(totalNum);
        log.setAvailableAfter(availableAfter);
        log.setFrozenAfter(userTicket.getFrozenQty() == null ? 0 : userTicket.getFrozenQty());
        log.setUsedAfter(usedAfter);
        log.setOrderId(gltTicketOrder.getId());
        log.setOrderNo(gltTicketOrder.getId() == null ? null : String.valueOf(gltTicketOrder.getId()));
        log.setUserId(userId);
        log.setSortNumber(0);
        String comments = gltTicketOrder.getComments();
        if (StrUtil.isBlank(comments)) {
            comments = gltTicketOrder.getBuyerRemarks();
        }
        if (StrUtil.isBlank(comments)) {
            comments = "水票下单核销";
        }
        log.setComments(comments);
        log.setStatus(0);
        log.setDeleted(0);
        log.setTenantId(tenantId);
        log.setCreateTime(now);
        log.setUpdateTime(now);
        if (!gltUserTicketLogService.save(log)) {
            throw new BusinessException("写入核销记录失败");
        }

        return gltTicketOrder;
    }

    private String resolveShopOrderNo(GltUserTicket userTicket, Integer tenantId) {
        if (userTicket == null || tenantId == null) {
            return null;
        }
        if (StringUtils.hasText(userTicket.getOrderNo())) {
            return userTicket.getOrderNo();
        }

        Integer orderId = userTicket.getOrderId();
        // 兜底：历史数据可能只写了 orderGoodsId，未写 orderId/orderNo
        if (orderId == null && userTicket.getOrderGoodsId() != null) {
            ShopOrderGoods og = shopOrderGoodsService.getOne(
                new LambdaQueryWrapper<ShopOrderGoods>()
                    .select(ShopOrderGoods::getOrderId)
                    .eq(ShopOrderGoods::getTenantId, tenantId)
                    .eq(ShopOrderGoods::getId, userTicket.getOrderGoodsId())
                    .last("limit 1")
            );
            if (og != null) {
                orderId = og.getOrderId();
            }
        }
        if (orderId == null) {
            return null;
        }

        ShopOrder order = shopOrderService.getOne(
            new LambdaQueryWrapper<ShopOrder>()
                .select(ShopOrder::getOrderNo)
                .eq(ShopOrder::getTenantId, tenantId)
                .eq(ShopOrder::getDeleted, 0)
                .eq(ShopOrder::getOrderId, orderId)
                .last("limit 1")
        );
        return order == null ? null : order.getOrderNo();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void accept(Integer id, Integer riderId, Integer tenantId) {
        if (id == null) {
            throw new BusinessException("订单id不能为空");
        }
        if (riderId == null) {
            throw new BusinessException("配送员信息缺失");
        }
        if (tenantId == null) {
            throw new BusinessException("租户信息缺失");
        }

        // 原子接单：避免并发抢单
        LocalDateTime now = LocalDateTime.now();
        boolean ok = this.lambdaUpdate()
                .set(GltTicketOrder::getRiderId, riderId)
                .set(GltTicketOrder::getUpdateTime, now)
                .eq(GltTicketOrder::getId, id)
                .eq(GltTicketOrder::getTenantId, tenantId)
                .eq(GltTicketOrder::getDeleted, 0)
                .and(w -> w.eq(GltTicketOrder::getDeliveryStatus, DELIVERY_STATUS_WAITING)
                        .or().isNull(GltTicketOrder::getDeliveryStatus))
                .and(w -> w.isNull(GltTicketOrder::getRiderId).or().eq(GltTicketOrder::getRiderId, 0))
                .update();
        if (ok) {
            // 接单成功后，同步商城订单发货状态：10未发货 -> 20已发货
            updateShopOrderDeliveryStatusAfterAccept(id, tenantId, riderId, now);
            return;
        }

        // 回查给出更明确的错误
        GltTicketOrder order = this.lambdaQuery()
                .eq(GltTicketOrder::getId, id)
                .eq(GltTicketOrder::getTenantId, tenantId)
                .eq(GltTicketOrder::getDeleted, 0)
                .one();
        if (order == null) {
            throw new BusinessException("订单不存在");
        }
        if (order.getRiderId() != null && order.getRiderId() > 0) {
            throw new BusinessException("订单已被其他配送员接单");
        }
        throw new BusinessException("订单状态不允许接单");
    }

    @Override
    public void markShopOrderShippedAfterRiderAssigned(Integer ticketOrderId, Integer tenantId, Integer riderId) {
        updateShopOrderDeliveryStatusAfterAccept(ticketOrderId, tenantId, riderId, LocalDateTime.now());
    }

    @Override
    public void markShopOrderCompletedAfterTicketFinished(Integer ticketOrderId, Integer tenantId) {
        updateShopOrderOrderStatusAfterTicketFinished(ticketOrderId, tenantId, LocalDateTime.now());
    }

    private void updateShopOrderDeliveryStatusAfterAccept(Integer ticketOrderId, Integer tenantId, Integer riderId, LocalDateTime now) {
        if (ticketOrderId == null || tenantId == null) {
            return;
        }

        // 找到关联水票的商城订单（glt_user_ticket.orderId / orderNo）
        GltTicketOrder ticketOrder = this.lambdaQuery()
            .select(GltTicketOrder::getId, GltTicketOrder::getUserTicketId, GltTicketOrder::getRiderId)
            .eq(GltTicketOrder::getId, ticketOrderId)
            .eq(GltTicketOrder::getTenantId, tenantId)
            .eq(GltTicketOrder::getDeleted, 0)
            .last("limit 1")
            .one();
        if (ticketOrder == null || ticketOrder.getUserTicketId() == null) {
            return;
        }

        Integer actualRiderId = (riderId != null && riderId > 0) ? riderId : ticketOrder.getRiderId();

        GltUserTicket userTicket = gltUserTicketService.getOne(
            new LambdaQueryWrapper<GltUserTicket>()
                .eq(GltUserTicket::getTenantId, tenantId)
                .eq(GltUserTicket::getDeleted, 0)
                .eq(GltUserTicket::getId, ticketOrder.getUserTicketId())
                .last("limit 1")
        );
        if (userTicket == null) {
            return;
        }

        Integer shopOrderId = userTicket.getOrderId();
        String shopOrderNo = userTicket.getOrderNo();
        boolean resolvedByOrderGoodsId = false;
        // 兼容历史数据：部分水票可能只写了 orderGoodsId（未写 orderId/orderNo），此处兜底通过 orderGoodsId 反查 ShopOrder.orderId。
        if (shopOrderId == null && !StringUtils.hasText(shopOrderNo) && userTicket.getOrderGoodsId() != null) {
            ShopOrderGoods og = shopOrderGoodsService.getOne(
                new LambdaQueryWrapper<ShopOrderGoods>()
                    .select(ShopOrderGoods::getOrderId)
                    .eq(ShopOrderGoods::getTenantId, tenantId)
                    .eq(ShopOrderGoods::getId, userTicket.getOrderGoodsId())
                    .last("limit 1")
            );
            if (og != null) {
                shopOrderId = og.getOrderId();
                resolvedByOrderGoodsId = shopOrderId != null;
            }
        }
        if (shopOrderId == null && !StringUtils.hasText(shopOrderNo)) {
            log.warn("同步商城订单发货状态失败：未找到关联商城订单 - tenantId={}, ticketOrderId={}, userTicketId={}, userTicket.orderId={}, userTicket.orderNo={}, userTicket.orderGoodsId={}",
                tenantId, ticketOrderId, userTicket.getId(), userTicket.getOrderId(), userTicket.getOrderNo(), userTicket.getOrderGoodsId());
            return;
        }
        // 若是通过 orderGoodsId 兜底反查到 orderId，则顺便回填 glt_user_ticket.order_id/order_no，减少后续同步/查询依赖兜底分支。
        if (resolvedByOrderGoodsId && userTicket.getOrderId() == null && shopOrderId != null) {
            if (!StringUtils.hasText(shopOrderNo)) {
                ShopOrder order = shopOrderService.getOne(
                    new LambdaQueryWrapper<ShopOrder>()
                        .select(ShopOrder::getOrderNo)
                        .eq(ShopOrder::getTenantId, tenantId)
                        .eq(ShopOrder::getDeleted, 0)
                        .eq(ShopOrder::getOrderId, shopOrderId)
                        .last("limit 1")
                );
                if (order != null) {
                    shopOrderNo = order.getOrderNo();
                }
            }

            LambdaUpdateWrapper<GltUserTicket> backfill = new LambdaUpdateWrapper<GltUserTicket>()
                .eq(GltUserTicket::getTenantId, tenantId)
                .eq(GltUserTicket::getDeleted, 0)
                .eq(GltUserTicket::getId, userTicket.getId());
            backfill.set(GltUserTicket::getOrderId, shopOrderId);
            if (!StringUtils.hasText(userTicket.getOrderNo()) && StringUtils.hasText(shopOrderNo)) {
                backfill.set(GltUserTicket::getOrderNo, shopOrderNo);
            }
            backfill.set(GltUserTicket::getUpdateTime, now);
            try {
                gltUserTicketService.update(backfill);
            } catch (Exception e) {
                log.debug("回填水票关联商城订单信息失败(不影响主流程) - tenantId={}, userTicketId={}, orderId={}, orderNo={}",
                    tenantId, userTicket.getId(), shopOrderId, shopOrderNo, e);
            }
        }

        LambdaUpdateWrapper<ShopOrder> uw = new LambdaUpdateWrapper<ShopOrder>()
            .eq(ShopOrder::getTenantId, tenantId)
            .eq(ShopOrder::getDeleted, 0)
            // deliveryStatus 已经是 20 时也可能需要补写/更新 riderId，因此条件包含 riderId 不一致的场景
            .and(w -> {
                w.ne(ShopOrder::getDeliveryStatus, 20).or().isNull(ShopOrder::getDeliveryStatus);
                if (actualRiderId != null && actualRiderId > 0) {
                    w.or().ne(ShopOrder::getRiderId, actualRiderId).or().isNull(ShopOrder::getRiderId);
                }
            })
            .set(ShopOrder::getDeliveryStatus, 20)
            .set(ShopOrder::getUpdateTime, now);
        if (actualRiderId != null && actualRiderId > 0) {
            uw.set(ShopOrder::getRiderId, actualRiderId);
        }
        if (shopOrderId != null) {
            uw.eq(ShopOrder::getOrderId, shopOrderId);
        } else {
            uw.eq(ShopOrder::getOrderNo, shopOrderNo);
        }

        boolean updated = shopOrderService.update(uw);
        if (updated) {
            return;
        }

        // 幂等：若已是 20，则视为成功；否则记录日志便于排查关联关系/数据缺失
        LambdaQueryWrapper<ShopOrder> qw = new LambdaQueryWrapper<ShopOrder>()
            .eq(ShopOrder::getTenantId, tenantId)
            .eq(ShopOrder::getDeleted, 0)
            .eq(ShopOrder::getDeliveryStatus, 20);
        if (actualRiderId != null && actualRiderId > 0) {
            qw.eq(ShopOrder::getRiderId, actualRiderId);
        }
        if (shopOrderId != null) {
            qw.eq(ShopOrder::getOrderId, shopOrderId);
        } else {
            qw.eq(ShopOrder::getOrderNo, shopOrderNo);
        }
        if (shopOrderService.count(qw) <= 0) {
            log.warn("接单/指派成功但同步商城订单发货状态/配送员失败 - tenantId={}, ticketOrderId={}, riderId={}, shopOrderId={}, shopOrderNo={}",
                tenantId, ticketOrderId, actualRiderId, shopOrderId, shopOrderNo);
        }
    }

    @Override
    public void start(Integer id, Integer riderId, Integer tenantId) {
        if (id == null) {
            throw new BusinessException("订单id不能为空");
        }
        if (riderId == null) {
            throw new BusinessException("配送员信息缺失");
        }
        if (tenantId == null) {
            throw new BusinessException("租户信息缺失");
        }

        LocalDateTime now = LocalDateTime.now();
        boolean ok = this.lambdaUpdate()
                .set(GltTicketOrder::getDeliveryStatus, DELIVERY_STATUS_DELIVERING)
                .set(GltTicketOrder::getSendStartTime, now)
                .set(GltTicketOrder::getUpdateTime, now)
                .eq(GltTicketOrder::getId, id)
                .eq(GltTicketOrder::getTenantId, tenantId)
                .eq(GltTicketOrder::getDeleted, 0)
                .eq(GltTicketOrder::getRiderId, riderId)
                .and(w -> w.eq(GltTicketOrder::getDeliveryStatus, DELIVERY_STATUS_WAITING)
                        .or().isNull(GltTicketOrder::getDeliveryStatus))
                .update();
        if (ok) {
            return;
        }

        GltTicketOrder order = this.lambdaQuery()
                .eq(GltTicketOrder::getId, id)
                .eq(GltTicketOrder::getTenantId, tenantId)
                .eq(GltTicketOrder::getDeleted, 0)
                .one();
        if (order == null) {
            throw new BusinessException("订单不存在");
        }
        if (!riderId.equals(order.getRiderId())) {
            throw new BusinessException("无权限操作该订单");
        }
        if (order.getDeliveryStatus() != null && order.getDeliveryStatus() == DELIVERY_STATUS_DELIVERING) {
            // 幂等：重复开始配送视为成功
            return;
        }
        throw new BusinessException("订单状态不允许开始配送");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delivered(Integer id, Integer riderId, Integer tenantId, String sendEndImg) {
        if (id == null) {
            throw new BusinessException("订单id不能为空");
        }
        if (riderId == null) {
            throw new BusinessException("配送员信息缺失");
        }
        if (tenantId == null) {
            throw new BusinessException("租户信息缺失");
        }

        LocalDateTime now = LocalDateTime.now();
        var update = this.lambdaUpdate()
                .set(GltTicketOrder::getDeliveryStatus, DELIVERY_STATUS_WAIT_CONFIRM)
                .set(GltTicketOrder::getSendEndTime, now)
                .set(GltTicketOrder::getUpdateTime, now);
        if (StringUtils.hasText(sendEndImg)) {
            update.set(GltTicketOrder::getSendEndImg, sendEndImg);
        }
        boolean ok = update
                .eq(GltTicketOrder::getId, id)
                .eq(GltTicketOrder::getTenantId, tenantId)
                .eq(GltTicketOrder::getDeleted, 0)
                .eq(GltTicketOrder::getRiderId, riderId)
                .eq(GltTicketOrder::getDeliveryStatus, DELIVERY_STATUS_DELIVERING)
                .update();
        if (ok) {
            // 配送员拍照上传送达后可触发提成结算（幂等，重复调用不会重复入账）
            settleRiderCommissionIfEligible(id, tenantId, true);
            // 配送员确认送达后，同步商城订单为“已完成”(orderStatus=1)
            updateShopOrderOrderStatusAfterTicketFinished(id, tenantId, now);
            return;
        }

        GltTicketOrder order = this.lambdaQuery()
                .eq(GltTicketOrder::getId, id)
                .eq(GltTicketOrder::getTenantId, tenantId)
                .eq(GltTicketOrder::getDeleted, 0)
                .one();
        if (order == null) {
            throw new BusinessException("订单不存在");
        }
        if (!riderId.equals(order.getRiderId())) {
            throw new BusinessException("无权限操作该订单");
        }
        if (order.getDeliveryStatus() != null
                && (order.getDeliveryStatus() == DELIVERY_STATUS_WAIT_CONFIRM
                || order.getDeliveryStatus() == DELIVERY_STATUS_FINISHED)) {
            // 幂等：重复送达视为成功
            settleRiderCommissionIfEligible(id, tenantId, true);
            updateShopOrderOrderStatusAfterTicketFinished(id, tenantId, LocalDateTime.now());
            return;
        }
        throw new BusinessException("订单状态不允许确认送达");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void confirmReceive(Integer id, Integer userId, Integer tenantId) {
        if (id == null) {
            throw new BusinessException("订单id不能为空");
        }
        if (userId == null) {
            throw new BusinessException("请先登录");
        }
        if (tenantId == null) {
            throw new BusinessException("租户信息缺失");
        }

        LocalDateTime now = LocalDateTime.now();
        boolean ok = this.lambdaUpdate()
                .set(GltTicketOrder::getDeliveryStatus, DELIVERY_STATUS_FINISHED)
                .set(GltTicketOrder::getReceiveConfirmTime, now)
                .set(GltTicketOrder::getReceiveConfirmType, RECEIVE_CONFIRM_TYPE_MANUAL)
                .set(GltTicketOrder::getUpdateTime, now)
                .eq(GltTicketOrder::getId, id)
                .eq(GltTicketOrder::getTenantId, tenantId)
                .eq(GltTicketOrder::getDeleted, 0)
                .eq(GltTicketOrder::getUserId, userId)
                .eq(GltTicketOrder::getDeliveryStatus, DELIVERY_STATUS_WAIT_CONFIRM)
                .update();
        if (ok) {
            // 用户确认收货完成后触发配送员提成结算（幂等，重复调用不会重复入账）
            settleRiderCommissionIfEligible(id, tenantId, false);
            // 送水订单完成后，同步商城订单为“已完成”(orderStatus=1)
            updateShopOrderOrderStatusAfterTicketFinished(id, tenantId, now);
            return;
        }

        GltTicketOrder order = this.lambdaQuery()
                .eq(GltTicketOrder::getId, id)
                .eq(GltTicketOrder::getTenantId, tenantId)
                .eq(GltTicketOrder::getDeleted, 0)
                .one();
        if (order == null) {
            throw new BusinessException("订单不存在");
        }
        if (!userId.equals(order.getUserId())) {
            throw new BusinessException("无权限操作该订单");
        }
        if (order.getDeliveryStatus() != null && order.getDeliveryStatus() == DELIVERY_STATUS_FINISHED) {
            // 幂等：重复确认收货视为成功
            settleRiderCommissionIfEligible(id, tenantId, false);
            updateShopOrderOrderStatusAfterTicketFinished(id, tenantId, LocalDateTime.now());
            return;
        }
        throw new BusinessException("订单状态不允许确认收货");
    }

    @Override
    public int autoConfirmTimeout(Integer tenantId, LocalDateTime now, int timeoutHours, int batchSize) {
        if (tenantId == null) {
            return 0;
        }
        if (now == null) {
            now = LocalDateTime.now();
        }
        int hours = Math.max(timeoutHours, 1);
        int limit = Math.max(batchSize, 1);
        LocalDateTime deadline = now.minusHours(hours);

        List<GltTicketOrder> candidates = this.lambdaQuery()
            .select(GltTicketOrder::getId)
            .eq(GltTicketOrder::getTenantId, tenantId)
            .eq(GltTicketOrder::getDeleted, 0)
            .eq(GltTicketOrder::getDeliveryStatus, DELIVERY_STATUS_WAIT_CONFIRM)
            .isNotNull(GltTicketOrder::getSendEndTime)
            .le(GltTicketOrder::getSendEndTime, deadline)
            .orderByAsc(GltTicketOrder::getSendEndTime)
            .orderByAsc(GltTicketOrder::getId)
            .last("limit " + limit)
            .list();
        if (candidates == null || candidates.isEmpty()) {
            return 0;
        }

        int confirmed = 0;
        for (GltTicketOrder item : candidates) {
            Integer id = item != null ? item.getId() : null;
            if (id == null) {
                continue;
            }
            try {
                final LocalDateTime nowFinal = now;
                final LocalDateTime deadlineFinal = deadline;
                Boolean ok = transactionTemplate.execute(status -> {
                    boolean updated = this.lambdaUpdate()
                        .set(GltTicketOrder::getDeliveryStatus, DELIVERY_STATUS_FINISHED)
                        .set(GltTicketOrder::getReceiveConfirmTime, nowFinal)
                        .set(GltTicketOrder::getReceiveConfirmType, RECEIVE_CONFIRM_TYPE_TIMEOUT)
                        .set(GltTicketOrder::getUpdateTime, nowFinal)
                        .eq(GltTicketOrder::getId, id)
                        .eq(GltTicketOrder::getTenantId, tenantId)
                        .eq(GltTicketOrder::getDeleted, 0)
                        .eq(GltTicketOrder::getDeliveryStatus, DELIVERY_STATUS_WAIT_CONFIRM)
                        .le(GltTicketOrder::getSendEndTime, deadlineFinal)
                        .update();
                    if (!updated) {
                        return false;
                    }
                    // 超时自动确认收货后，也按“完成”逻辑触发配送员提成结算（幂等）。
                    settleRiderCommissionIfEligible(id, tenantId, false);
                    updateShopOrderOrderStatusAfterTicketFinished(id, tenantId, nowFinal);
                    return true;
                });
                if (Boolean.TRUE.equals(ok)) {
                    confirmed++;
                }
            } catch (Exception e) {
                log.warn("送水订单超时自动确认收货失败 - tenantId={}, ticketOrderId={}", tenantId, id, e);
            }
        }
        return confirmed;
    }

    private void updateShopOrderOrderStatusAfterTicketFinished(Integer ticketOrderId, Integer tenantId, LocalDateTime now) {
        if (ticketOrderId == null || tenantId == null) {
            return;
        }
        if (now == null) {
            now = LocalDateTime.now();
        }

        // 找到关联水票的商城订单（glt_user_ticket.orderId / orderNo）
        GltTicketOrder ticketOrder = this.lambdaQuery()
            .select(GltTicketOrder::getId, GltTicketOrder::getUserTicketId)
            .eq(GltTicketOrder::getId, ticketOrderId)
            .eq(GltTicketOrder::getTenantId, tenantId)
            .eq(GltTicketOrder::getDeleted, 0)
            .last("limit 1")
            .one();
        if (ticketOrder == null || ticketOrder.getUserTicketId() == null) {
            return;
        }

        GltUserTicket userTicket = gltUserTicketService.getOne(
            new LambdaQueryWrapper<GltUserTicket>()
                .eq(GltUserTicket::getTenantId, tenantId)
                .eq(GltUserTicket::getDeleted, 0)
                .eq(GltUserTicket::getId, ticketOrder.getUserTicketId())
                .last("limit 1")
        );
        if (userTicket == null) {
            return;
        }

        Integer shopOrderId = userTicket.getOrderId();
        String shopOrderNo = userTicket.getOrderNo();
        boolean resolvedByOrderGoodsId = false;
        // 兼容历史数据：部分水票可能只写了 orderGoodsId（未写 orderId/orderNo），此处兜底通过 orderGoodsId 反查 ShopOrder.orderId。
        if (shopOrderId == null && !StringUtils.hasText(shopOrderNo) && userTicket.getOrderGoodsId() != null) {
            ShopOrderGoods og = shopOrderGoodsService.getOne(
                new LambdaQueryWrapper<ShopOrderGoods>()
                    .select(ShopOrderGoods::getOrderId)
                    .eq(ShopOrderGoods::getTenantId, tenantId)
                    .eq(ShopOrderGoods::getId, userTicket.getOrderGoodsId())
                    .last("limit 1")
            );
            if (og != null) {
                shopOrderId = og.getOrderId();
                resolvedByOrderGoodsId = shopOrderId != null;
            }
        }
        if (shopOrderId == null && !StringUtils.hasText(shopOrderNo)) {
            log.warn("送水订单完成但未找到关联商城订单，无法同步完成状态 - tenantId={}, ticketOrderId={}, userTicketId={}, userTicket.orderId={}, userTicket.orderNo={}, userTicket.orderGoodsId={}",
                tenantId, ticketOrderId, userTicket.getId(), userTicket.getOrderId(), userTicket.getOrderNo(), userTicket.getOrderGoodsId());
            return;
        }
        // 若是通过 orderGoodsId 兜底反查到 orderId，则顺便回填 glt_user_ticket.order_id/order_no，减少后续同步/查询依赖兜底分支。
        if (resolvedByOrderGoodsId && userTicket.getOrderId() == null && shopOrderId != null) {
            if (!StringUtils.hasText(shopOrderNo)) {
                ShopOrder order = shopOrderService.getOne(
                    new LambdaQueryWrapper<ShopOrder>()
                        .select(ShopOrder::getOrderNo)
                        .eq(ShopOrder::getTenantId, tenantId)
                        .eq(ShopOrder::getDeleted, 0)
                        .eq(ShopOrder::getOrderId, shopOrderId)
                        .last("limit 1")
                );
                if (order != null) {
                    shopOrderNo = order.getOrderNo();
                }
            }

            LambdaUpdateWrapper<GltUserTicket> backfill = new LambdaUpdateWrapper<GltUserTicket>()
                .eq(GltUserTicket::getTenantId, tenantId)
                .eq(GltUserTicket::getDeleted, 0)
                .eq(GltUserTicket::getId, userTicket.getId());
            backfill.set(GltUserTicket::getOrderId, shopOrderId);
            if (!StringUtils.hasText(userTicket.getOrderNo()) && StringUtils.hasText(shopOrderNo)) {
                backfill.set(GltUserTicket::getOrderNo, shopOrderNo);
            }
            backfill.set(GltUserTicket::getUpdateTime, now);
            try {
                gltUserTicketService.update(backfill);
            } catch (Exception e) {
                log.debug("回填水票关联商城订单信息失败(不影响主流程) - tenantId={}, userTicketId={}, orderId={}, orderNo={}",
                    tenantId, userTicket.getId(), shopOrderId, shopOrderNo, e);
            }
        }

        //查询未完成订单，完成资金解冻
        LambdaQueryWrapper<ShopOrder> orderLambdaQueryWrapper;
        if(shopOrderId != null){
            orderLambdaQueryWrapper = new LambdaQueryWrapper<ShopOrder>().eq(ShopOrder::getOrderId, shopOrderId).eq(ShopOrder::getOrderStatus, 0);
        }else{
            orderLambdaQueryWrapper = new LambdaQueryWrapper<ShopOrder>().eq(ShopOrder::getOrderId, shopOrderNo).eq(ShopOrder::getOrderStatus, 0);
        }
        ShopOrder order = shopOrderService.getOne(orderLambdaQueryWrapper);
        if(order != null){
            ShopDealerUserReduceDto reduceDto = new ShopDealerUserReduceDto();
            reduceDto.setTypeEnum(ShopDealerTypeEnum.DEFROST);
            reduceDto.setOrderUserId(order.getUserId());
            reduceDto.setOrderNo(order.getOrderNo());
            reduceDto.setUpdateEnum(ShopDealerCapitalUpdateEnum.FREEZE_MONEY_THAW);

            //按订单号资金解冻
            shopDealerUserService.reduceBalance(reduceDto);
        }

        LambdaUpdateWrapper<ShopOrder> uw = new LambdaUpdateWrapper<ShopOrder>()
            .eq(ShopOrder::getTenantId, tenantId)
            .eq(ShopOrder::getDeleted, 0)
            .and(w -> w.ne(ShopOrder::getOrderStatus, 1).or().isNull(ShopOrder::getOrderStatus))
            .set(ShopOrder::getOrderStatus, 1)
            .set(ShopOrder::getUpdateTime, now);
        if (shopOrderId != null) {
            uw.eq(ShopOrder::getOrderId, shopOrderId);
        } else {
            uw.eq(ShopOrder::getOrderNo, shopOrderNo);
        }
        boolean updated = shopOrderService.update(uw);
        if (updated) {
            return;
        }

        // 幂等：若已是 1，则视为成功；否则记录日志便于排查关联关系/数据缺失
        LambdaQueryWrapper<ShopOrder> qw = new LambdaQueryWrapper<ShopOrder>()
            .eq(ShopOrder::getTenantId, tenantId)
            .eq(ShopOrder::getDeleted, 0)
            .eq(ShopOrder::getOrderStatus, 1);
        if (shopOrderId != null) {
            qw.eq(ShopOrder::getOrderId, shopOrderId);
        } else {
            qw.eq(ShopOrder::getOrderNo, shopOrderNo);
        }
        if (shopOrderService.count(qw) <= 0) {
            log.warn("送水订单完成但同步商城订单完成状态失败 - tenantId={}, ticketOrderId={}, shopOrderId={}, shopOrderNo={}",
                tenantId, ticketOrderId, shopOrderId, shopOrderNo);
        }
    }

    private void settleRiderCommissionIfEligible(Integer ticketOrderId, Integer tenantId, boolean requirePhoto) {
        if (ticketOrderId == null || tenantId == null) {
            return;
        }
        // 目前仅租户10584启用该提成规则，避免影响其他租户历史逻辑。
        if (tenantId != TENANT_ID_10584) {
            return;
        }

        transactionTemplate.executeWithoutResult(status -> {
            // 锁定送水订单行：避免并发下重复结算（如：配送员送达&用户确认收货同时触发）
            GltTicketOrder order = this.lambdaQuery()
                .eq(GltTicketOrder::getId, ticketOrderId)
                .eq(GltTicketOrder::getTenantId, tenantId)
                .eq(GltTicketOrder::getDeleted, 0)
                .last("limit 1 for update")
                .one();
            if (order == null) {
                return;
            }

            Integer riderId = order.getRiderId();
            if (riderId == null || riderId <= 0) {
                return;
            }

            Integer deliveryStatus = order.getDeliveryStatus();
            if (requirePhoto) {
                // 配送员拍照上传触发：至少需要到“待客户确认”或“已完成”状态，且存在送达照片。
                if (deliveryStatus == null || (deliveryStatus != DELIVERY_STATUS_WAIT_CONFIRM && deliveryStatus != DELIVERY_STATUS_FINISHED)) {
                    return;
                }
                if (!StringUtils.hasText(order.getSendEndImg())) {
                    return;
                }
            } else {
                // 用户确认收货触发：必须为“已完成”状态。
                if (deliveryStatus == null || deliveryStatus != DELIVERY_STATUS_FINISHED) {
                    return;
                }
            }

            int qty = order.getTotalNum() == null ? 0 : order.getTotalNum();
            if (qty <= 0) {
                return;
            }

            BigDecimal money = RIDER_UNIT_COMMISSION
                .multiply(BigDecimal.valueOf(qty))
                .setScale(RIDER_COMMISSION_SCALE, RoundingMode.HALF_UP);
            if (money.signum() <= 0) {
                return;
            }

            String orderNo = "gltTicketOrder:" + order.getId();
            String comments = "配送员提成(ticketOrderId=" + order.getId() + ",unit=" + RIDER_UNIT_COMMISSION + ",qty=" + qty + ")";

            // 幂等：同一送水订单同一配送员只结算一次
            boolean already = shopDealerCapitalService.count(
                new LambdaQueryWrapper<ShopDealerCapital>()
                    .eq(ShopDealerCapital::getTenantId, tenantId)
                    .eq(ShopDealerCapital::getFlowType, 10)
                    .eq(ShopDealerCapital::getUserId, riderId)
                    .eq(ShopDealerCapital::getOrderNo, orderNo)
                    .likeRight(ShopDealerCapital::getComments, "配送员提成(ticketOrderId=" + order.getId() + ",")
            ) > 0;
            if (already) {
                return;
            }

            // 送水订单提成：直接入账可提现余额 money（配送员提成即时可用，无需冻结）
            LocalDateTime now = LocalDateTime.now();
            boolean updated = shopDealerUserService.update(
                new LambdaUpdateWrapper<ShopDealerUser>()
                    .eq(ShopDealerUser::getTenantId, tenantId)
                    .eq(ShopDealerUser::getUserId, riderId)
                    .setSql("money = IFNULL(money,0) + " + money.toPlainString())
                    .setSql("total_money = IFNULL(total_money,0) + " + money.toPlainString())
                    .set(ShopDealerUser::getUpdateTime, now)
            );

            if (!updated) {
                // 配送员可能未开通分销账户：创建后再尝试入账一次（与分销结算逻辑保持一致）
                ShopDealerUser existed = shopDealerUserService.getOne(
                    new LambdaQueryWrapper<ShopDealerUser>()
                        .eq(ShopDealerUser::getTenantId, tenantId)
                        .eq(ShopDealerUser::getUserId, riderId)
                        .last("limit 1")
                );
                if (existed == null) {
                    ShopDealerUser newDealerUser = new ShopDealerUser();
                    newDealerUser.setTenantId(tenantId);
                    newDealerUser.setUserId(riderId);
                    newDealerUser.setType(0);
                    newDealerUser.setIsDelete(0);
                    newDealerUser.setSortNumber(0);
                    newDealerUser.setFirstNum(0);
                    newDealerUser.setSecondNum(0);
                    newDealerUser.setThirdNum(0);
                    newDealerUser.setMoney(BigDecimal.ZERO);
                    newDealerUser.setFreezeMoney(BigDecimal.ZERO);
                    newDealerUser.setTotalMoney(BigDecimal.ZERO);
                    try {
                        List<User> sysUsers = userMapper.selectByIdIgnoreTenant(riderId);
                        User sysUser = (sysUsers != null && !sysUsers.isEmpty()) ? sysUsers.get(0) : null;
                        if (sysUser != null) {
                            newDealerUser.setRealName(sysUser.getRealName() != null ? sysUser.getRealName() : sysUser.getNickname());
                            newDealerUser.setMobile(sysUser.getPhone());
                        }
                    } catch (Exception ignore) {
                        // 基础信息补齐失败不影响入账
                    }
                    newDealerUser.setCreateTime(now);
                    newDealerUser.setUpdateTime(now);
                    shopDealerUserService.save(newDealerUser);
                }

                updated = shopDealerUserService.update(
                    new LambdaUpdateWrapper<ShopDealerUser>()
                        .eq(ShopDealerUser::getTenantId, tenantId)
                        .eq(ShopDealerUser::getUserId, riderId)
                        .setSql("money = IFNULL(money,0) + " + money.toPlainString())
                        .setSql("total_money = IFNULL(total_money,0) + " + money.toPlainString())
                        .set(ShopDealerUser::getUpdateTime, now)
                );
                if (!updated) {
                    log.warn("配送员提成入账失败：未找到/创建分销账户 - tenantId={}, ticketOrderId={}, riderId={}", tenantId, order.getId(), riderId);
                    return;
                }
            }

            ShopDealerCapital cap = new ShopDealerCapital();
            cap.setUserId(riderId);
            cap.setOrderNo(orderNo);
            cap.setFlowType(10);
            cap.setMoney(money);
            cap.setComments(comments);
            cap.setToUserId(order.getUserId());
            cap.setMonth(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM")));
            cap.setTenantId(tenantId);
            cap.setCreateTime(now);
            cap.setUpdateTime(now);
            shopDealerCapitalService.save(cap);
        });
    }

}
