package com.seer.teach.pay.service.impl;

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.seer.teach.common.constants.CommonConstant;
import com.seer.teach.common.enums.ResultCodeEnum;
import com.seer.teach.common.exception.CommonException;
import com.seer.teach.common.utils.RedisDistributedLock;
import com.seer.teach.pay.entity.PayMerchantAccountEntity;
import com.seer.teach.pay.mapper.PayMerchantAccountMapper;
import com.seer.teach.pay.service.IPayMerchantAccountService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 用户可提现余额表 服务实现类
 * </p>
 *
 * @author ZS
 * @since 2025-09-22
 */
@RequiredArgsConstructor
@Slf4j
@Service
public class PayMerchantAccountServiceImpl extends ServiceImpl<PayMerchantAccountMapper, PayMerchantAccountEntity> implements IPayMerchantAccountService {

    private static final String USER_WITHDRAWAL_BALANCE_STATUS_KEY_PREFIX = "lock:user:account:balance:";

    private final RedisDistributedLock distributedLock;
    private static final long LOCK_EXPIRE = 5;
    private static final long LOCK_WAIT = 2;

    @Override
    public PayMerchantAccountEntity getMyAccount(Long userId) {
        PayMerchantAccountEntity withdrawalBalance = super.getOne(new LambdaQueryWrapper<>(PayMerchantAccountEntity.class).eq(PayMerchantAccountEntity::getUserId, userId));
        if(Objects.isNull( withdrawalBalance)){
            withdrawalBalance = this.initUserWithdrawalBalance(userId);
        }
        return withdrawalBalance;
    }

    @Override
    public PayMerchantAccountEntity initUserWithdrawalBalance(Long userId) {
        PayMerchantAccountEntity withdrawalBalance = new PayMerchantAccountEntity();
        withdrawalBalance.setUserId(userId);
        withdrawalBalance.setAvailableBalance(BigDecimal.ZERO);
        withdrawalBalance.setFrozenBalance(BigDecimal.ZERO);
        withdrawalBalance.setTotalSettledAmount(BigDecimal.ZERO);
        withdrawalBalance.setTotalAmount(BigDecimal.ZERO);
        withdrawalBalance.setVersion(0);
        withdrawalBalance.setIsBlock(CommonConstant.ACCOUNT_UNLOCKED);
        boolean result = super.save(withdrawalBalance);
        log.info("save PayMerchantAccountEntity result:{}", result);
        return withdrawalBalance;
    }

    @Override
    public boolean releaseFrozenToAvailable(Long userId, BigDecimal amount) {
        PayMerchantAccountEntity withdrawalBalance = this.getMyAccount(userId);
        Integer withdrawalBalanceId = withdrawalBalance.getId();
        String lockKey = USER_WITHDRAWAL_BALANCE_STATUS_KEY_PREFIX + userId + ":" + withdrawalBalanceId;
        String lockValue = null;
        try {
            lockValue = acquireLock(lockKey);
            if (lockValue == null) {
                log.info("用户 {} 的解冻资金获取锁失败,redis key :{}", userId,lockKey);
                throw new CommonException(ResultCodeEnum.USER_WITHDRAWAL_ACQUIRE_LOCK_ERROR);
            }
            boolean updated = super.update(new LambdaUpdateWrapper<>(PayMerchantAccountEntity.class)
                    .set(PayMerchantAccountEntity::getFrozenBalance, withdrawalBalance.getFrozenBalance().subtract(amount))
                    .set(PayMerchantAccountEntity::getAvailableBalance, withdrawalBalance.getAvailableBalance().add(amount))
                    .set(PayMerchantAccountEntity::getVersion, withdrawalBalance.getVersion() + 1)
                    .eq(PayMerchantAccountEntity::getId, withdrawalBalanceId)
                    .eq(PayMerchantAccountEntity::getVersion, withdrawalBalance.getVersion())
                    .eq(PayMerchantAccountEntity::getUserId, userId));
            log.info("解冻用户 {} 的资金结果: {}", userId, updated);
            return updated;
        } finally {
            if (lockValue != null) {
                distributedLock.unlock(lockKey, lockValue);
            }
        }
    }

