package com.yupi.project.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yupi.project.common.ErrorCode;
import com.yupi.project.exception.BusinessException;
import com.yupi.project.mapper.ActivationCodeMapper;
import com.yupi.project.model.dto.activationcode.ActivationCodeQueryRequest;
import com.yupi.project.model.entity.ActivationCode;
import com.yupi.project.service.ActivationCodeService;
import com.yupi.project.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Service
@Slf4j
public class ActivationCodeServiceImpl extends ServiceImpl<ActivationCodeMapper, ActivationCode> implements ActivationCodeService {

    @Resource
    private UserService userService;

    // 通常不直接注入Mapper，而是通过继承的ServiceImpl的方法操作，但如果需要复杂查询可以注入
    // @Resource
    // private ActivationCodeMapper activationCodeMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<ActivationCode> generateCodes(int count, BigDecimal value, LocalDateTime expireTime, String batchId) throws Exception {
        if (count <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "生成数量必须大于0");
        }
        if (value == null || value.compareTo(BigDecimal.ZERO) <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "激活码面值必须大于0");
        }

        List<ActivationCode> generatedCodes = new ArrayList<>();
        String effectiveBatchId = (batchId == null || batchId.isEmpty()) ? UUID.randomUUID().toString() : batchId;

        for (int i = 0; i < count; i++) {
            ActivationCode activationCode = new ActivationCode();
            // 生成唯一code的简单策略，实际项目中可能需要更复杂的防碰撞机制或预生成库
            String uniqueCode = UUID.randomUUID().toString().replace("-", "").substring(0, 16).toUpperCase();
            // TODO: 考虑code冲突的可能性，虽然UUID冲突概率极低，但严格来说需要循环检查或数据库唯一约束处理
            // 在高并发下，先生成再批量插入，并处理唯一约束异常可能是更好的方式

            activationCode.setCode(uniqueCode);
            activationCode.setValue(value);
            activationCode.setExpireTime(expireTime);
            activationCode.setBatchId(effectiveBatchId);
            activationCode.setIsUsed(0); // 0 for not used
            activationCode.setIsDelete(0); // 0 for not deleted
            // createTime and updateTime will be handled by DB default or MyBatis Plus fill strategy
            generatedCodes.add(activationCode);
        }

        boolean saveBatchResult = this.saveBatch(generatedCodes);
        if (!saveBatchResult) {
            log.error("Failed to batch save activation codes for batchId: {}", effectiveBatchId);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "批量生成激活码失败");
        }
        log.info("Successfully generated {} activation codes with value {}, batchId: {}.", count, value, effectiveBatchId);
        return generatedCodes;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean redeemCode(Long currentUserId, String code) {
        if (currentUserId == null || currentUserId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "无效的用户ID");
        }
        if (code == null || code.trim().isEmpty()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "激活码不能为空");
        }

        // 1. 查询激活码
        QueryWrapper<ActivationCode> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("code", code.trim());
        // is_delete = 0 会由 @TableLogic 自动处理
        ActivationCode activationCode = this.getOne(queryWrapper);

        if (activationCode == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "无效的激活码");
        }

        // 2. 检查是否已使用
        if (activationCode.getIsUsed() == 1) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "此激活码已被使用");
        }

        // 3. 检查是否过期
        if (activationCode.getExpireTime() != null && LocalDateTime.now().isAfter(activationCode.getExpireTime())) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "此激活码已过期");
        }

        // 4. 增加用户余额
        boolean increaseSuccess = userService.increaseBalance(currentUserId, activationCode.getValue());
        if (!increaseSuccess) {
            // increaseBalance 内部应该会抛出异常如果失败（如用户不存在），或者返回false
            log.error("Failed to increase balance for user {} while redeeming code {}", currentUserId, code);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "余额充值失败，请稍后再试");
        }

        // 5. 更新激活码状态
        activationCode.setIsUsed(1);
        activationCode.setUserId(currentUserId);
        activationCode.setUseTime(LocalDateTime.now());
        // updateTime 会自动更新

        boolean updateResult = this.updateById(activationCode);
        if (!updateResult) {
            log.error("Failed to update activation code {} status after redeeming by user {}. Potential inconsistency!", code, currentUserId);
            // 由于余额已增加，但激活码状态更新失败，这是一个危险状态。事务应回滚。
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新激活码状态失败，请联系客服");
        }

        log.info("User {} successfully redeemed activation code {}. Value: {}", currentUserId, code, activationCode.getValue());
        return true;
    }

    @Override
    public QueryWrapper<ActivationCode> getQueryWrapper(ActivationCodeQueryRequest activationCodeQueryRequest) {
        QueryWrapper<ActivationCode> queryWrapper = new QueryWrapper<>();
        if (activationCodeQueryRequest == null) {
            return queryWrapper;
        }

        String code = activationCodeQueryRequest.getCode();
        Integer isUsed = activationCodeQueryRequest.getIsUsed();
        String batchId = activationCodeQueryRequest.getBatchId();
        Long userId = activationCodeQueryRequest.getUserId();
        BigDecimal valueMin = activationCodeQueryRequest.getValueMin();
        BigDecimal valueMax = activationCodeQueryRequest.getValueMax();
        LocalDateTime expireTimeStart = activationCodeQueryRequest.getExpireTimeStart();
        LocalDateTime expireTimeEnd = activationCodeQueryRequest.getExpireTimeEnd();
        LocalDateTime createTimeStart = activationCodeQueryRequest.getCreateTimeStart();
        LocalDateTime createTimeEnd = activationCodeQueryRequest.getCreateTimeEnd();
        String sortField = activationCodeQueryRequest.getSortField();
        String sortOrder = activationCodeQueryRequest.getSortOrder();

        // 拼接查询条件
        if (StringUtils.isNotBlank(code)) {
            queryWrapper.like("code", code);
        }
        if (isUsed != null) {
            queryWrapper.eq("isUsed", isUsed);
        }
        if (StringUtils.isNotBlank(batchId)) {
            queryWrapper.eq("batchId", batchId);
        }
        if (userId != null && userId > 0) {
            queryWrapper.eq("userId", userId);
        }
        if (valueMin != null) {
            queryWrapper.ge("value", valueMin);
        }
        if (valueMax != null) {
            queryWrapper.le("value", valueMax);
        }
        if (expireTimeStart != null) {
            queryWrapper.ge("expireTime", expireTimeStart);
        }
        if (expireTimeEnd != null) {
            queryWrapper.le("expireTime", expireTimeEnd);
        }
        if (createTimeStart != null) {
            queryWrapper.ge("createTime", createTimeStart);
        }
        if (createTimeEnd != null) {
            queryWrapper.le("createTime", createTimeEnd);
        }
        // 默认按创建时间降序
        if (StringUtils.isNotBlank(sortField)) {
            queryWrapper.orderBy(true, "ascend".equalsIgnoreCase(sortOrder), sortField);
        } else {
             queryWrapper.orderByDesc("create_time");
        }

        return queryWrapper;
    }

    @Override
    @Transactional
    public boolean deleteCode(Long codeId) {
        if (codeId == null || codeId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "无效的激活码ID");
        }
        // 检查激活码是否存在，这步可选，因为removeById如果找不到也不会报错，但返回false
        ActivationCode existingCode = this.getById(codeId);
        if (existingCode == null || existingCode.getIsDelete() == 1) { // 已经是逻辑删除状态
            // 可以选择抛出未找到错误，或者认为已经是"已删除"状态，返回true
            // throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "激活码不存在或已被删除");
            log.warn("Attempted to delete a non-existent or already deleted activation code with ID: {}", codeId);
            return true; // 或者 false，取决于业务定义，这里认为已经是目标状态所以返回true
        }

        // 使用MyBatis Plus的逻辑删除
        boolean result = this.removeById(codeId);
        if (!result) {
            // 这种情况理论上不应该发生，除非并发删除了或者 getById 和 removeById 之间状态变了
            log.error("Failed to logically delete activation code with ID: {}. removeById returned false.", codeId);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "删除激活码失败");
        }
        log.info("Activation code with ID: {} logically deleted.", codeId);
        return true;
    }
} 