package com.ycwl.basic.pricing.service;

import org.junit.jupiter.api.Test;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * 独立的流水号生成测试（不依赖Spring）
 * 专门测试generateCode方法的重复率和性能
 */
public class CodeGenerationStandaloneTest {

    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;
    }

    /**
     * 测试单线程环境下快速生成10个流水号的重复率
     */
    @Test
    public void testSingleThreadDuplication() {
        System.out.println("=== 开始单线程重复率测试 ===");
        
        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;
                System.out.printf("第%d轮发现%d个重复: %s%n", round + 1, duplicates, codeList);
            }
            
            totalCodes += codesPerRound;
            
            // 稍微休息一下
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
        
        double duplicationRate = (double) totalDuplicates / totalCodes * 100;
        System.out.println("=== 单线程测试结果 ===");
        System.out.printf("总轮数: %d%n", totalRounds);
        System.out.printf("每轮生成数: %d%n", codesPerRound);
        System.out.printf("总生成数: %d%n", totalCodes);
        System.out.printf("总重复数: %d%n", totalDuplicates);
        System.out.printf("重复率: %.4f%%%n", duplicationRate);
    }

    /**
     * 高并发多线程测试
     */
    @Test
    public void testHighConcurrency() throws InterruptedException {
        System.out.println("=== 开始高并发测试 ===");
        
        int threadCount = 10;           // 10个并发线程
        int codesPerThread = 10;        // 每个线程生成10个流水号
        int totalExpectedCodes = threadCount * codesPerThread;
        
        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
        CountDownLatch latch = new CountDownLatch(threadCount);
        List<String> allCodesList = Collections.synchronizedList(new ArrayList<>());
        
        // 启动所有线程
        for (int i = 0; i < threadCount; i++) {
            final int threadId = i;
            executor.submit(() -> {
                try {
                    // 每个线程快速生成流水号
                    for (int j = 0; j < codesPerThread; j++) {
                        String code = generateCode();
                        allCodesList.add(code);
                    }
                    
                    System.out.printf("线程%d完成%n", threadId);
                    
                } finally {
                    latch.countDown();
                }
            });
        }
        
        // 等待所有线程完成
        boolean finished = latch.await(10, TimeUnit.SECONDS);
        executor.shutdown();
        
        if (!finished) {
            System.err.println("测试超时！");
            return;
        }
        
        // 分析结果
        Set<String> uniqueCodes = new HashSet<>(allCodesList);
        int duplicates = totalExpectedCodes - uniqueCodes.size();
        double duplicationRate = (double) duplicates / totalExpectedCodes * 100;
        
        // 找出重复的流水号
        Map<String, Long> codeCount = allCodesList.stream()
                .collect(Collectors.groupingBy(code -> code, Collectors.counting()));
        
        List<Map.Entry<String, Long>> duplicatedCodes = codeCount.entrySet().stream()
                .filter(entry -> entry.getValue() > 1)
                .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
                .limit(10)
                .toList();
        
        System.out.println("=== 高并发测试结果 ===");
        System.out.printf("并发线程数: %d%n", threadCount);
        System.out.printf("每线程生成数: %d%n", codesPerThread);
        System.out.printf("预期总数: %d%n", totalExpectedCodes);
        System.out.printf("实际总数: %d%n", allCodesList.size());
        System.out.printf("唯一流水号数: %d%n", uniqueCodes.size());
        System.out.printf("重复数: %d%n", duplicates);
        System.out.printf("重复率: %.4f%%%n", duplicationRate);
        
        if (!duplicatedCodes.isEmpty()) {
            System.out.println("=== 发现重复流水号 ===");
            duplicatedCodes.forEach(entry -> 
                System.out.printf("流水号: %s 重复了 %d 次%n", entry.getKey(), entry.getValue()));
        }
        
        if (duplicationRate > 1.0) {
            System.err.println("严重警告：高并发下重复率超过1.0%，必须优化generateCode方法！");
        }
    }

    /**
     * 严格1秒内生成测试
     */
    @Test
    public void testOneSecondGeneration() {
        System.out.println("=== 开始1秒内生成测试 ===");
        
        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);
        }
        
        long duration = System.currentTimeMillis() - startTime;
        int duplicates = codeList.size() - codes.size();
        double duplicationRate = (double) duplicates / codeList.size() * 100;
        
        System.out.println("=== 1秒内生成测试结果 ===");
        System.out.printf("测试时长: %d ms%n", duration);
        System.out.printf("总生成数: %d%n", codeList.size());
        System.out.printf("唯一数: %d%n", codes.size());
        System.out.printf("重复数: %d%n", duplicates);
        System.out.printf("重复率: %.4f%%%n", duplicationRate);
        System.out.printf("生成速率: %.1f codes/sec%n", (double) codeList.size() / duration * 1000);
        
        if (duplicates > 0) {
            // 找出重复的流水号
            Map<String, Long> codeCount = codeList.stream()
                    .collect(Collectors.groupingBy(code -> code, Collectors.counting()));
            
            codeCount.entrySet().stream()
                    .filter(entry -> entry.getValue() > 1)
                    .limit(10)
                    .forEach(entry -> 
                        System.out.printf("重复流水号: %s (出现%d次)%n", entry.getKey(), entry.getValue()));
        }
    }

    /**
     * 综合评估报告
     */
    @Test
    public void generateReport() {
        System.out.println("=== generateCode方法综合评估报告 ===");
        
        // 基础性能测试
        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);
        
        System.out.println("基础信息:");
        System.out.printf("  - 代码前缀: %s%n", CODE_PREFIX);
        System.out.printf("  - 流水号长度: %d%n", codeLength);
        System.out.printf("  - 格式正确: %b%n", hasCorrectPrefix);
        System.out.printf("  - 示例流水号: %s%n", sampleCode);
        
        System.out.println("性能指标:");
        System.out.printf("  - 平均生成时间: %.3f ms%n", avgTimePerCode);
        System.out.printf("  - 理论最大生成速率: %.0f codes/sec%n", 1000.0 / avgTimePerCode);
        
        System.out.println("唯一性分析:");
        System.out.printf("  - 样本重复率: %.4f%% (1000个样本)%n", sampleDuplicationRate);
        System.out.printf("  - 理论冲突概率: %.4f%% (1秒内10个)%n", theoreticalCollisionProbability * 100);
        System.out.println("  - 随机数范围: 100,000 (00000-99999)");
        
        System.out.println("风险评估:");
        if (sampleDuplicationRate > 0.5) {
            System.err.println("  - 高风险：样本重复率过高，不适合生产环境");
        } else if (sampleDuplicationRate > 0.1) {
            System.out.println("  - 中风险：存在一定重复概率，建议优化");
        } else {
            System.out.println("  - 低风险：重复概率较低，基本可用");
        }
        
        System.out.println("优化建议:");
        System.out.println("  - 建议1：使用毫秒级时间戳替代秒级");
        System.out.println("  - 建议2：增加机器标识或进程ID");
        System.out.println("  - 建议3：使用原子递增计数器");
        System.out.println("  - 建议4：采用UUID算法确保全局唯一性");
        
        // 显示一些示例流水号
        System.out.println("示例流水号:");
        for (int i = 0; i < 10; i++) {
            System.out.printf("  %s%n", generateCode());
        }
    }
    
    /**
     * 计算生日悖论概率
     */
    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;
    }
}