package com.ycwl.basic.pricing.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ycwl.basic.constant.BaseContextHandler;
import com.ycwl.basic.exception.BizException;
import com.ycwl.basic.pricing.dto.req.VoucherClaimReq;
import com.ycwl.basic.pricing.dto.req.VoucherCodeQueryReq;
import com.ycwl.basic.pricing.dto.resp.VoucherCodeResp;
import com.ycwl.basic.pricing.entity.PriceVoucherBatchConfig;
import com.ycwl.basic.pricing.entity.PriceVoucherCode;
import com.ycwl.basic.pricing.enums.VoucherCodeStatus;
import com.ycwl.basic.pricing.enums.VoucherDiscountType;
import com.ycwl.basic.pricing.mapper.PriceVoucherBatchConfigMapper;
import com.ycwl.basic.pricing.mapper.PriceVoucherCodeMapper;
import com.ycwl.basic.pricing.service.VoucherBatchService;
import com.ycwl.basic.pricing.service.VoucherCodeService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

@Slf4j
@Service
public class VoucherCodeServiceImpl implements VoucherCodeService {

    // 券码生成相关常量
    private static final String SAFE_CHARS = "23456789ABCDEFGHJKMNPQRTUVWXYZ";
    private static final char[] SAFE_CHARS_ARRAY = SAFE_CHARS.toCharArray();
    private static final int CODE_LENGTH = 6;
    private static final int MAX_RETRY = 10;
    private static final SecureRandom RANDOM = new SecureRandom();

    @Autowired
    private PriceVoucherCodeMapper voucherCodeMapper;
    @Autowired
    private PriceVoucherBatchConfigMapper voucherBatchMapper;
    @Autowired
    @Lazy
    private VoucherBatchService voucherBatchService;
    
    @Override
public void generateVoucherCodes(Long batchId, Long scenicId, Integer count) {
    List<PriceVoucherCode> codes = new ArrayList<>();
    
    for (int i = 0; i < count; i++) {
        PriceVoucherCode code = new PriceVoucherCode();
        code.setBatchId(batchId);
        code.setScenicId(scenicId);
        code.setCode(generateVoucherCode());
        code.setStatus(VoucherCodeStatus.UNCLAIMED.getCode());
        code.setCurrentUseCount(0);  // 初始化使用次数为0
        code.setCreateTime(new Date());
        code.setDeleted(0);
        codes.add(code);
    }
    
    for (PriceVoucherCode code : codes) {
        voucherCodeMapper.insert(code);
    }
}
    
    @Override
@Transactional
public VoucherCodeResp claimVoucher(VoucherClaimReq req) {
    if (!StringUtils.hasText(req.getCode())) {
        throw new BizException(400, "券码不能为空");
    }

    Long userId = Long.valueOf(BaseContextHandler.getUserId());

    // 查询券码
    LambdaQueryWrapper<PriceVoucherCode> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(PriceVoucherCode::getCode, req.getCode())
            .eq(PriceVoucherCode::getDeleted, 0);

    PriceVoucherCode voucherCode = voucherCodeMapper.selectOne(wrapper);
    if (voucherCode == null) {
        throw new BizException(400, "券码不存在");
    }

    // 查询批次信息，用于构建响应
    PriceVoucherBatchConfig batch = voucherBatchMapper.selectById(voucherCode.getBatchId());

    // 券码已找到，后续校验失败时仍返回 scenicId 等信息
    if (!Objects.equals(voucherCode.getStatus(), VoucherCodeStatus.UNCLAIMED.getCode())) {
        VoucherCodeResp resp = convertToResp(voucherCode, batch);
        resp.setSuccess(false);
        resp.setMessage("券码已被领取或已使用");
        return resp;
    }

    if (!canClaimVoucher(userId, voucherCode.getScenicId())) {
        VoucherCodeResp resp = convertToResp(voucherCode, batch);
        resp.setSuccess(false);
        resp.setMessage("该用户在此景区已领取过券码");
        return resp;
    }

    if (batch == null || batch.getDeleted() == 1) {
        VoucherCodeResp resp = convertToResp(voucherCode, batch);
        resp.setSuccess(false);
        resp.setMessage("券码批次不存在");
        return resp;
    }

    // 更新券码状态
    voucherCode.setUserId(userId);
    voucherCode.setStatus(VoucherCodeStatus.CLAIMED_UNUSED.getCode());
    voucherCode.setClaimedTime(new Date());
    // 确保currentUseCount被初始化
    if (voucherCode.getCurrentUseCount() == null) {
        voucherCode.setCurrentUseCount(0);
    }

    voucherCodeMapper.updateById(voucherCode);

    voucherBatchService.updateBatchClaimedCount(batch.getId());

    VoucherCodeResp resp = convertToResp(voucherCode, batch);
    resp.setSuccess(true);
    resp.setMessage("领取成功");
    return resp;
}
    
