package com.ruoyi.app.order;

import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.MessageUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.util.concurrent.TimeUnit;

/** 串行化同一骑手跨外卖和闪送的接单检查与订单更新。 */
@Service
public class RiderDeliveryLockService {
    private static final Logger log = LoggerFactory.getLogger(RiderDeliveryLockService.class);
    private static final String PREFIX = "lock:delivery:rider:";
    private static final long LOCK_WAIT_SECONDS = 3L;

    private final RedissonClient redissonClient;

    public RiderDeliveryLockService(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    public <T> T withLock(Long riderId, LockedCall<T> call) {
        if (riderId == null) throw new IllegalArgumentException("riderId is required");
        RLock lock;
        boolean acquired;
        try {
            lock = redissonClient.getLock(PREFIX + riderId);
            // 不指定 leaseTime，由 Redisson watchdog 续期到事务完成。
            acquired = lock.tryLock(LOCK_WAIT_SECONDS, TimeUnit.SECONDS);
        } catch (InterruptedException interrupted) {
            Thread.currentThread().interrupt();
            throw new ServiceException(MessageUtils.message("no.operation.interrupted.try.again"));
        } catch (RuntimeException redisFailure) {
            log.error("Failed to acquire rider delivery lock, riderId={}", riderId, redisFailure);
            throw new ServiceException(MessageUtils.message("no.system.busy.try.again"));
        }
        if (!acquired) {
            log.warn("Rider delivery lock not acquired within {}s, riderId={}", LOCK_WAIT_SECONDS, riderId);
            throw new ServiceException(MessageUtils.message("no.system.busy.try.again"));
        }

        boolean deferredUnlock = false;
        try {
            T result = call.call();
            if (TransactionSynchronizationManager.isSynchronizationActive()) {
                TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                    @Override
                    public void afterCompletion(int status) {
                        if (lock.isHeldByCurrentThread()) lock.unlock();
                    }
                });
                deferredUnlock = true;
            }
            return result;
        } finally {
            if (!deferredUnlock && lock.isHeldByCurrentThread()) lock.unlock();
        }
    }

    @FunctionalInterface
    public interface LockedCall<T> {
        T call();
    }
}