    @Override
    public boolean decreaseBalance(Long userId, BigDecimal amount) {
        PayMerchantAccountEntity withdrawalBalance = this.getMyAccount(userId);
        Integer withdrawalBalanceId = withdrawalBalance.getId();
        String lockKey = USER_WITHDRAWAL_BALANCE_STATUS_KEY_PREFIX + userId + ":" + withdrawalBalanceId;
        String lockValue = null;
        try {
            lockValue = acquireLock(lockKey);
            if (lockValue == null) {
                log.info("扣减用户[{}]账户[{}]余额时获取锁失败,redis key :{}", userId,withdrawalBalanceId,lockKey);
                throw new CommonException(ResultCodeEnum.USER_WITHDRAWAL_ACQUIRE_LOCK_ERROR);
            }
            if (withdrawalBalance.getAvailableBalance().compareTo(amount) < 0) {
                log.info("用户[{}]账户[{}]余额不足,当前余额: {}, 扣减金额: {}", userId, withdrawalBalanceId, withdrawalBalance.getAvailableBalance(), amount);
                throw new CommonException(ResultCodeEnum.USER_NOT_ENOUGH_COIN);
            }
            boolean updated = super.update(new LambdaUpdateWrapper<>(PayMerchantAccountEntity.class)
                    .set(PayMerchantAccountEntity::getAvailableBalance, withdrawalBalance.getAvailableBalance().subtract(amount))
                    .set(PayMerchantAccountEntity::getTotalAmount, withdrawalBalance.getTotalAmount().subtract(amount))
                    .set(PayMerchantAccountEntity::getTotalSettledAmount, withdrawalBalance.getTotalSettledAmount().subtract(amount))
                    .set(PayMerchantAccountEntity::getVersion, withdrawalBalance.getVersion() + 1)
                    .eq(PayMerchantAccountEntity::getId, withdrawalBalanceId)
                    .eq(PayMerchantAccountEntity::getVersion, withdrawalBalance.getVersion())
                    .eq(PayMerchantAccountEntity::getUserId, userId));
            log.info("扣减用户[{}]账户[{}]余额的结果: {}", userId, withdrawalBalanceId, updated);
            return updated;
        } finally {
            if (lockValue != null) {
                distributedLock.unlock(lockKey, lockValue);
            }
        }
    }

    @Override
    public boolean increaseBalance(Long userId, BigDecimal amount) {
        PayMerchantAccountEntity withdrawalBalance = this.getMyAccount(userId);
        Integer withdrawalBalanceId = withdrawalBalance.getId();
        String lockKey = USER_WITHDRAWAL_BALANCE_STATUS_KEY_PREFIX + userId + ":" + withdrawalBalanceId;
        String lockValue = null;
        try {
            lockValue = acquireLock(lockKey);
            if (lockValue == null) {
                log.info("增加用户[{}]账户[{}]余额时获取锁失败,redis key :{}", userId,withdrawalBalanceId,lockKey);
                throw new CommonException(ResultCodeEnum.USER_WITHDRAWAL_ACQUIRE_LOCK_ERROR);
            }
            boolean updated = super.update(new LambdaUpdateWrapper<>(PayMerchantAccountEntity.class)
                    .set(PayMerchantAccountEntity::getAvailableBalance, withdrawalBalance.getAvailableBalance().add(amount))
                    .set(PayMerchantAccountEntity::getTotalAmount, withdrawalBalance.getTotalAmount().add(amount))
                    .set(PayMerchantAccountEntity::getTotalSettledAmount, withdrawalBalance.getTotalSettledAmount().add(amount))
                    .set(PayMerchantAccountEntity::getVersion, withdrawalBalance.getVersion() + 1)
                    .eq(PayMerchantAccountEntity::getId, withdrawalBalanceId)
                    .eq(PayMerchantAccountEntity::getVersion, withdrawalBalance.getVersion())
                    .eq(PayMerchantAccountEntity::getUserId, userId));
            log.info("增加用户[{}]账户[{}]余额的结果: {}", userId, withdrawalBalanceId, updated);
            return updated;
        } finally {
            if (lockValue != null) {
                distributedLock.unlock(lockKey, lockValue);
            }
        }
    }