    @Override
    public PageInfo<VoucherCodeResp> queryCodeList(VoucherCodeQueryReq req) {
        PageHelper.startPage(req.getPageNum(), req.getPageSize());

        LambdaQueryWrapper<PriceVoucherCode> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PriceVoucherCode::getDeleted, 0)
                .eq(req.getBatchId() != null, PriceVoucherCode::getBatchId, req.getBatchId())
                .eq(req.getScenicId() != null, PriceVoucherCode::getScenicId, req.getScenicId())
                .eq(req.getUserId() != null, PriceVoucherCode::getUserId, req.getUserId())
                .eq(req.getStatus() != null, PriceVoucherCode::getStatus, req.getStatus())
                .like(StringUtils.hasText(req.getCode()), PriceVoucherCode::getCode, req.getCode())
                .orderByDesc(PriceVoucherCode::getId);

        List<PriceVoucherCode> list = voucherCodeMapper.selectList(wrapper);

        List<VoucherCodeResp> respList = new ArrayList<>();
        for (PriceVoucherCode code : list) {
            PriceVoucherBatchConfig batch = voucherBatchMapper.selectById(code.getBatchId());
            respList.add(convertToResp(code, batch));
        }

        return new PageInfo<>(respList);
    }
    
    @Override
    public List<VoucherCodeResp> getMyVoucherCodes(Long userId) {
        LambdaQueryWrapper<PriceVoucherCode> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PriceVoucherCode::getUserId, userId)
                .eq(PriceVoucherCode::getDeleted, 0)
                .orderByDesc(PriceVoucherCode::getClaimedTime);
        
        List<PriceVoucherCode> codes = voucherCodeMapper.selectList(wrapper);
        
        List<VoucherCodeResp> respList = new ArrayList<>();
        for (PriceVoucherCode code : codes) {
            PriceVoucherBatchConfig batch = voucherBatchMapper.selectById(code.getBatchId());
            respList.add(convertToResp(code, batch));
        }
        
        return respList;
    }
    
    @Override
