package com.gxwebsoft.glt.task;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.gxwebsoft.common.core.annotation.IgnoreTenant;
import com.gxwebsoft.glt.entity.GltTicketOrder;
import com.gxwebsoft.glt.entity.GltTicketTemplate;
import com.gxwebsoft.glt.entity.GltUserTicket;
import com.gxwebsoft.glt.service.GltTicketOrderService;
import com.gxwebsoft.glt.service.GltTicketTemplateService;
import com.gxwebsoft.glt.service.GltUserTicketService;
import com.gxwebsoft.shop.entity.ShopDealerCapital;
import com.gxwebsoft.shop.entity.ShopDealerOrder;
import com.gxwebsoft.shop.entity.ShopDealerUser;
import com.gxwebsoft.shop.entity.ShopGoods;
import com.gxwebsoft.shop.entity.ShopOrder;
import com.gxwebsoft.shop.entity.ShopOrderGoods;
import com.gxwebsoft.shop.service.ShopDealerCapitalService;
import com.gxwebsoft.shop.service.ShopDealerOrderService;
import com.gxwebsoft.shop.service.ShopDealerUserService;
import com.gxwebsoft.shop.service.ShopGoodsService;
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
import com.gxwebsoft.shop.service.ShopOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
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.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 租户10584：分销佣金解冻任务
 *
 * <p>规则：</p>
 * <p>1) 送水套餐(formId in 水票模板 goodsId)：订单号关联的水票产生了第一次送水订单，且该第一次送水订单状态=已完成(40) -> 解冻。</p>
 * <p>2) 非送水套餐(formId not in 水票模板 goodsId)：订单已确认收货(orderStatus=1) -> 解冻。</p>
 *
 * <p>实现策略：以 ShopDealerCapital(flowType=10) 的"佣金明细"为解冻粒度，
 * 每条佣金明细对应生成一条 ShopDealerCapital(flowType=50) 作为幂等标记，并执行
 * ShopDealerUser.freezeMoney -> ShopDealerUser.money 的转移。</p>
 */
@Slf4j
@Component
@ConditionalOnProperty(prefix = "dealer.commission.unfreeze10584", name = "enabled", havingValue = "true", matchIfMissing = true)
public class DealerCommissionUnfreeze10584Task {

    private static final int TENANT_ID = 10584;

    private static final int ORDER_STATUS_CONFIRMED_RECEIVE = 1;

    private static final int MAX_ELIGIBLE_ORDER_NOS_PER_RUN = 200;
    private static final int MAX_ELIGIBLE_TICKET_ORDERS_PER_RUN = 200;
    private static final int MAX_CAPITALS_PER_RUN = 500;

    private static final int FLOW_TYPE_DELIVERY_REWARD = 60; // 配送奖励（直接入可提现金额）

    /**
     * 兼容两种录入方式：
     * - 0.05 表示 5%（比例）
     * - 5 表示 5%（百分比）
     */
    private static BigDecimal normalizeDeliveryRate(BigDecimal rawRate) {
        if (rawRate == null || rawRate.signum() <= 0) {
            return null;
        }
        // 如果录入 >= 1，按"百分比"处理（1 => 1%）
        if (rawRate.compareTo(BigDecimal.ONE) >= 0) {
            return rawRate.movePointLeft(2);
        }
        return rawRate;
    }

    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    private ShopOrderService shopOrderService;

    @Resource
    private ShopOrderGoodsService shopOrderGoodsService;

    @Resource
    private ShopGoodsService shopGoodsService;

    @Resource
    private ShopDealerCapitalService shopDealerCapitalService;

    @Resource
    private ShopDealerOrderService shopDealerOrderService;

    @Resource
    private ShopDealerUserService shopDealerUserService;

    @Resource
    private GltUserTicketService gltUserTicketService;

    @Resource
    private GltTicketOrderService gltTicketOrderService;

    @Resource
    private GltTicketTemplateService gltTicketTemplateService;

    private final AtomicBoolean running = new AtomicBoolean(false);

