package com.dora.service.impl;

import com.dora.entity.PointsConsumptionLog;
import com.dora.entity.User;
import com.dora.exception.InsufficientPointsException;
import com.dora.mapper.PointsConsumptionLogMapper;
import com.dora.mapper.UserMapper;
import com.dora.service.PointsService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class PointsServiceImpl implements PointsService {

    private final UserMapper userMapper;
    private final PointsConsumptionLogMapper pointsConsumptionLogMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deductPoints(Long userId, Integer amount, String taskNo, String description) throws InsufficientPointsException {
        if (amount <= 0) {
            throw new IllegalArgumentException("Deduction amount must be positive.");
        }

        User user = userMapper.findById(userId);
        if (user == null) {
            throw new IllegalArgumentException("User not found with ID: " + userId);
        }

        int balanceBefore = user.getPoints();
        if (balanceBefore < amount) {
            throw new InsufficientPointsException("User " + userId + " has insufficient points. Required: " + amount + ", Available: " + balanceBefore);
        }

        int updatedRows = userMapper.updateUserPoints(userId, -amount);
        if (updatedRows == 0) {
            // This could happen due to a race condition where points were spent after the initial check.
            throw new InsufficientPointsException("Failed to deduct points due to a concurrent update. Please try again.");
        }

        int balanceAfter = balanceBefore - amount;
        createLog(userId, taskNo, "consume", -amount, balanceBefore, balanceAfter, description);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refundPoints(Long userId, Integer amount, String taskNo, String description) {
        if (amount <= 0) {
            throw new IllegalArgumentException("退款积分数量必须为正数。");
        }

        User user = userMapper.findById(userId);
        if (user == null) {
            throw new IllegalArgumentException("用户不存在: " + userId);
        }
        
        int balanceBefore = user.getPoints();
        userMapper.updateUserPoints(userId, amount);
        int balanceAfter = balanceBefore + amount;

        createLog(userId, taskNo, "refund", amount, balanceBefore, balanceAfter, description);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void adminAdjustPoints(Long userId, Integer amount, String description, Long adminId) {
        if (amount == 0) {
            return; // No change needed
        }

        User user = userMapper.findById(userId);
        if (user == null) {
            throw new IllegalArgumentException("用户不存在: " + userId);
        }
        
        int balanceBefore = user.getPoints();
        int updatedRows = userMapper.updateUserPoints(userId, amount);
        if (updatedRows == 0 && amount < 0) {
            // This prevents admin from making points negative
            throw new InsufficientPointsException("Admin adjustment failed. User points cannot be negative.");
        }
        int balanceAfter = balanceBefore + amount;
        
        String logDescription = String.format("Admin (ID:%d) adjustment: %s", adminId, description);
        createLog(userId, null, "admin_adjust", amount, balanceBefore, balanceAfter, logDescription);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void grantPoints(Long userId, Integer amount, String description) {
        if (amount <= 0) {
            throw new IllegalArgumentException("发放积分数量必须为正数。");
        }

        User user = userMapper.findById(userId);
        if (user == null) {
            throw new IllegalArgumentException("用户不存在: " + userId);
        }

        int balanceBefore = user.getPoints();
        userMapper.updateUserPoints(userId, amount);
        int balanceAfter = balanceBefore + amount;

        createLog(userId, null, "grant", amount, balanceBefore, balanceAfter, description);
    }

    private void createLog(Long userId, String taskNo, String changeType, Integer amount, int before, int after, String description) {
        PointsConsumptionLog log = new PointsConsumptionLog();
        log.setUserId(userId);
        log.setTaskNo(taskNo);
        log.setChangeType(changeType);
        log.setChangeAmount(amount);
        log.setBalanceBefore(before);
        log.setBalanceAfter(after);
        log.setDescription(description);
        log.setIsDeleted(0); // 设置逻辑删除标识为0（未删除）
        pointsConsumptionLogMapper.insert(log);
    }
}
