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.pay.entity.PayCoinAccountEntity;
import com.seer.teach.pay.mapper.PayCoinAccountMapper;
import com.seer.teach.pay.service.IPayCoinAccountService;
import com.seer.teach.pay.service.IPayCoinAccountStatusService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;

import static java.util.Objects.isNull;

@Slf4j
@Service
@RequiredArgsConstructor
public class PayCoinAccountServiceImpl extends ServiceImpl<PayCoinAccountMapper, PayCoinAccountEntity> implements IPayCoinAccountService {


    private final IPayCoinAccountStatusService userCoinAccountStatusService;

    /**
     * 根据用户ID获取账户账户
     *
     * @param userId 用户ID
     * @return 账户账户实体
     */
    @Override
    public PayCoinAccountEntity getByUserId(Integer userId) {
        log.info("开始查询用户 {} 的账户", userId);
        PayCoinAccountEntity account = super.getOne(new LambdaQueryWrapper<PayCoinAccountEntity>()
                .eq(PayCoinAccountEntity::getUserId, userId));
        if (account == null) {
            log.warn("用户 {} 没有找到对应的账户账户,开始创建用户账号", userId);
            account = createAccount(userId);

        }
        log.info("用户 {} 的账户账户信息: {}", userId, account);
        return account;
    }

    /**
     * 根据用户ID和账户ID获取账户账户
     *
     * @param userId    用户ID
     * @param accountId 账户ID
     * @return 账户账户实体
     */
    @Override
    public PayCoinAccountEntity getByUserIdAndAccountId(Integer userId, Integer accountId) {
        log.info("开始查询用户 {} 的账户账户 ID: {}", userId, accountId);
        PayCoinAccountEntity account = super.getOne(new LambdaQueryWrapper<PayCoinAccountEntity>()
                .eq(PayCoinAccountEntity::getUserId, userId)
                .eq(PayCoinAccountEntity::getId, accountId));
        if (account == null) {
            log.warn("用户 {} 未找到对应账户账户 ID: {}", userId, accountId);
        } else {
            log.info("用户 {} 的账户账户详情: {}", userId, account);
        }
        return account;
    }

    /**
     * 创建用户账户账户
     *
     * @param userId 用户ID
     * @return 是否创建成功
     */
    @Override
    public PayCoinAccountEntity createAccount(Integer userId) {
        log.info("开始为用户 {} 创建账户账户", userId);
        PayCoinAccountEntity account = new PayCoinAccountEntity();
        account.setUserId(userId);
        account.setTotalAmount(BigDecimal.ZERO);
        account.setAvailableBalance(BigDecimal.ZERO);
        account.setFreezeBalance(BigDecimal.ZERO);
        account.setIsBlock(CommonConstant.ACCOUNT_UNLOCKED);
        boolean result = super.save(account);
        if (result) {
            log.info("用户 {} 账户账户创建成功", userId);
        } else {
            log.error("用户 {} 账户账户创建失败", userId);
        }
        return account;
    }