    @Override
    public boolean batchIncreaseMerchantFrozenFunds(Map<Long, BigDecimal> merchantAmountMap) {
        boolean allSuccess = true;
        for (Map.Entry<Long, BigDecimal> entry : merchantAmountMap.entrySet()) {
            Long merchantId = entry.getKey();
            BigDecimal amount = entry.getValue();
            boolean result = increaseFrozenBalance(merchantId, amount);
            if (!result) {
                log.error("批量增加冻结资金失败，商户ID: {}, 金额: {}", merchantId, amount);
                allSuccess = false;
            }
        }
        return allSuccess;
    }
    
    @Override
    public boolean batchDecreaseFrozenBalance(Map<Long, BigDecimal> merchantAmountMap) {
        boolean allSuccess = true;
        for (Map.Entry<Long, BigDecimal> entry : merchantAmountMap.entrySet()) {
            Long merchantId = entry.getKey();
            BigDecimal amount = entry.getValue();
            boolean result = decreaseFrozenBalance(merchantId, amount);
            if (!result) {
                log.error("批量扣减冻结余额失败，商户ID: {}, 金额: {}", merchantId, amount);
                allSuccess = false;
            }
        }
        return allSuccess;
    }
    
    /**
     * 增加账户冻结余额
     *
     * @param userId 用户ID
     * @param amount 增加的金额
     * @return true：成功，false：失败
     */
    public boolean increaseFrozenBalance(Long userId, BigDecimal amount) {
        PayMerchantAccountEntity withdrawalBalance = this.getMyAccount(userId);
        Integer withdrawalBalanceId = withdrawalBalance.getId();
        String lockKey = USER_WITHDRAWAL_BALANCE_STATUS_KEY_PREFIX + userId + ":" + withdrawalBalanceId;
        String lockValue = null;
        try {
            lockValue = acquireLock(lockKey);
            if (lockValue == null) {
                log.info("用户 {} 的增加冻结余额获取锁失败,redis key :{}", userId,lockKey);
                throw new CommonException(ResultCodeEnum.USER_WITHDRAWAL_ACQUIRE_LOCK_ERROR);
            }
            boolean updated = super.update(new LambdaUpdateWrapper<>(PayMerchantAccountEntity.class)
                    .set(PayMerchantAccountEntity::getFrozenBalance, withdrawalBalance.getFrozenBalance().add(amount))
                    .set(PayMerchantAccountEntity::getTotalAmount, withdrawalBalance.getTotalAmount().add(amount))
                    .set(PayMerchantAccountEntity::getVersion, withdrawalBalance.getVersion() + 1)
                    .eq(PayMerchantAccountEntity::getId, withdrawalBalanceId)
                    .eq(PayMerchantAccountEntity::getVersion, withdrawalBalance.getVersion())
                    .eq(PayMerchantAccountEntity::getUserId, userId));
            log.info("更新用户 {} 的提现冻结余额表 ID: {} 成功: {}", userId, withdrawalBalanceId, updated);
            return updated;
        } finally {
            if (lockValue != null) {
                distributedLock.unlock(lockKey, lockValue);
            }
        }
    }
    
