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.ShopOrder;
import com.gxwebsoft.shop.service.ShopDealerCapitalService;
import com.gxwebsoft.shop.service.ShopDealerOrderService;
import com.gxwebsoft.shop.service.ShopDealerUserService;
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.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
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;

    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    private ShopOrderService shopOrderService;

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

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

            // 先按“最近确认收货”的订单扫描，避免总是卡在很早的历史订单上。
            Set<String> eligibleOrderNos = new HashSet<>();
            eligibleOrderNos.addAll(findEligibleNonWaterOrderNos(waterFormIds, true));
            eligibleOrderNos.addAll(findEligibleWaterOrderNosByFirstFinishedTicketOrder(waterFormIds));

            if (eligibleOrderNos.isEmpty()) {
                return;
            }

            List<ShopDealerCapital> capitals = findCapitalsByEligibleOrderNos(eligibleOrderNos);
            if (capitals.isEmpty()) {
                // 若本轮没有取到佣金明细，回退再按“最早确认收货”的订单扫一轮，尽量覆盖历史遗留未解冻。
                eligibleOrderNos.clear();
                eligibleOrderNos.addAll(findEligibleNonWaterOrderNos(waterFormIds, false));
                eligibleOrderNos.addAll(findEligibleWaterOrderNosByFirstFinishedTicketOrder(waterFormIds));
                capitals = findCapitalsByEligibleOrderNos(eligibleOrderNos);
            }

            if (capitals.isEmpty()) {
                return;
            }

            int unfrozen = 0;
            for (ShopDealerCapital cap : capitals) {
                try {
                    boolean ok = unfreezeOneCapitalIfNeeded(cap);
                    if (ok) {
                        unfrozen++;
                    }
                } 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);
                }
            }

            if (unfrozen > 0) {
                log.info("分销佣金解冻完成 - tenantId={}, eligibleOrderNos={}, scannedCapitals={}, unfrozen={}",
                    TENANT_ID, eligibleOrderNos.size(), capitals.size(), unfrozen);
            }
        } finally {
            running.set(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<>();

        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)
        );

        Set<String> orderNos = new HashSet<>();
        for (GltTicketOrder ticketOrder : finishedTicketOrders) {
            Integer userTicketId = ticketOrder.getUserTicketId();
            if (userTicketId == null) {
                continue;
            }

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

            String orderNo = userTicketOrderNoCache.computeIfAbsent(userTicketId, id -> findOrderNoByUserTicketId(id));
            if (orderNo == null || orderNo.isBlank()) {
                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())) {
                continue;
            }

            orderNos.add(orderNo);
            if (orderNos.size() >= MAX_ELIGIBLE_ORDER_NOS_PER_RUN) {
                break;
            }
        }
        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();
        }
        return 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)
        );
    }

    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 + ")";
    }
}
