package com.ycwl.basic.pricing.service;

import com.ycwl.basic.pricing.service.impl.VoucherPrintServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * 优惠券打印服务流水号生成测试
 * 专门测试generateCode方法的重复率和性能
 */
@Slf4j
@SpringBootTest
public class VoucherPrintServiceCodeGenerationTest {

    private static final String CODE_PREFIX = "VT";
    
    /**
     * 模拟当前的generateCode方法实现
     */
    private String generateCode() {
        SimpleDateFormat sdf = new SimpleDateFormat("ss");
        String timestamp = sdf.format(new Date());
        String randomSuffix = String.valueOf((int)(Math.random() * 100000)).formatted("%05d");
        return CODE_PREFIX + timestamp + randomSuffix;
    }

    /**
     * 测试单线程环境下1秒内生成10个流水号的重复率
     */
    @Test
    public void testGenerateCodeDuplicationRateInOneSecond() {
        log.info("=== 开始测试1秒内生成10个流水号的重复率 ===");
        
        int totalRounds = 1000; // 测试1000轮
        int codesPerRound = 10;  // 每轮生成10个流水号
        int totalDuplicates = 0;
        int totalCodes = 0;
        
        for (int round = 0; round < totalRounds; round++) {
            Set<String> codes = new HashSet<>();
            List<String> codeList = new ArrayList<>();
            
            // 在很短时间内生成10个流水号
            for (int i = 0; i < codesPerRound; i++) {
                String code = generateCode();
                codes.add(code);
                codeList.add(code);
            }
            
            int duplicates = codeList.size() - codes.size();
            if (duplicates > 0) {
                totalDuplicates += duplicates;
                log.warn("第{}轮发现{}个重复: {}", round + 1, duplicates, codeList);
            }
            
            totalCodes += codesPerRound;
            
            // 稍微休息一下，避免在完全同一时间生成
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        
        double duplicationRate = (double) totalDuplicates / totalCodes * 100;
        log.info("=== 单线程测试结果 ===");
        log.info("总轮数: {}", totalRounds);
        log.info("每轮生成数: {}", codesPerRound);
        log.info("总生成数: {}", totalCodes);
        log.info("总重复数: {}", totalDuplicates);
        log.info("重复率: {:.4f}%", duplicationRate);
        
        // 记录一些示例生成的流水号
        log.info("=== 示例流水号 ===");
        for (int i = 0; i < 20; i++) {
            log.info("示例{}: {}", i + 1, generateCode());
        }
    }

    /**
     * 测试严格在1秒内生成流水号的重复率
     */
    @Test
    public void testStrictOneSecondGeneration() {
        log.info("=== 开始测试严格1秒内生成流水号重复率 ===");
        
        int rounds = 100;
        int totalDuplicates = 0;
        int totalCodes = 0;
        
        for (int round = 0; round < rounds; round++) {
            Set<String> codes = new HashSet<>();
            List<String> codeList = new ArrayList<>();
            
            long startTime = System.currentTimeMillis();
            
            // 在1秒内尽可能多地生成流水号
            while (System.currentTimeMillis() - startTime < 1000) {
                String code = generateCode();
                codes.add(code);
                codeList.add(code);
            }
            
            int duplicates = codeList.size() - codes.size();
            totalDuplicates += duplicates;
            totalCodes += codeList.size();
            
            if (duplicates > 0) {
                log.warn("第{}轮: 生成{}个，重复{}个，重复率{:.2f}%", 
                         round + 1, codeList.size(), duplicates, 
                         (double) duplicates / codeList.size() * 100);
            }
            
            // 等待下一秒开始
            try {
                Thread.sleep(1100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        
        double overallDuplicationRate = (double) totalDuplicates / totalCodes * 100;
        log.info("=== 严格1秒测试结果 ===");
        log.info("测试轮数: {}", rounds);
        log.info("总生成数: {}", totalCodes);
        log.info("总重复数: {}", totalDuplicates);
        log.info("总体重复率: {:.4f}%", overallDuplicationRate);
        log.info("平均每轮生成: {:.1f}个", (double) totalCodes / rounds);
    }

    /**
     * 分析流水号的分布特征
     */
    @Test
    public void testCodeDistributionAnalysis() {
        log.info("=== 开始分析流水号分布特征 ===");
        
        int sampleSize = 10000;
        List<String> codes = new ArrayList<>();
        Map<String, Integer> prefixCount = new HashMap<>();  // 时间前缀统计
        Map<String, Integer> suffixCount = new HashMap<>();  // 随机后缀统计
        
        // 生成样本
        for (int i = 0; i < sampleSize; i++) {
            String code = generateCode();
            codes.add(code);
            
            // 提取时间前缀 (VTxx)
            String prefix = code.substring(0, 4);
            prefixCount.put(prefix, prefixCount.getOrDefault(prefix, 0) + 1);
            
            // 提取随机后缀 (最后5位)
            String suffix = code.substring(4);
            suffixCount.put(suffix, suffixCount.getOrDefault(suffix, 0) + 1);
            
            // 稍微间隔一下，避免全在同一秒
            if (i % 100 == 0) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
        
        // 计算去重率
        Set<String> uniqueCodes = new HashSet<>(codes);
        double uniqueRate = (double) uniqueCodes.size() / sampleSize * 100;
        
        // 分析时间前缀分布
        log.info("=== 时间前缀分布 (前10个) ===");
        prefixCount.entrySet().stream()
                .sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
                .limit(10)
                .forEach(entry -> log.info("前缀 {}: {}次 ({:.2f}%)", 
                        entry.getKey(), entry.getValue(), 
                        (double) entry.getValue() / sampleSize * 100));
        
        // 检查随机后缀的重复情况
        long duplicatedSuffixes = suffixCount.entrySet().stream()
                .filter(entry -> entry.getValue() > 1)
                .count();
        
        log.info("=== 分布分析结果 ===");
        log.info("样本总数: {}", sampleSize);
        log.info("唯一流水号数: {}", uniqueCodes.size());
        log.info("去重率: {:.4f}%", uniqueRate);
        log.info("时间前缀种类: {}", prefixCount.size());
        log.info("随机后缀种类: {}", suffixCount.size());
        log.info("重复的随机后缀数: {}", duplicatedSuffixes);
        log.info("随机后缀重复率: {:.4f}%", (double) duplicatedSuffixes / suffixCount.size() * 100);
    }

    /**
     * 模拟真实业务场景：快速连续请求
     */
    @Test
    public void testRealBusinessScenario() {
        log.info("=== 开始模拟真实业务场景测试 ===");
        
        // 模拟场景：10个用户几乎同时请求打印小票
        int simultaneousUsers = 10;
        int testsPerUser = 5; // 每个用户发送5次请求
        int totalTests = 50; // 总共进行50次这样的场景测试
        
        int totalDuplicates = 0;
        int totalCodes = 0;
        
        for (int test = 0; test < totalTests; test++) {
            Set<String> allCodes = new HashSet<>();
            List<String> allCodesList = new ArrayList<>();
            
            // 模拟同一时刻多个用户的请求
            for (int user = 0; user < simultaneousUsers; user++) {
                for (int request = 0; request < testsPerUser; request++) {
                    String code = generateCode();
                    allCodes.add(code);
                    allCodesList.add(code);
                }
            }
            
            int duplicates = allCodesList.size() - allCodes.size();
            if (duplicates > 0) {
                totalDuplicates += duplicates;
                log.warn("第{}次场景测试发现{}个重复流水号", test + 1, duplicates);
                
                // 找出重复的流水号
                Map<String, Integer> codeCount = new HashMap<>();
                for (String code : allCodesList) {
                    codeCount.put(code, codeCount.getOrDefault(code, 0) + 1);
                }
                
                codeCount.entrySet().stream()
                        .filter(entry -> entry.getValue() > 1)
                        .forEach(entry -> log.warn("重复流水号: {} (出现{}次)", 
                                entry.getKey(), entry.getValue()));
            }
            
            totalCodes += allCodesList.size();
            
            // 模拟请求间隔
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        
        double duplicationRate = (double) totalDuplicates / totalCodes * 100;
        log.info("=== 真实业务场景测试结果 ===");
        log.info("场景测试次数: {}", totalTests);
        log.info("每次场景用户数: {}", simultaneousUsers);
        log.info("每用户请求数: {}", testsPerUser);
        log.info("总生成流水号数: {}", totalCodes);
        log.info("总重复数: {}", totalDuplicates);
        log.info("重复率: {:.4f}%", duplicationRate);
        
        if (duplicationRate > 0.1) {
            log.warn("警告：重复率超过0.1%，建议优化generateCode方法！");
        }
    }

    /**
     * 高并发多线程测试
     */
    @Test
    public void testHighConcurrencyGeneration() throws InterruptedException {
        log.info("=== 开始高并发多线程测试 ===");
        
        int threadCount = 20;           // 20个并发线程
        int codesPerThread = 50;        // 每个线程生成50个流水号
        int totalExpectedCodes = threadCount * codesPerThread;
        
        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
        CountDownLatch latch = new CountDownLatch(threadCount);
        ConcurrentHashMap<String, Integer> allCodes = new ConcurrentHashMap<>();
        List<String> allCodesList = Collections.synchronizedList(new ArrayList<>());
        
        // 启动所有线程
        for (int i = 0; i < threadCount; i++) {
            final int threadId = i;
            executor.submit(() -> {
                try {
                    List<String> threadCodes = new ArrayList<>();
                    
                    // 每个线程快速生成流水号
                    for (int j = 0; j < codesPerThread; j++) {
                        String code = generateCode();
                        threadCodes.add(code);
                        allCodesList.add(code);
                        
                        // 统计重复
                        Integer count = allCodes.put(code, 1);
                        if (count != null) {
                            allCodes.put(code, count + 1);
                        }
                    }
                    
                    log.debug("线程{}完成，生成{}个流水号", threadId, threadCodes.size());
                    
                } finally {
                    latch.countDown();
                }
            });
        }
        
        // 等待所有线程完成
        boolean finished = latch.await(30, TimeUnit.SECONDS);
        executor.shutdown();
        
        if (!finished) {
            log.error("测试超时！");
            return;
        }
        
        // 分析结果
        Set<String> uniqueCodes = new HashSet<>(allCodesList);
        int duplicates = totalExpectedCodes - uniqueCodes.size();
        double duplicationRate = (double) duplicates / totalExpectedCodes * 100;
        
        // 找出重复的流水号
        List<Map.Entry<String, Integer>> duplicatedCodes = allCodes.entrySet().stream()
                .filter(entry -> entry.getValue() > 1)
                .sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
                .limit(20) // 只显示前20个最多重复的
                .toList();
        
        log.info("=== 高并发测试结果 ===");
        log.info("并发线程数: {}", threadCount);
        log.info("每线程生成数: {}", codesPerThread);
        log.info("预期总数: {}", totalExpectedCodes);
        log.info("实际总数: {}", allCodesList.size());
        log.info("唯一流水号数: {}", uniqueCodes.size());
        log.info("重复数: {}", duplicates);
        log.info("重复率: {:.4f}%", duplicationRate);
        
        if (!duplicatedCodes.isEmpty()) {
            log.warn("=== 发现重复流水号 ===");
            duplicatedCodes.forEach(entry -> 
                log.warn("流水号: {} 重复了 {} 次", entry.getKey(), entry.getValue()));
        }
        
        if (duplicationRate > 1.0) {
            log.error("严重警告：高并发下重复率超过1.0%，必须优化generateCode方法！");
        }
    }

    /**
     * 模拟极端高压场景：短时间内大量请求
     */
    @Test
    public void testExtremeHighPressure() throws InterruptedException {
        log.info("=== 开始极端高压测试 ===");
        
        int threadCount = 50;           // 50个并发线程
        int codesPerThread = 20;        // 每个线程生成20个
        long timeWindowMs = 1000;       // 在1秒内完成
        
        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
        CountDownLatch startLatch = new CountDownLatch(1);
        CountDownLatch endLatch = new CountDownLatch(threadCount);
        
        ConcurrentHashMap<String, List<Integer>> codeToThreads = new ConcurrentHashMap<>();
        List<String> allCodes = Collections.synchronizedList(new ArrayList<>());
        
        // 准备所有线程
        for (int i = 0; i < threadCount; i++) {
            final int threadId = i;
            executor.submit(() -> {
                try {
                    // 等待开始信号
                    startLatch.await();
                    
                    long startTime = System.currentTimeMillis();
                    List<String> threadCodes = new ArrayList<>();
                    
                    // 在时间窗口内尽可能快地生成
                    while (System.currentTimeMillis() - startTime < timeWindowMs && 
                           threadCodes.size() < codesPerThread) {
                        String code = generateCode();
                        threadCodes.add(code);
                        allCodes.add(code);
                        
                        // 记录哪个线程生成了这个流水号
                        codeToThreads.computeIfAbsent(code, k -> 
                            Collections.synchronizedList(new ArrayList<>())).add(threadId);
                    }
                    
                    log.debug("线程{}在{}ms内生成{}个流水号", 
                            threadId, System.currentTimeMillis() - startTime, threadCodes.size());
                    
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    endLatch.countDown();
                }
            });
        }
        
        // 开始测试
        long testStartTime = System.currentTimeMillis();
        startLatch.countDown();
        
        // 等待完成
        boolean finished = endLatch.await(timeWindowMs + 5000, TimeUnit.MILLISECONDS);
        executor.shutdown();
        long testDuration = System.currentTimeMillis() - testStartTime;
        
        if (!finished) {
            log.error("极端高压测试超时！");
            return;
        }
        
        // 分析结果
        Set<String> uniqueCodes = new HashSet<>(allCodes);
        int totalGenerated = allCodes.size();
        int duplicates = totalGenerated - uniqueCodes.size();
        double duplicationRate = (double) duplicates / totalGenerated * 100;
        double generationRate = (double) totalGenerated / testDuration * 1000; // 每秒生成数
        
        // 分析重复模式
        Map<String, List<Integer>> duplicatedCodes = codeToThreads.entrySet().stream()
                .filter(entry -> entry.getValue().size() > 1)
                .collect(Collectors.toMap(
                    Map.Entry::getKey,
                    Map.Entry::getValue
                ));
        
        log.info("=== 极端高压测试结果 ===");
        log.info("并发线程数: {}", threadCount);
        log.info("预期每线程生成数: {}", codesPerThread);
        log.info("测试持续时间: {}ms", testDuration);
        log.info("实际总生成数: {}", totalGenerated);
        log.info("唯一流水号数: {}", uniqueCodes.size());
        log.info("重复数: {}", duplicates);
        log.info("重复率: {:.4f}%", duplicationRate);
        log.info("生成速率: {:.1f} codes/sec", generationRate);
        
        if (!duplicatedCodes.isEmpty()) {
            log.warn("=== 极端高压下的重复情况 ===");
            duplicatedCodes.entrySet().stream()
                    .limit(10) // 只显示前10个
                    .forEach(entry -> {
                        String code = entry.getKey();
                        List<Integer> threads = entry.getValue();
                        log.warn("流水号: {} 被线程 {} 重复生成", code, threads);
                    });
        }
        
        // 评估结果
        if (duplicationRate > 5.0) {
            log.error("极严重警告：极端高压下重复率超过5.0%，generateCode方法不适合高并发场景！");
        } else if (duplicationRate > 1.0) {
            log.warn("警告：极端高压下重复率超过1.0%，建议优化generateCode方法");
        }
        
        if (generationRate > 10000) {
            log.info("性能良好：生成速率超过10,000 codes/sec");
        }
    }

    /**
     * 综合测试报告
     */
    @Test
    public void generateComprehensiveReport() {
        log.info("=== 生成综合测试报告 ===");
        
        // 基础性能测试
        long startTime = System.nanoTime();
        List<String> sample = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            sample.add(generateCode());
        }
        long duration = System.nanoTime() - startTime;
        double avgTimePerCode = duration / 1000.0 / 1_000_000; // 毫秒
        
        // 唯一性分析
        Set<String> uniqueSample = new HashSet<>(sample);
        double sampleDuplicationRate = (double) (sample.size() - uniqueSample.size()) / sample.size() * 100;
        
        // 长度和格式分析
        String sampleCode = generateCode();
        int codeLength = sampleCode.length();
        boolean hasCorrectPrefix = sampleCode.startsWith(CODE_PREFIX);
        
        // 理论分析
        double theoreticalCollisionProbability = calculateBirthdayParadoxProbability(10, 100000);
        
        log.info("=== generateCode方法综合评估报告 ===");
        log.info("基础信息:");
        log.info("  - 代码前缀: {}", CODE_PREFIX);
        log.info("  - 流水号长度: {}", codeLength);
        log.info("  - 格式正确: {}", hasCorrectPrefix);
        log.info("  - 示例流水号: {}", sampleCode);
        
        log.info("性能指标:");
        log.info("  - 平均生成时间: {:.3f}ms", avgTimePerCode);
        log.info("  - 理论最大生成速率: {:.0f} codes/sec", 1000.0 / avgTimePerCode);
        
        log.info("唯一性分析:");
        log.info("  - 样本重复率: {:.4f}% (1000个样本)", sampleDuplicationRate);
        log.info("  - 理论冲突概率: {:.4f}% (1秒内10个)", theoreticalCollisionProbability * 100);
        log.info("  - 随机数范围: 100,000 (00000-99999)");
        
        log.info("风险评估:");
        if (sampleDuplicationRate > 0.5) {
            log.error("  - 高风险：样本重复率过高，不适合生产环境");
        } else if (sampleDuplicationRate > 0.1) {
            log.warn("  - 中风险：存在一定重复概率，建议优化");
        } else {
            log.info("  - 低风险：重复概率较低，基本可用");
        }
        
        log.info("优化建议:");
        log.info("  - 建议1：使用毫秒级时间戳替代秒级");
        log.info("  - 建议2：增加机器标识或进程ID");
        log.info("  - 建议3：使用原子递增计数器");
        log.info("  - 建议4：采用UUID算法确保全局唯一性");
    }
    
    /**
     * 计算生日悖论概率
     */
    private double calculateBirthdayParadoxProbability(int n, int d) {
        if (n > d) return 1.0;
        
        double probability = 1.0;
        for (int i = 0; i < n; i++) {
            probability *= (double) (d - i) / d;
        }
        return 1.0 - probability;
    }
}