    @Scheduled(cron = "${dealer.commission.unfreeze10584.cron:0/50 * * * * ?}")
    @IgnoreTenant("定时任务无登录态，需忽略租户隔离；内部使用 tenantId=10584 精确过滤")
    public void run() {
        if (!running.compareAndSet(false, true)) {
            log.warn("分销佣金解冻任务仍在执行中，本轮跳过 - tenantId={}", TENANT_ID);
            return;
        }

        try {
            // ========== 步骤1: 加载水票模板 ==========
            Set<Integer> waterFormIds = loadWaterFormIds();
            log.info("【步骤1】加载水票模板 - tenantId={}, waterFormIds={}", TENANT_ID, waterFormIds);
            if (waterFormIds.isEmpty()) {
                // 送水/非送水的判断依赖模板 goodsId；拿不到会导致误判，宁可跳过本轮。
                log.warn("分销佣金解冻任务跳过：未找到水票模板 goodsId - tenantId={}", TENANT_ID);
                return;
            }

            // ========== 步骤2: 扫描非送水订单（优先最新） ==========
            Set<String> eligibleOrderNos = new HashSet<>();
            List<String> nonWaterOrders = findEligibleNonWaterOrderNos(waterFormIds, true);
            log.info("【步骤2】扫描非送水订单（最新优先）- tenantId={}, count={}, orderNos={}", TENANT_ID, nonWaterOrders.size(), nonWaterOrders.size() <= 20 ? nonWaterOrders : nonWaterOrders.subList(0, 20));
            eligibleOrderNos.addAll(nonWaterOrders);

            // ========== 步骤3: 扫描送水订单（第一条送水完成） ==========
            Set<String> waterOrders = findEligibleWaterOrderNosByFirstFinishedTicketOrder(waterFormIds);
            log.info("【步骤3】扫描送水订单（第一条送水完成）- tenantId={}, count={}, orderNos={}", TENANT_ID, waterOrders.size(), waterOrders);
            eligibleOrderNos.addAll(waterOrders);

            if (eligibleOrderNos.isEmpty()) {
                log.info("【步骤4-9】无可处理订单，本轮结束 - tenantId={}", TENANT_ID);
                return;
            }
            // 订单太多时不打印完整列表
            String orderNosSummary = eligibleOrderNos.size() <= 30 ? eligibleOrderNos.toString() : eligibleOrderNos.size() + " orders (too many to show)";
            log.info("【步骤4】汇总待处理订单 - tenantId={}, totalCount={}, orderNos={}", TENANT_ID, eligibleOrderNos.size(), orderNosSummary);

            // ========== 步骤5: 发放配送奖励 ==========
            log.info("【步骤5】开始发放配送奖励 - tenantId={}, orderCount={}", TENANT_ID, eligibleOrderNos.size());
            int rewarded = 0;
            List<String> rewardedOrders = new ArrayList<>();
            for (String orderNo : eligibleOrderNos) {
                try {
                    if (settleDeliveryRewardIfNeeded(orderNo)) {
                        rewarded++;
                        rewardedOrders.add(orderNo);
                    }
                } catch (Exception e) {
                    log.error("发放配送奖励失败，将在下次任务重试 - tenantId={}, orderNo={}", TENANT_ID, orderNo, e);
                }
            }
            log.info("【步骤5】配送奖励发放完成 - tenantId={}, rewardedCount={}, rewardedOrders={}", TENANT_ID, rewarded, rewardedOrders);

            // ========== 步骤6: 查询佣金明细 ==========
            log.info("【步骤6】查询佣金明细（flowType=10）- tenantId={}, orderCount={}", TENANT_ID, eligibleOrderNos.size());
            List<ShopDealerCapital> capitals = findCapitalsByEligibleOrderNos(eligibleOrderNos);
            String capitalsSummary = capitals.size() <= 50 
                ? capitals.stream().map(c -> "capitalId=" + c.getId() + ", orderNo=" + c.getOrderNo() + ", amount=" + c.getMoney()).toList().toString()
                : capitals.size() + " capitals (too many to show)";
            log.info("【步骤6】查询到佣金明细 - tenantId={}, count={}, capitals={}", TENANT_ID, capitals.size(), capitalsSummary);

            if (capitals.isEmpty()) {
                // ========== 步骤6.1: 兜底扫描历史订单 ==========
                log.info("【步骤6.1】本轮未取到佣金明细，执行兜底扫描（最早确认收货） - tenantId={}", TENANT_ID);
                eligibleOrderNos.clear();
                List<String> fallbackNonWater = findEligibleNonWaterOrderNos(waterFormIds, false);
                log.info("【步骤6.1】兜底-非送水订单 - tenantId={}, count={}", TENANT_ID, fallbackNonWater.size());
                eligibleOrderNos.addAll(fallbackNonWater);

                Set<String> fallbackWater = findEligibleWaterOrderNosByFirstFinishedTicketOrder(waterFormIds);
                log.info("【步骤6.1】兜底-送水订单 - tenantId={}, count={}", TENANT_ID, fallbackWater.size());
                eligibleOrderNos.addAll(fallbackWater);

                // 兜底扫描出来的订单也补发配送奖励（幂等）
                log.info("【步骤6.1】兜底扫描后补发配送奖励 - tenantId={}, orderCount={}", TENANT_ID, eligibleOrderNos.size());
                for (String orderNo : eligibleOrderNos) {
                    try {
                        if (settleDeliveryRewardIfNeeded(orderNo)) {
                            rewarded++;
                            rewardedOrders.add(orderNo + "(兜底)");
                        }
                    } catch (Exception e) {
                        log.error("发放配送奖励失败，将在下次任务重试 - tenantId={}, orderNo={}", TENANT_ID, orderNo, e);
                    }
                }

                capitals = findCapitalsByEligibleOrderNos(eligibleOrderNos);
                log.info("【步骤6.1】兜底扫描到佣金明细 - tenantId={}, count={}", TENANT_ID, capitals.size());
            }

            if (capitals.isEmpty()) {
                log.info("【步骤7-9】仍未查到佣金明细，本轮结束 - tenantId={}", TENANT_ID);
                return;
            }

            // ========== 步骤7: 执行佣金解冻 ==========
            log.info("【步骤7】开始执行佣金解冻 - tenantId={}, totalCount={}", TENANT_ID, capitals.size());
            int unfrozen = 0;
            List<String> unfrozenDetails = new ArrayList<>();
            for (ShopDealerCapital cap : capitals) {
                try {
                    boolean ok = unfreezeOneCapitalIfNeeded(cap);
                    if (ok) {
                        unfrozen++;
                        unfrozenDetails.add("capitalId=" + cap.getId() + ", orderNo=" + cap.getOrderNo() + ", amount=" + cap.getMoney());
                    }
                } catch (Exception e) {
                    log.error("解冻佣金失败，将在下次任务重试 - tenantId={}, capitalId={}, orderNo={}, userId={}",
                        TENANT_ID, cap != null ? cap.getId() : null, cap != null ? cap.getOrderNo() : null, cap != null ? cap.getUserId() : null, e);
                }
            }
            log.info("【步骤7】佣金解冻完成 - tenantId={}, unfrozenDetails={}", TENANT_ID, unfrozenDetails);

            // ========== 步骤8: 更新分销订单状态 ==========
            log.info("【步骤8】检查并更新分销订单状态（isUnfreeze=1）- tenantId={}, unfrozenCount={}", TENANT_ID, unfrozen);

            // ========== 步骤9: 汇总报告 ==========
            log.info("========================================");
            log.info("【步骤9】本轮任务执行完毕 - tenantId={}", TENANT_ID);
            log.info("  - 配送奖励: rewarded={}, orders={}", rewarded, rewardedOrders);
            log.info("  - 佣金解冻: unfrozen={}, details={}", unfrozen, unfrozenDetails);
            log.info("========================================");
        } finally {
            running.set(false);
        }
    }