    /**
     * 增加账户余额
     *
     * @param userId    用户ID
     * @param accountId 账户ID
     * @param amount    需要增加的金额
     * @return 是否更新成功
     */
    @Transactional(rollbackFor = Exception.class, timeout = 120)
    @Override
    public boolean addBalance(Integer userId, Integer accountId, BigDecimal amount) {
        log.info("开始为用户 {} 添加余额，金额: {} 分", userId, amount);
        PayCoinAccountEntity account = getByUserIdAndAccountId(userId, accountId);
        if (isNull(account)) {
            log.error("用户 {} 不存在账户账户 ID: {}", userId, accountId);
            return false;
        }

        // 2. 锁定账户
        if (!userCoinAccountStatusService.lockAccount(userId, accountId)) {
            log.warn("用户 {} 账户锁定失败", userId);
            return false;
        }
        try {
            BigDecimal originTotal = account.getTotalAmount();
            BigDecimal originAvailable = account.getAvailableBalance();

            BigDecimal totalAmount = defaultIfNull(originTotal).add(amount);
            BigDecimal availableBalance = defaultIfNull(originAvailable).add(amount);

            boolean result = super.update(new LambdaUpdateWrapper<PayCoinAccountEntity>()
                    .eq(PayCoinAccountEntity::getUserId, userId)
                    .eq(PayCoinAccountEntity::getId, accountId)
                    .set(PayCoinAccountEntity::getTotalAmount, totalAmount)
                    .set(PayCoinAccountEntity::getTotalAmount, account.getTotalAmount().add(amount))
                    .set(PayCoinAccountEntity::getAvailableBalance, availableBalance));

            if (result) {
                log.info("用户 {} 账户余额更新成功，原总余额: {}, 原可用余额: {}, 新总余额: {}, 新可用余额: {}",
                        userId, originTotal, originAvailable, totalAmount, availableBalance);
            } else {
                log.error("用户 {} 账户余额更新失败", userId);
            }
            return result;
        } catch (Exception e) {
            log.error("扣除账户中发生异常，账户仍处于锁定状态", e);
            throw new CommonException(ResultCodeEnum.USER_COIN_PAY_ERROR);
        } finally {
            // 4. 解锁账户
            if (!userCoinAccountStatusService.unlockAccount(userId, accountId)) {
                log.warn("用户 {} 扣除账户解锁失败，需人工处理", userId);
            } else {
                log.info("用户 {} 扣除账户已解锁", userId);
            }
        }
    }

    @Transactional(rollbackFor = Exception.class,timeout = 60)
    @Retryable(maxAttempts = 3, backoff = @Backoff(delay = 500))
    @Override
    public boolean deductBalance(Integer userId, Integer accountId, BigDecimal amount) {
        if (userId == null || accountId == null || amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
            return false;
        }
        log.info("用户 {} 开始扣除账户 {} ，扣除金额: {}", userId, accountId, amount);
        // 1. 获取当前账户信息,检查可用余额是否足够
        PayCoinAccountEntity account = getByUserId(userId);
        BigDecimal availableBalance = account.getAvailableBalance() == null ? BigDecimal.ZERO : account.getAvailableBalance();
        if (availableBalance.compareTo(amount) < 0) {
            log.warn("用户 {} 账户可用余额不足，当前可用余额: {}", userId, availableBalance);
            throw new CommonException(ResultCodeEnum.USER_NOT_ENOUGH_COIN);
        }
        // 2. 锁定账户
        if (!userCoinAccountStatusService.lockAccount(userId, accountId)) {
            log.warn("扣减用户 {} 账户余额，锁定账户失败", userId);
            throw new CommonException(ResultCodeEnum.USER_COIN_LOCK_ERROR);
        }
        try {
            // 3. 执行扣除操作
            boolean result = update(new LambdaUpdateWrapper<>(PayCoinAccountEntity.class)
                    .set(PayCoinAccountEntity::getAvailableBalance, availableBalance.subtract(amount))
                    .set(PayCoinAccountEntity::getTotalAmount, account.getTotalAmount().subtract(amount))
                    .set(PayCoinAccountEntity::getFreezeBalance, account.getFreezeBalance().add(amount))
                    .eq(PayCoinAccountEntity::getUserId, userId)
                    .eq(PayCoinAccountEntity::getId, accountId)
                    .ge(PayCoinAccountEntity::getAvailableBalance, amount));
            log.info("用户 {} 冻结金额结果：{}", userId, result ? "成功" : "失败");
            if(!result){
                throw new CommonException(ResultCodeEnum.USER_COIN_PAY_ERROR);
            }
            return true;
        } catch (Exception e) {
            log.error("扣除账户中发生异常，账户仍处于锁定状态", e);
            throw new CommonException(ResultCodeEnum.USER_COIN_PAY_ERROR);
        } finally {
            // 4. 解锁账户
            if (!userCoinAccountStatusService.unlockAccount(userId, accountId)) {
                log.warn("用户 {} 账户解锁失败，需人工处理", userId);
            } else {
                log.info("用户 {} 账户已解锁", userId);
            }
        }
    }

    @Override
    public boolean deductBalance(Integer userId, BigDecimal amount) {
        PayCoinAccountEntity account = getByUserId(userId);

        return deductBalance(userId, account.getId(), amount);
    }

