package com.usky.cdi.service.util;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 人防掩蔽单元人数模拟工具类（动态自适应版）
 * <p>
 * 核心特性：
 * 1. 即时计算：根据当前时间动态返回合理总人数，无预生成/硬编码条数限制
 * 2. 自适应调用管理器：跟踪调用频率，动态评估并自动扩容阈值
 * 3. 分时段基准曲线：每时段独立配置基准人数与波动范围，支持平滑插值
 * 4. 边界安全：防负数、防无限循环、防溢出
 *
 * @author fyc
 * @email yuchuan.fu@chinausky.com
 * @date 2026/5/30
 */
public class AirDefenseSimulator {

    // ===================== 可配置常量 =====================
    private static final int MIN_PEOPLE = 0;                    // 最小人数（禁止负数）
    private static final int DEFAULT_MAX_CALLS = 48;            // 默认最大调用次数（向下兼容）
    private static final int MAX_EXPANSION_LIMIT = 288;         // 扩容上限（=24h*12次/5min，即5分钟粒度全天）
    private static final double EXPANSION_FACTOR = 2.0;         // 每次扩容倍数
    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm");

    // ===================== 自适应调用管理器（单例状态） =====================
    /** 已调用次数计数器 */
    private static final AtomicInteger callCount = new AtomicInteger(0);
    /** 当前动态阈值（初始为默认值，可自动扩容） */
    private static final AtomicInteger dynamicThreshold = new AtomicInteger(DEFAULT_MAX_CALLS);
    /** 上次调用时间戳（用于检测异常高频调用） */
    private static final AtomicLong lastCallTimestamp = new AtomicLong(0);
    /** 是否已触发过扩容标志 */
    private static volatile boolean expanded = false;

    // ===================== 时段规则定义（基准人数 + 波动范围）=====================
    /**
     * 时段规则：[起始小时, 结束小时) → {基准人数, 最小波动, 最大波动}
     * 基准人数代表该时段中心时刻的典型值，波动范围为±随机偏移量
     */
    private static final int[][] TIME_SLOT_RULES = {
        //  起始H   结束H   基准   波动Min  波动Max   规则说明
            {0,      5,      0,     0,       0},      // 规则1: 00:00-05:00  人数为0
            {5,      6,      3,     2,       3},      // 规则2: 05:00-06:00  缓慢上升
            {6,      8,      12,    4,       6},      // 规则3: 06:00-08:00  攀升至第一高点
            {8,      9,      25,    2,       5},      // 规则3延:08:00-09:00 第一高点后短暂调整
            {9,      11,     31,    2,       5},      // 规则4: 09:00-11:00  趋于稳定
            {11,     12,     19,    1,       3},      // 规则5: 11:00-12:00  大量流出(就餐)
            {12,     13,     12,    1,       3},      // 规则5延:12:00-13:00 午间低谷
            {13,     14,     21,    3,       6},      // 规则6: 13:00-14:00  开始回流
            {14,     15,     30,    4,       7},      // 规则7: 14:00-15:00  达到第二高点
            {15,     19,     31,    2,       5},      // 规则8: 15:00-19:00  保持稳定
            {19,     21,     18,    3,       6},      // 规则9: 19:00-21:00  持续流出
            {21,     22,     12,    2,       5},      // 规则9延:21:00-22:00  继续清场
            {22,     24,     6,     1,       3}       // 规则10:22:00-24:00  逐渐降至0
    };

    // ===================== 公开API：获取当前时刻掩蔽单元内的总人数 =====================

    /**
     * 【核心方法】根据当前系统时间，动态计算并返回掩蔽单元内应有的模拟总人数。
     * <p>
     * 无需预生成数据，即时根据24小时时间规则曲线计算出合理人数，
     * 支持任意时间粒度的连续调用，通过自适应管理器自动扩容保护。
     *
     * @return 当前时刻的模拟人数（int, >= 0）
     */
    public static int getCurrentPeopleCount() {
        return calculatePeopleCount(LocalDateTime.now());
    }