    private boolean settleDeliveryRewardIfNeeded(String orderNo) {
        if (orderNo == null || orderNo.isBlank()) {
            log.debug("【步骤5.X】配送奖励跳过：orderNo为空 - tenantId={}", TENANT_ID);
            return false;
        }
        log.debug("【步骤5.X】开始处理配送奖励 - tenantId={}, orderNo={}", TENANT_ID, orderNo);

        ShopOrder order = shopOrderService.getOne(
            new LambdaQueryWrapper<ShopOrder>()
                .eq(ShopOrder::getTenantId, TENANT_ID)
                .eq(ShopOrder::getDeleted, 0)
                .eq(ShopOrder::getOrderNo, orderNo)
                .last("limit 1")
        );
        if (order == null) {
            log.debug("【步骤5.X】配送奖励跳过：订单不存在 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
            return false;
        }

        Integer riderId = order.getRiderId();
        if (riderId == null || riderId <= 0) {
            log.debug("【步骤5.X】配送奖励跳过：无配送员 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
            return false;
        }
        log.debug("【步骤5.X】找到配送员 - tenantId={}, orderNo={}, riderId={}", TENANT_ID, orderNo, riderId);

        // 快速幂等检查：已发放则跳过（事务内仍会二次校验避免并发重复）
        boolean already = shopDealerCapitalService.count(
            new LambdaQueryWrapper<ShopDealerCapital>()
                .eq(ShopDealerCapital::getTenantId, TENANT_ID)
                .eq(ShopDealerCapital::getFlowType, FLOW_TYPE_DELIVERY_REWARD)
                .eq(ShopDealerCapital::getOrderNo, orderNo)
        ) > 0;
        if (already) {
            log.debug("【步骤5.X】配送奖励跳过：已发放（幂等）- tenantId={}, orderNo={}", TENANT_ID, orderNo);
            return false;
        }
        log.debug("【步骤5.X】进入事务 - tenantId={}, orderNo={}", TENANT_ID, orderNo);

        try {
            return Boolean.TRUE.equals(transactionTemplate.execute(status -> {
                LocalDateTime now = LocalDateTime.now();
                log.debug("【步骤5.X】事务内开始 - tenantId={}, orderNo={}", TENANT_ID, orderNo);

                // 锁定配送员资金明细 marker，确保并发幂等
                ShopDealerCapital existedMarker = shopDealerCapitalService.getOne(
                    new LambdaQueryWrapper<ShopDealerCapital>()
                        .eq(ShopDealerCapital::getTenantId, TENANT_ID)
                        .eq(ShopDealerCapital::getFlowType, FLOW_TYPE_DELIVERY_REWARD)
                        .eq(ShopDealerCapital::getOrderNo, orderNo)
                        .last("limit 1 for update")
                );
                if (existedMarker != null) {
                    log.debug("【步骤5.X】配送奖励跳过：事务内检测已存在 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
                    return false;
                }

                Integer orderId = order.getOrderId();
                if (orderId == null) {
                    log.debug("【步骤5.X】配送奖励跳过：orderId为空 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
                    return false;
                }

                List<ShopOrderGoods> orderGoodsList = shopOrderGoodsService.list(
                    new LambdaQueryWrapper<ShopOrderGoods>()
                        .eq(ShopOrderGoods::getTenantId, TENANT_ID)
                        .eq(ShopOrderGoods::getOrderId, orderId)
                );
                if (orderGoodsList == null || orderGoodsList.isEmpty()) {
                    log.debug("【步骤5.X】配送奖励跳过：订单商品为空 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
                    return false;
                }

                List<Integer> goodsIds = orderGoodsList.stream()
                    .map(ShopOrderGoods::getGoodsId)
                    .filter(Objects::nonNull)
                    .distinct()
                    .toList();
                if (goodsIds.isEmpty()) {
                    log.debug("【步骤5.X】配送奖励跳过：商品ID列表为空 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
                    return false;
                }

                Map<Integer, BigDecimal> goodsDeliveryMoneyMap = shopGoodsService.list(
                    new LambdaQueryWrapper<ShopGoods>()
                        .eq(ShopGoods::getTenantId, TENANT_ID)
                        .in(ShopGoods::getGoodsId, goodsIds)
                ).stream().collect(java.util.stream.Collectors.toMap(
                    ShopGoods::getGoodsId,
                    g -> g.getDeliveryMoney() != null ? g.getDeliveryMoney() : BigDecimal.ZERO,
                    (a, b) -> a
                ));

                BigDecimal reward = BigDecimal.ZERO;
                for (ShopOrderGoods og : orderGoodsList) {
                    Integer goodsId = og.getGoodsId();
                    if (goodsId == null) {
                        continue;
                    }
                    int qty = og.getTotalNum() == null ? 0 : og.getTotalNum();
                    if (qty <= 0) {
                        continue;
                    }
                    BigDecimal rawRate = goodsDeliveryMoneyMap.getOrDefault(goodsId, BigDecimal.ZERO);
                    BigDecimal rate = normalizeDeliveryRate(rawRate);
                    if (rate == null || rate.signum() <= 0) {
                        continue;
                    }
                    BigDecimal unitPrice = og.getPrice() != null ? og.getPrice() : BigDecimal.ZERO;
                    if (unitPrice.signum() <= 0) {
                        continue;
                    }
                    BigDecimal lineAmount = unitPrice.multiply(BigDecimal.valueOf(qty));
                    reward = reward.add(lineAmount.multiply(rate));
                }

                reward = reward.setScale(2, RoundingMode.HALF_UP);
                if (reward.signum() <= 0) {
                    log.debug("【步骤5.X】配送奖励跳过：计算金额为0 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
                    return false;
                }
                log.debug("【步骤5.X】计算配送奖励 - tenantId={}, orderNo={}, reward={}", TENANT_ID, orderNo, reward);

                // 锁定/创建配送员分销账户
                ShopDealerUser dealerUser = shopDealerUserService.getOne(
                    new LambdaQueryWrapper<ShopDealerUser>()
                        .eq(ShopDealerUser::getTenantId, TENANT_ID)
                        .eq(ShopDealerUser::getUserId, riderId)
                        .last("limit 1 for update")
                );
                if (dealerUser == null) {
                    log.info("【步骤5.X】创建配送员分销账户 - tenantId={}, orderNo={}, riderId={}", TENANT_ID, orderNo, riderId);
                    ShopDealerUser newDealerUser = new ShopDealerUser();
                    newDealerUser.setTenantId(TENANT_ID);
                    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);
                    newDealerUser.setCreateTime(now);
                    newDealerUser.setUpdateTime(now);
                    shopDealerUserService.save(newDealerUser);

                    dealerUser = shopDealerUserService.getOne(
                        new LambdaQueryWrapper<ShopDealerUser>()
                            .eq(ShopDealerUser::getTenantId, TENANT_ID)
                            .eq(ShopDealerUser::getUserId, riderId)
                            .last("limit 1 for update")
                    );
                    if (dealerUser == null) {
                        log.warn("配送奖励入账失败：未找到/创建分销账户 - tenantId={}, orderNo={}, riderId={}", TENANT_ID, orderNo, riderId);
                        return false;
                    }
                }

                BigDecimal moneyVal = dealerUser.getMoney() != null ? dealerUser.getMoney() : BigDecimal.ZERO;
                BigDecimal totalMoneyVal = dealerUser.getTotalMoney() != null ? dealerUser.getTotalMoney() : BigDecimal.ZERO;
                dealerUser.setMoney(moneyVal.add(reward));
                dealerUser.setTotalMoney(totalMoneyVal.add(reward));
                dealerUser.setUpdateTime(now);
                if (!shopDealerUserService.updateById(dealerUser)) {
                    log.warn("配送奖励入账失败：更新分销账户失败 - tenantId={}, orderNo={}, riderId={}, reward={}", TENANT_ID, orderNo, riderId, reward);
                    return false;
                }

                ShopDealerCapital cap = new ShopDealerCapital();
                cap.setUserId(riderId);
                cap.setOrderNo(orderNo);
                cap.setFlowType(FLOW_TYPE_DELIVERY_REWARD);
                cap.setMoney(reward);
                cap.setComments("配送奖励");
                cap.setToUserId(order.getUserId());
                cap.setMonth(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM")));
                cap.setTenantId(TENANT_ID);
                cap.setCreateTime(now);
                cap.setUpdateTime(now);
                shopDealerCapitalService.save(cap);

                log.info("【步骤5.X】配送奖励发放成功 - tenantId={}, orderNo={}, riderId={}, reward={}", TENANT_ID, orderNo, riderId, reward);
                return true;
            }));
        } catch (Exception e) {
            log.error("【步骤5.X】配送奖励发放异常 - tenantId={}, orderNo={}, error={}", TENANT_ID, orderNo, e.getMessage(), e);
            return false;
        }
    }

    private Set<Integer> loadWaterFormIds() {
        return gltTicketTemplateService.list(
                new LambdaQueryWrapper<GltTicketTemplate>()
                    .eq(GltTicketTemplate::getTenantId, TENANT_ID)
                    .eq(GltTicketTemplate::getDeleted, 0)
                    .isNotNull(GltTicketTemplate::getGoodsId)
        ).stream()
            .map(GltTicketTemplate::getGoodsId)
            .collect(java.util.stream.Collectors.toSet());
    }

    private List<String> findEligibleNonWaterOrderNos(Set<Integer> waterFormIds, boolean newestFirst) {
        LambdaQueryWrapper<ShopOrder> qw = new LambdaQueryWrapper<ShopOrder>()
            .eq(ShopOrder::getTenantId, TENANT_ID)
            .eq(ShopOrder::getDeleted, 0)
            .eq(ShopOrder::getPayStatus, true)
            .eq(ShopOrder::getOrderStatus, ORDER_STATUS_CONFIRMED_RECEIVE)
            .and(w -> w.notIn(ShopOrder::getFormId, waterFormIds).or().isNull(ShopOrder::getFormId))
            .isNotNull(ShopOrder::getOrderNo);

        if (newestFirst) {
            qw.orderByDesc(ShopOrder::getUpdateTime).orderByDesc(ShopOrder::getOrderId);
        } else {
            qw.orderByAsc(ShopOrder::getUpdateTime).orderByAsc(ShopOrder::getOrderId);
        }
        qw.last("limit " + MAX_ELIGIBLE_ORDER_NOS_PER_RUN);

        return shopOrderService.list(qw).stream()
            .map(ShopOrder::getOrderNo)
            .filter(s -> s != null && !s.isBlank())
            .toList();
    }

    private Set<String> findEligibleWaterOrderNosByFirstFinishedTicketOrder(Set<Integer> waterFormIds) {
        // 缓存减少 DB 往返：userTicketId -> 是否"第一次送水单已完成"
        Map<Integer, Boolean> firstFinishedCache = new HashMap<>();
        Map<Integer, String> userTicketOrderNoCache = new HashMap<>();

        log.info("【步骤3.1】查询已完成的送水订单 - tenantId={}, limit={}", TENANT_ID, MAX_ELIGIBLE_TICKET_ORDERS_PER_RUN);
        List<GltTicketOrder> finishedTicketOrders = gltTicketOrderService.list(
            new LambdaQueryWrapper<GltTicketOrder>()
                .eq(GltTicketOrder::getTenantId, TENANT_ID)
                .eq(GltTicketOrder::getDeleted, 0)
                .eq(GltTicketOrder::getDeliveryStatus, GltTicketOrderService.DELIVERY_STATUS_FINISHED)
                .isNotNull(GltTicketOrder::getUserTicketId)
                .orderByDesc(GltTicketOrder::getId)
                .last("limit " + MAX_ELIGIBLE_TICKET_ORDERS_PER_RUN)
        );
        log.info("【步骤3.1】查到已完成的送水订单数量 - tenantId={}, count={}", TENANT_ID, finishedTicketOrders.size());

        Set<String> orderNos = new HashSet<>();
        int checked = 0, skippedNull = 0, skippedNotFirst = 0, skippedNoOrderNo = 0, skippedNotWater = 0;
        for (GltTicketOrder ticketOrder : finishedTicketOrders) {
            Integer userTicketId = ticketOrder.getUserTicketId();
            checked++;
            if (userTicketId == null) {
                skippedNull++;
                continue;
            }

            boolean firstFinished = firstFinishedCache.computeIfAbsent(userTicketId, id -> isFirstTicketOrderFinished(id));
            if (!firstFinished) {
                skippedNotFirst++;
                continue;
            }

            String orderNo = userTicketOrderNoCache.computeIfAbsent(userTicketId, id -> findOrderNoByUserTicketId(id));
            if (orderNo == null || orderNo.isBlank()) {
                skippedNoOrderNo++;
                continue;
            }

            // 再校验一次确实是送水套餐订单，避免误关联
            ShopOrder shopOrder = shopOrderService.getOne(
                new LambdaQueryWrapper<ShopOrder>()
                    .eq(ShopOrder::getTenantId, TENANT_ID)
                    .eq(ShopOrder::getDeleted, 0)
                    .eq(ShopOrder::getPayStatus, true)
                    .eq(ShopOrder::getOrderNo, orderNo)
                    .last("limit 1")
            );
            if (shopOrder == null || shopOrder.getFormId() == null || !waterFormIds.contains(shopOrder.getFormId())) {
                skippedNotWater++;
                continue;
            }

            orderNos.add(orderNo);
            if (orderNos.size() >= MAX_ELIGIBLE_ORDER_NOS_PER_RUN) {
                break;
            }
        }
        log.info("【步骤3.2】送水订单筛选完成 - tenantId={}, checked={}, skippedNull={}, skippedNotFirst={}, skippedNoOrderNo={}, skippedNotWater={}, matched={}, orderNos={}",
            TENANT_ID, checked, skippedNull, skippedNotFirst, skippedNoOrderNo, skippedNotWater, orderNos.size(), orderNos);
        return orderNos;
    }

    private boolean isFirstTicketOrderFinished(Integer userTicketId) {
        if (userTicketId == null) {
            return false;
        }
        GltTicketOrder first = gltTicketOrderService.getOne(
            new LambdaQueryWrapper<GltTicketOrder>()
                .eq(GltTicketOrder::getTenantId, TENANT_ID)
                .eq(GltTicketOrder::getDeleted, 0)
                .eq(GltTicketOrder::getUserTicketId, userTicketId)
                .orderByAsc(GltTicketOrder::getId)
                .last("limit 1")
        );
        return first != null && first.getDeliveryStatus() != null && first.getDeliveryStatus() == GltTicketOrderService.DELIVERY_STATUS_FINISHED;
    }

    private String findOrderNoByUserTicketId(Integer userTicketId) {
        if (userTicketId == null) {
            return null;
        }
        GltUserTicket userTicket = gltUserTicketService.getOne(
            new LambdaQueryWrapper<GltUserTicket>()
                .eq(GltUserTicket::getTenantId, TENANT_ID)
                .eq(GltUserTicket::getDeleted, 0)
                .eq(GltUserTicket::getId, userTicketId)
                .last("limit 1")
        );
        return userTicket != null ? userTicket.getOrderNo() : null;
    }

    private List<ShopDealerCapital> findCapitalsByEligibleOrderNos(Set<String> eligibleOrderNos) {
        if (eligibleOrderNos == null || eligibleOrderNos.isEmpty()) {
            return List.of();
        }
        // 先按 eligibleOrderNos 集合的顺序（最新订单优先）处理佣金
        // 注意：MyBatis-Plus 的 in 查询会保持 in() 列表的顺序
        List<ShopDealerCapital> capitals = shopDealerCapitalService.list(
            new LambdaQueryWrapper<ShopDealerCapital>()
                .eq(ShopDealerCapital::getTenantId, TENANT_ID)
                .eq(ShopDealerCapital::getFlowType, 10)
                .in(ShopDealerCapital::getOrderNo, eligibleOrderNos)
                .isNotNull(ShopDealerCapital::getOrderNo)
                .orderByAsc(ShopDealerCapital::getId)
                .last("limit " + MAX_CAPITALS_PER_RUN)
        );
        // 按 eligibleOrderNos 的顺序重新排序（最新订单优先）
        List<String> orderList = List.copyOf(eligibleOrderNos);
        capitals.sort((a, b) -> {
            int idxA = orderList.indexOf(a.getOrderNo());
            int idxB = orderList.indexOf(b.getOrderNo());
            return Integer.compare(idxA, idxB);
        });
        return capitals;
    }

    private boolean unfreezeOneCapitalIfNeeded(ShopDealerCapital cap) {
        if (cap == null) {
            return false;
        }
        Integer capitalId = cap.getId();
        Integer dealerUserId = cap.getUserId();
        String orderNo = cap.getOrderNo();
        BigDecimal amount = cap.getMoney();
        if (capitalId == null || dealerUserId == null || orderNo == null || orderNo.isBlank() || amount == null || amount.signum() <= 0) {
            return false;
        }

        String markerComment = buildUnfreezeMarkerComment(capitalId);

        // 快速幂等检查（避免每条都进事务）
        boolean already = shopDealerCapitalService.count(
            new LambdaQueryWrapper<ShopDealerCapital>()
                .eq(ShopDealerCapital::getTenantId, TENANT_ID)
                .eq(ShopDealerCapital::getFlowType, 50)
                .eq(ShopDealerCapital::getUserId, dealerUserId)
                .eq(ShopDealerCapital::getOrderNo, orderNo)
                .eq(ShopDealerCapital::getComments, markerComment)
        ) > 0;
        if (already) {
            return false;
        }

        return Boolean.TRUE.equals(transactionTemplate.execute(status -> {
            LocalDateTime now = LocalDateTime.now();
            // 锁定分销商账户行，避免多实例并发导致同一条佣金重复解冻。
            ShopDealerUser dealerUser = shopDealerUserService.getOne(
                new LambdaQueryWrapper<ShopDealerUser>()
                    .eq(ShopDealerUser::getTenantId, TENANT_ID)
                    .eq(ShopDealerUser::getUserId, dealerUserId)
                    .last("limit 1 for update")
            );
            if (dealerUser == null) {
                log.warn("解冻失败：未找到分销账户 - tenantId={}, orderNo={}, dealerUserId={}, amount={}",
                    TENANT_ID, orderNo, dealerUserId, amount);
                return false;
            }

            // RR 隔离级别下，先锁 user 行，再用锁定读检查 marker，避免"读到旧快照"导致重复解冻。
            ShopDealerCapital existedMarker = shopDealerCapitalService.getOne(
                new LambdaQueryWrapper<ShopDealerCapital>()
                    .eq(ShopDealerCapital::getTenantId, TENANT_ID)
                    .eq(ShopDealerCapital::getFlowType, 50)
                    .eq(ShopDealerCapital::getUserId, dealerUserId)
                    .eq(ShopDealerCapital::getOrderNo, orderNo)
                    .eq(ShopDealerCapital::getComments, markerComment)
                    .last("limit 1 for update")
            );
            if (existedMarker != null) {
                return false;
            }

            BigDecimal freezeMoney = dealerUser.getFreezeMoney() != null ? dealerUser.getFreezeMoney() : BigDecimal.ZERO;
            if (freezeMoney.compareTo(amount) < 0) {
                log.warn("解冻失败：冻结金额不足 - tenantId={}, orderNo={}, dealerUserId={}, freezeMoney={}, amount={}",
                    TENANT_ID, orderNo, dealerUserId, freezeMoney, amount);
                return false;
            }

            BigDecimal moneyVal = dealerUser.getMoney() != null ? dealerUser.getMoney() : BigDecimal.ZERO;
            dealerUser.setFreezeMoney(freezeMoney.subtract(amount));
            dealerUser.setMoney(moneyVal.add(amount));
            dealerUser.setUpdateTime(now);
            if (!shopDealerUserService.updateById(dealerUser)) {
                log.warn("解冻失败：更新分销账户失败 - tenantId={}, orderNo={}, dealerUserId={}, amount={}",
                    TENANT_ID, orderNo, dealerUserId, amount);
                return false;
            }

            ShopDealerCapital marker = new ShopDealerCapital();
            marker.setUserId(dealerUserId);
            marker.setOrderNo(orderNo);
            marker.setFlowType(50);
            marker.setMoney(amount);
            marker.setComments(markerComment);
            marker.setToUserId(cap.getToUserId());
            marker.setMonth(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM")));
            marker.setTenantId(TENANT_ID);
            marker.setCreateTime(now);
            marker.setUpdateTime(now);
            shopDealerCapitalService.save(marker);

            // 佣金全部解冻完成后，将分销订单状态置为"已解冻"(0)。
            // 以当前任务生成的 flowType=50 marker 数量作为完成度判断，避免提前将订单置为已解冻。
            setDealerOrderUnfrozenIfCompleted(orderNo, now);

            log.info("佣金解冻成功 - tenantId={}, orderNo={}, dealerUserId={}, amount={}, capitalId={}",
                TENANT_ID, orderNo, dealerUserId, amount, capitalId);
            return true;
        }));
    }

    private void setDealerOrderUnfrozenIfCompleted(String orderNo, LocalDateTime now) {
        if (orderNo == null || orderNo.isBlank()) {
            return;
        }

        long totalCommissions = shopDealerCapitalService.count(
            new LambdaQueryWrapper<ShopDealerCapital>()
                .eq(ShopDealerCapital::getTenantId, TENANT_ID)
                .eq(ShopDealerCapital::getFlowType, 10)
                .eq(ShopDealerCapital::getOrderNo, orderNo)
        );
        if (totalCommissions <= 0) {
            return;
        }

        long unfrozenMarkers = shopDealerCapitalService.count(
            new LambdaQueryWrapper<ShopDealerCapital>()
                .eq(ShopDealerCapital::getTenantId, TENANT_ID)
                .eq(ShopDealerCapital::getFlowType, 50)
                .eq(ShopDealerCapital::getOrderNo, orderNo)
                .like(ShopDealerCapital::getComments, "佣金解冻(capitalId=")
        );

        if (unfrozenMarkers < totalCommissions) {
            return;
        }

        boolean updated = shopDealerOrderService.update(
            new LambdaUpdateWrapper<ShopDealerOrder>()
                .eq(ShopDealerOrder::getTenantId, TENANT_ID)
                .eq(ShopDealerOrder::getOrderNo, orderNo)
                .set(ShopDealerOrder::getIsUnfreeze, 1)
                .set(ShopDealerOrder::getUnfreezeTime, now)
                .set(ShopDealerOrder::getUpdateTime, now)
        );
        if (!updated) {
            log.warn("已完成佣金解冻，但更新分销订单isUnfreeze失败/无记录 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
        }
    }

    private String buildUnfreezeMarkerComment(Integer capitalId) {
        return "佣金解冻(capitalId=" + capitalId + ")";
    }
}