    /**
     * 冻结金额
     *
     * @param userId    用户ID
     * @param accountId 账户ID
     * @param amount    需要冻结的金额（单位：分）
     * @return 是否冻结成功
     */
    @Override
    public boolean freezeBalance(Integer userId, Integer accountId, BigDecimal amount) {
        log.info("开始冻结用户 {} 的账户金额: {} 分", userId, amount);
        PayCoinAccountEntity account = getByUserIdAndAccountId(userId, accountId);
        if (isNull(account)) {
            log.error("用户 {} 不存在账户账户 ID: {}", userId, accountId);
            return false;
        }

        BigDecimal originAvailable = account.getAvailableBalance();
        BigDecimal originFreeze = account.getFreezeBalance();

        if (defaultIfNull(originAvailable).compareTo(amount) < 0) {
            log.warn("用户 {} 可用余额不足，无法冻结", userId);
            return false;
        }

        BigDecimal availableBalance = originAvailable.subtract(amount);
        BigDecimal freezeBalance = originFreeze.add(amount);

        boolean result = super.update(new LambdaUpdateWrapper<PayCoinAccountEntity>()
                .eq(PayCoinAccountEntity::getUserId, userId)
                .eq(PayCoinAccountEntity::getId, accountId)
                .set(PayCoinAccountEntity::getAvailableBalance, availableBalance)
                .set(PayCoinAccountEntity::getFreezeBalance, freezeBalance));

        if (result) {
            log.info("用户 {} 成功冻结金额 {} 分，可用余额: {}, 冻结余额: {}",
                    userId, amount, availableBalance, freezeBalance);
        } else {
            log.error("用户 {} 冻结金额失败", userId);
        }

        return result;
    }

    /**
     * 解冻金额
     *
     * @param userId    用户ID
     * @param accountId 账户ID
     * @param amount    需要解冻的金额（单位：分）
     * @return 是否解冻成功
     */
    @Override
    public boolean unfreezeBalance(Integer userId, Integer accountId, BigDecimal amount) {
        log.info("开始为用户 {} 解冻金额: {} 分", userId, amount);
        PayCoinAccountEntity account = getByUserIdAndAccountId(userId, accountId);
        if (isNull(account)) {
            log.error("用户 {} 不存在账户账户 ID: {}", userId, accountId);
            return false;
        }

        BigDecimal originAvailable = account.getAvailableBalance();
        BigDecimal originFreeze = account.getFreezeBalance();

        if (defaultIfNull(originFreeze).compareTo(amount) < 0) {
            log.warn("用户 {} 冻结余额不足，无法解冻", userId);
            return false;
        }

        BigDecimal availableBalance = originAvailable.add(amount);
        BigDecimal freezeBalance = originFreeze.subtract(amount);

        boolean result = super.update(new LambdaUpdateWrapper<PayCoinAccountEntity>()
                .eq(PayCoinAccountEntity::getUserId, userId)
                .eq(PayCoinAccountEntity::getId, accountId)
                .set(PayCoinAccountEntity::getAvailableBalance, availableBalance)
                .set(PayCoinAccountEntity::getFreezeBalance, freezeBalance));

        if (result) {
            log.info("用户 {} 成功解冻金额 {} 分，可用余额: {}, 冻结余额: {}",
                    userId, amount, availableBalance, freezeBalance);
        } else {
            log.error("用户 {} 解冻金额失败", userId);
        }

        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, timeout = 120)
    public boolean recharge(Integer userId, BigDecimal amount) {
        // 1. 获取账户信息
        PayCoinAccountEntity account = getByUserId(userId);
        Integer accountId = account.getId();

        try {
            // 2. 执行充值操作
            boolean rechargeResult = addBalance(userId, accountId, amount);
            if (!rechargeResult) {
                log.error("用户 {} 账户充值失败", userId);
                return false;
            }
            return true;
        } catch (Exception e) {
            log.error("充值过程中发生异常，账户仍处于锁定状态", e);
            return false;
        }
    }

    /**
     * BigDecimal 空值处理工具方法
     *
     * @param value BigDecimal 值
     * @return 如果为空则返回 BigDecimal.ZERO，否则返回原值
     */
    private BigDecimal defaultIfNull(BigDecimal value) {
        return value == null ? BigDecimal.ZERO : value;
    }

}