    /**
     * 【核心方法-指定时间版本】根据指定时间，动态计算掩蔽单元内应有的模拟总人数。
     *
     * @param targetTime 目标时间
     * @return 该时刻的模拟人数（int, >= 0）
     */
    public static int calculatePeopleCount(LocalDateTime targetTime) {
        // 1. 自适应检查：是否需要扩容
        adaptiveCheck();

        int hour = targetTime.getHour();
        int minute = targetTime.getMinute();

        // 2. 匹配对应时段规则
        int[] rule = matchTimeRule(hour);
        int baseCount = rule[2];
        int fluctuationMin = rule[3];
        int fluctuationMax = rule[4];

        // 3. 时段内线性插值：使同一小时内不同分钟的人数更平滑自然
        // 特殊处理：基准为0的时段（如00:00-05:00无人期）不参与插值，强制保持0
        double minuteRatio = minute / 60.0;
        int interpolatedBase = (baseCount == 0) ? 0 : interpolateWithinHour(hour, baseCount, minuteRatio);

        // 4. 叠加随机波动
        int fluctuation = ThreadLocalRandom.current().nextInt(fluctuationMin, fluctuationMax + 1);
        boolean positiveTrend = isInRisingPhase(hour);
        int peopleCount = interpolatedBase + (positiveTrend ? fluctuation : -fluctuation);

        // 5. 边界约束
        peopleCount = Math.max(peopleCount, MIN_PEOPLE);

        // 6. 记录调用
        recordCall();

        return peopleCount / 4;
    }

    // ===================== 兼容接口：生成全天模拟数据列表（动态条数）=====================

    /**
     * 生成当天全天的模拟数据列表。
     * <p>
     * 条数由自适应管理器的当前阈值决定（默认48条/半小时粒度），
     * 当业务需求更高频时可自动扩容至更细粒度（如288条/5分钟粒度）。
     *
     * @return 模拟数据列表
     */
    public static List<PeopleFlowData> generateAllDayData() {
        return generateAllDayData(dynamicThreshold.get());
    }

    /**
     * 生成全天模拟数据列表，自定义数据条数。
     * <p>
     * 动态扩容策略：
     * - requestedCount <= DEFAULT_MAX_CALLS(48): 正常生成，半小时粒度
     * - 48 < requestedCount <= MAX_EXPANSION_LIMIT(288): 自动扩容，最小粒度5分钟
     * - requestedCount > 288: 截断到上限，防止内存溢出
     *
     * @param requestedCount 请求数据条数
     * @return 模拟数据列表（实际条数可能因边界截断而小于请求值）
     */
    public static List<PeopleFlowData> generateAllDayData(int requestedCount) {
        // 边界约束：防止非法参数导致问题
        int actualCount = normalizeRequestedCount(requestedCount);

        List<PeopleFlowData> dataList = new ArrayList<>(actualCount);
        long totalMinutes = 24 * 60;
        long intervalMinutes = totalMinutes / actualCount;

        LocalDateTime currentTime = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0);
        int lastPeople = 0;