    /**
     * 扣减账户冻结余额
     *
     * @param userId 用户ID
     * @param amount 扣减的金额
     * @return true：成功，false：失败
     */
    public boolean decreaseFrozenBalance(Long userId, BigDecimal amount) {
        PayMerchantAccountEntity withdrawalBalance = this.getMyAccount(userId);
        Integer withdrawalBalanceId = withdrawalBalance.getId();
        String lockKey = USER_WITHDRAWAL_BALANCE_STATUS_KEY_PREFIX + userId + ":" + withdrawalBalanceId;
        String lockValue = null;
        try {
            lockValue = acquireLock(lockKey);
            if (lockValue == null) {
                log.info("扣减用户[{}]账户[{}]冻结余额时获取锁失败,redis key :{}", userId,withdrawalBalanceId,lockKey);
                throw new CommonException(ResultCodeEnum.USER_WITHDRAWAL_ACQUIRE_LOCK_ERROR);
            }
            if (withdrawalBalance.getFrozenBalance().compareTo(amount) < 0) {
                log.info("用户[{}]账户[{}]冻结余额不足,当前余额: {}, 扣减金额: {}", userId, withdrawalBalanceId, withdrawalBalance.getFrozenBalance(), amount);
                throw new CommonException(ResultCodeEnum.USER_NOT_ENOUGH_COIN);
            }
            boolean updated = super.update(new LambdaUpdateWrapper<>(PayMerchantAccountEntity.class)
                    .set(PayMerchantAccountEntity::getFrozenBalance, withdrawalBalance.getFrozenBalance().subtract(amount))
                    .set(PayMerchantAccountEntity::getTotalAmount, withdrawalBalance.getTotalAmount().subtract(amount))
                    .set(PayMerchantAccountEntity::getVersion, withdrawalBalance.getVersion() + 1)
                    .eq(PayMerchantAccountEntity::getId, withdrawalBalanceId)
                    .eq(PayMerchantAccountEntity::getVersion, withdrawalBalance.getVersion())
                    .eq(PayMerchantAccountEntity::getUserId, userId));
            log.info("扣减用户[{}]账户[{}]冻结余额的结果: {}", userId, withdrawalBalanceId, updated);
            return updated;
        } finally {
            if (lockValue != null) {
                distributedLock.unlock(lockKey, lockValue);
            }
        }
    }
    
    @Override
    public boolean freezeBalanceForWithdrawal(Long userId, BigDecimal amount) {
        PayMerchantAccountEntity withdrawalBalance = this.getMyAccount(userId);
        Integer withdrawalBalanceId = withdrawalBalance.getId();
        String lockKey = USER_WITHDRAWAL_BALANCE_STATUS_KEY_PREFIX + userId + ":" + withdrawalBalanceId;
        String lockValue = null;
        try {
            lockValue = acquireLock(lockKey);
            if (lockValue == null) {
                log.info("用户 {} 的冻结资金获取锁失败,redis key :{}", userId,lockKey);
                throw new CommonException(ResultCodeEnum.USER_WITHDRAWAL_ACQUIRE_LOCK_ERROR);
            }
            
            // 检查可用余额是否足够
            if (withdrawalBalance.getAvailableBalance().compareTo(amount) < 0) {
                log.info("用户[{}]账户[{}]可用余额不足,当前余额: {}, 冻结金额: {}", userId, withdrawalBalanceId, withdrawalBalance.getAvailableBalance(), amount);
                throw new CommonException(ResultCodeEnum.USER_NOT_ENOUGH_COIN);
            }
            
            // 减少可用余额，增加冻结余额
            boolean updated = super.update(new LambdaUpdateWrapper<>(PayMerchantAccountEntity.class)
                    .set(PayMerchantAccountEntity::getAvailableBalance, withdrawalBalance.getAvailableBalance().subtract(amount))
                    .set(PayMerchantAccountEntity::getFrozenBalance, withdrawalBalance.getFrozenBalance().add(amount))
                    .set(PayMerchantAccountEntity::getTotalWithdrawnAmount, withdrawalBalance.getTotalWithdrawnAmount().add(amount))
                    .set(PayMerchantAccountEntity::getVersion, withdrawalBalance.getVersion() + 1)
                    .eq(PayMerchantAccountEntity::getId, withdrawalBalanceId)
                    .eq(PayMerchantAccountEntity::getVersion, withdrawalBalance.getVersion())
                    .eq(PayMerchantAccountEntity::getUserId, userId));
            log.info("冻结用户[{}]账户[{}]资金的结果: {}", userId, withdrawalBalanceId, updated);
            return updated;
        } finally {
            if (lockValue != null) {
                distributedLock.unlock(lockKey, lockValue);
            }
        }
    }