@Transactional
public void markCodeAsUsed(Long codeId, String remark) {
    PriceVoucherCode code = voucherCodeMapper.selectById(codeId);
    if (code == null || code.getDeleted() == 1) {
        throw new BizException(404, "券码不存在");
    }
    
    if (!Objects.equals(code.getStatus(), VoucherCodeStatus.CLAIMED_UNUSED.getCode())) {
        throw new BizException(400, "券码状态异常，无法使用");
    }
    
    // 更新使用计数和时间
    Integer currentUseCount = code.getCurrentUseCount() != null ? code.getCurrentUseCount() : 0;
    code.setCurrentUseCount(currentUseCount + 1);
    code.setLastUsedTime(new Date());
    
    code.setStatus(VoucherCodeStatus.USED.getCode());
    code.setUsedTime(new Date());
    code.setRemark(remark);
    
    voucherCodeMapper.updateById(code);
    
    voucherBatchService.updateBatchUsedCount(code.getBatchId());
}
    
    @Override
    public boolean canClaimVoucher(Long userId, Long scenicId) {
        Integer count = voucherCodeMapper.countByUserIdAndScenicId(userId, scenicId);
        return count == 0;
    }
    
    /**
     * 生成6位安全券码（去除易混淆字符）
     * 字符集：数字2-9 + 大写字母（去除0,1,I,L,O,S）
     * 
     * @return 6位券码
     */
    private String generateVoucherCode() {
        for (int attempt = 0; attempt < MAX_RETRY; attempt++) {
            String code = generateRandomCode();
            log.debug("生成券码候选: {} (尝试第{}次)", code, attempt + 1);
            
            if (!isCodeExists(code)) {
                log.info("成功生成券码: {} (字符集大小: {}, 理论组合数: {})", 
                        code, SAFE_CHARS.length(), Math.pow(SAFE_CHARS.length(), CODE_LENGTH));
                return code;
            }
            
            log.warn("券码重复，重新生成: {}", code);
        }
        
        // 如果重试次数用完仍有重复，抛出异常
        log.error("券码生成失败：达到最大重试次数 {}", MAX_RETRY);
        throw new RuntimeException("券码生成失败：达到最大重试次数，请稍后重试");
    }
    
    /**
     * 生成随机6位字符
     * 
     * @return 随机6位字符
     */
    private String generateRandomCode() {
        StringBuilder code = new StringBuilder(CODE_LENGTH);
        for (int i = 0; i < CODE_LENGTH; i++) {
            int randomIndex = RANDOM.nextInt(SAFE_CHARS_ARRAY.length);
            code.append(SAFE_CHARS_ARRAY[randomIndex]);
        }
        return code.toString();
    }
    
    /**
     * 检查券码是否已存在
     * 
     * @param code 券码
     * @return 是否存在
     */
    private boolean isCodeExists(String code) {
        LambdaQueryWrapper<PriceVoucherCode> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PriceVoucherCode::getCode, code)
               .eq(PriceVoucherCode::getDeleted, 0);
        return voucherCodeMapper.selectCount(wrapper) > 0;
    }
    
    private VoucherCodeResp convertToResp(PriceVoucherCode code, PriceVoucherBatchConfig batch) {
        VoucherCodeResp resp = new VoucherCodeResp();
        BeanUtils.copyProperties(code, resp);
        
        if (batch != null) {
            resp.setBatchName(batch.getBatchName());
            resp.setDiscountType(batch.getDiscountType());
            resp.setDiscountValue(batch.getDiscountValue());
            
            VoucherDiscountType discountType = VoucherDiscountType.getByCode(batch.getDiscountType());
            if (discountType != null) {
                resp.setDiscountTypeName(discountType.getName());
                resp.setDiscountDescription(discountType.getDescription());
            }
            
            // 设置可重复使用相关字段
            Integer currentUseCount = code.getCurrentUseCount() != null ? code.getCurrentUseCount() : 0;
            resp.setCurrentUseCount(currentUseCount);
            resp.setMaxUseCount(batch.getMaxUseCount());
            resp.setLastUsedTime(code.getLastUsedTime());
            
            // 计算剩余使用次数
            if (batch.getMaxUseCount() != null) {
                int remaining = batch.getMaxUseCount() - currentUseCount;
                resp.setRemainingUseCount(Math.max(0, remaining));
                
                // 计算是否还能使用
                resp.setCanUseMore(currentUseCount < batch.getMaxUseCount());
            } else {
                resp.setRemainingUseCount(null); // 无限制
                resp.setCanUseMore(true);
            }
        }
        
        VoucherCodeStatus status = VoucherCodeStatus.getByCode(code.getStatus());
        if (status != null) {
            resp.setStatusName(status.getName());
        }
        
        return resp;
    }
}