        for (int i = 0; i < actualCount; i++) {
            int currentPeople = calculatePeopleCount(currentTime);

            // 反推流入流出（保持数据完整性）
            int delta = currentPeople - lastPeople;
            int inFlow, outFlow;
            if (delta >= 0) {
                inFlow = delta + ThreadLocalRandom.current().nextInt(0, 2);
                outFlow = ThreadLocalRandom.current().nextInt(0, 2);
            } else {
                inFlow = ThreadLocalRandom.current().nextInt(0, 2);
                outFlow = -delta + ThreadLocalRandom.current().nextInt(0, 2);
            }

            String timeStr = currentTime.format(TIME_FORMATTER);
            dataList.add(new PeopleFlowData(timeStr, lastPeople, inFlow, outFlow, currentPeople));

            lastPeople = currentPeople;
            currentTime = currentTime.plusMinutes(intervalMinutes);
        }
        return dataList;
    }

    // ===================== 自适应调用管理器 =====================

    /**
     * 自适应检查：在每次调用前评估是否需要触发扩容
     */
    private static void adaptiveCheck() {
        int calls = callCount.incrementAndGet();
        int threshold = dynamicThreshold.get();

        if (calls > threshold && !expanded) {
            synchronized (AirDefenseSimulator.class) {
                // 双检锁，防止并发重复扩容
                if (calls > dynamicThreshold.get() && !expanded) {
                    int newThreshold = expandThreshold(threshold);
                    dynamicThreshold.set(newThreshold);
                    expanded = true;
                }
            }
        }
    }

    /**
     * 动态扩容策略：按指数因子扩展阈值，但不超过硬性上限
     *
     * @param currentThreshold 当前阈值
     * @return 扩容后的新阈值
     */
    private static int expandThreshold(int currentThreshold) {
        int newThreshold = (int) Math.min(currentThreshold * EXPANSION_FACTOR, MAX_EXPANSION_LIMIT);
        return Math.max(newThreshold, currentThreshold + 1); // 至少+1保证递增
    }

    /**
     * 记录一次有效调用
     */
    private static void recordCall() {
        lastCallTimestamp.set(System.currentTimeMillis());
    }

    /**
     * 归置自适应管理器状态（测试/重启场景使用）
     */
    public static void resetAdaptiveManager() {
        callCount.set(0);
        dynamicThreshold.set(DEFAULT_MAX_CALLS);
        lastCallTimestamp.set(0);
        expanded = false;
    }

    // ===================== 管理器状态查询API =====================

    /** 获取已调用次数 */
    public static int getCallCount() { return callCount.get(); }

    /** 获取当前动态阈值 */
    public static int getDynamicThreshold() { return dynamicThreshold.get(); }

    /** 获取扩容上限 */
    public static int getMaxExpansionLimit() { return MAX_EXPANSION_LIMIT; }

    /** 是否已触发过扩容 */
    public static boolean isExpanded() { return expanded; }

    // ===================== 私有工具方法 =====================

    /**
     * 根据小时数匹配对应的时段规则
     */
    private static int[] matchTimeRule(int hour) {
        for (int[] rule : TIME_SLOT_RULES) {
            if (hour >= rule[0] && hour < rule[1]) {
                return rule;
            }
        }
        // 兜底：返回午夜规则
        return TIME_SLOT_RULES[0];
    }

    /**
     * 判断当前时段是否处于人数上升期（用于决定波动的正负方向）
     */
    private static boolean isInRisingPhase(int hour) {
        return (hour >= 5 && hour < 9) || (hour >= 13 && hour < 15);
    }

    /**
     * 时段内线性插值：根据分钟位置微调基准值，实现同小时内平滑过渡
     * <p>
     * 例如：6:00基准15人→8:00基准30人，6:30应约为22人而非突变为30人
     */
    private static int interpolateWithinHour(int hour, int baseCount, double minuteRatio) {
        int[] currentRule = matchTimeRule(hour);
        int nextHour = (hour + 1) % 24;
        int[] nextRule = matchTimeRule(nextHour);
        int nextBase = nextRule[2];

        // 线性插值：当前基准 + (下一基准-当前基准) * 分钟占比
        int interpolated = (int) Math.round(baseCount + (nextBase - baseCount) * minuteRatio * 0.3);
        return Math.max(interpolated, MIN_PEOPLE);
    }

    /**
     * 规范化请求数据条数：边界条件处理，防止越界
     */
    private static int normalizeRequestedCount(int requestedCount) {
        if (requestedCount <= 0) {
            return DEFAULT_MAX_CALLS; // 非法值回退默认
        }
        if (requestedCount > MAX_EXPANSION_LIMIT) {
            return MAX_EXPANSION_LIMIT; // 截断到上限
        }
        return requestedCount;
    }

    // ===================== 测试主方法 =====================
    public static void main(String[] args) {
        System.out.println("========== 自适应人防掩蔽单元人数模拟器 ==========");
        System.out.println("初始阈值: " + DEFAULT_MAX_CALLS + ", 上限: " + MAX_EXPANSION_LIMIT);

        // ========== 核心：60次模拟调用测试 ==========
        test60Calls();
    }

    /**
     * 模拟60次调用 calculatePeopleCount，每次传入不同的时间点，
     * 验证自适应扩容机制和时段规则的正确性
     */
    private static void test60Calls() {
        final int TOTAL_CALLS = 60;

        // 重置管理器，确保从干净状态开始
        resetAdaptiveManager();

        System.out.println("\n========== 【60次模拟调用测试】开始 ==========");
        System.out.printf("初始状态 → 阈值:%d, 已调用:%d, 已扩容:%s%n",
                getDynamicThreshold(), getCallCount(), isExpanded());
        printSeparator(70);
        System.out.printf("%-6s %-8s %-10s %-15s %-10s%n", "序号", "时间点", "返回人数", "时段规则描述", "当前阈值");
        printSeparator(70);

        LocalDateTime baseDate = LocalDateTime.now().toLocalDate().atStartOfDay();

        for (int i = 0; i < TOTAL_CALLS; i++) {
            // 每24分钟推进一次（60次 × 24min = 1440min = 24小时，刚好覆盖全天）
            int totalMinutesElapsed = i * 24;
            int hour = (totalMinutesElapsed / 60) % 24;
            int minute = totalMinutesElapsed % 60;

            LocalDateTime targetTime = baseDate.plusMinutes(totalMinutesElapsed);
            int peopleCount = calculatePeopleCount(targetTime);

            // 获取时段描述
            String timeSlotDesc = getTimeSlotDescription(hour);
            String timeStr = String.format("%02d:%02d", hour, minute);

            System.out.printf("%-6d %-8s %-10d %-15s %-10d%n",
                    i + 1, timeStr, peopleCount, timeSlotDesc, getDynamicThreshold());
        }

        printSeparator(70);
        System.out.printf("最终状态 → 总调用:%d, 阈值:%d, 已扩容:%b%n",
                getCallCount(), getDynamicThreshold(), isExpanded());

        // 验证结论
        if (getCallCount() >= TOTAL_CALLS) {
            System.out.println("[PASS] 60次调用全部完成，自适应机制正常工作");
        } else {
            System.out.println("[WARN] 调用次数异常: 期望" + TOTAL_CALLS + ", 实际" + getCallCount());
        }
        if (getDynamicThreshold() > DEFAULT_MAX_CALLS) {
            System.out.println("[PASS] 阈值已自动扩容: " + DEFAULT_MAX_CALLS + " → " + getDynamicThreshold()
                    + "（因调用次数超过原阈值触发）");
        }
        System.out.println("========== 【60次模拟调用测试】结束 ==========");
    }

    /**
     * Java8兼容：打印指定长度的分隔线（替代 String.repeat()）
     */
    private static void printSeparator(int length) {
        StringBuilder sb = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            sb.append('─');
        }
        System.out.println(sb.toString());
    }

    /**
     * 根据小时数返回时段规则描述（用于测试输出）
     */
    private static String getTimeSlotDescription(int hour) {
        if (hour < 5) return "00-05无人";
        if (hour < 6) return "05-06缓升";
        if (hour < 8) return "06-08第一高点";
        if (hour < 9) return "08-09调整";
        if (hour < 11) return "09-11稳定";
        if (hour < 12) return "11-12就餐流出";
        if (hour < 13) return "12-13午间低谷";
        if (hour < 14) return "13-14回流";
        if (hour < 15) return "14-15第二高点";
        if (hour < 19) return "15-19稳定";
        if (hour < 21) return "19-21持续流出";
        if (hour < 22) return "21-22清场";
        return "22-00降至0";
    }
}