    @Override
    public boolean unfreezeBalanceForWithdrawalReject(Long userId, BigDecimal amount) {
        PayMerchantAccountEntity withdrawalBalance = this.getMyAccount(userId);
        Integer withdrawalBalanceId = withdrawalBalance.getId();
        String lockKey = USER_WITHDRAWAL_BALANCE_STATUS_KEY_PREFIX + userId + ":" + withdrawalBalanceId;
        String lockValue = null;
        try {
            lockValue = acquireLock(lockKey);
            if (lockValue == null) {
                log.info("用户 {} 的解冻资金获取锁失败,redis key :{}", userId,lockKey);
                throw new CommonException(ResultCodeEnum.USER_WITHDRAWAL_ACQUIRE_LOCK_ERROR);
            }
            
            // 检查冻结余额是否足够
            if (withdrawalBalance.getFrozenBalance().compareTo(amount) < 0) {
                log.info("用户[{}]账户[{}]冻结余额不足,当前余额: {}, 解冻金额: {}", userId, withdrawalBalanceId, withdrawalBalance.getFrozenBalance(), amount);
                throw new CommonException(ResultCodeEnum.USER_NOT_ENOUGH_COIN);
            }
            
            // 增加可用余额，减少冻结余额
            boolean updated = super.update(new LambdaUpdateWrapper<>(PayMerchantAccountEntity.class)
                    .set(PayMerchantAccountEntity::getAvailableBalance, withdrawalBalance.getAvailableBalance().add(amount))
                    .set(PayMerchantAccountEntity::getFrozenBalance, withdrawalBalance.getFrozenBalance().subtract(amount))
                    .set(PayMerchantAccountEntity::getTotalWithdrawnAmount, withdrawalBalance.getTotalWithdrawnAmount().subtract(amount))
                    .set(PayMerchantAccountEntity::getVersion, withdrawalBalance.getVersion() + 1)
                    .eq(PayMerchantAccountEntity::getId, withdrawalBalanceId)
                    .eq(PayMerchantAccountEntity::getVersion, withdrawalBalance.getVersion())
                    .eq(PayMerchantAccountEntity::getUserId, userId));
            log.info("解冻用户[{}]账户[{}]资金的结果: {}", userId, withdrawalBalanceId, updated);
            return updated;
        } finally {
            if (lockValue != null) {
                distributedLock.unlock(lockKey, lockValue);
            }
        }
    }

    private String acquireLock(String lockKey) {
        long start = System.currentTimeMillis();
        long waitTimeMillis = TimeUnit.SECONDS.toMillis(LOCK_WAIT);
        long endTime = start + waitTimeMillis;

        long sleepTime = 50;
        final long maxSleepTime = 200;
        log.debug("尝试获取分布式锁: {}, 最大等待时间: {}ms", lockKey, waitTimeMillis);
        while (System.currentTimeMillis() < endTime) {
            String lockValue = distributedLock.tryLock(lockKey, LOCK_EXPIRE, TimeUnit.SECONDS);
            if (lockValue != null) {
                long elapsed = System.currentTimeMillis() - start;
                log.debug("成功获取分布式锁: {}, 耗时: {}ms", lockKey, elapsed);
                return lockValue;
            }

            long remainingTime = endTime - System.currentTimeMillis();
            if (remainingTime <= 0) {
                break;
            }
            try {
                log.debug("获取分布式锁失败: {}, {}ms后重试", lockKey, Math.min(sleepTime, remainingTime));
                TimeUnit.MILLISECONDS.sleep(Math.min(sleepTime, remainingTime));
            } catch (InterruptedException e) {
                log.warn("等待获取分布式锁过程中线程被中断: {}", lockKey);
                Thread.currentThread().interrupt();
                return null;
            }
            sleepTime = Math.min(sleepTime * 2, maxSleepTime);
        }
        long elapsed = System.currentTimeMillis() - start;
        log.warn("获取分布式锁超时: {}, 总耗时: {}ms", lockKey, elapsed);
        return null;
    }
}