import os
import pickle
import glob

def load_and_merge_pickle_chunks(folder_path, file_pattern="*.pkl"):
    """
    从指定文件夹中读取所有匹配的分片文件，解包并合并成一个总的数据列表。
    
    Args:
        folder_path: 存放 .pkl 分片文件的文件夹路径
        file_pattern: 文件匹配模式，默认匹配所有 .pkl 文件
    """
    all_episodes = []
    
    # 获取所有匹配的 pkl 文件路径，并按名称排序（确保 part1, part2 顺序或逻辑清晰）
    search_path = os.path.join(folder_path, file_pattern)
    file_list = sorted(glob.glob(search_path))
    
    if not file_list:
        print(f"❌ 未在路径 【{folder_path}】 下找到任何匹配 【{file_pattern}】 的文件！")
        return []

    print(f"📂 找到 {len(file_list)} 个数据分片文件，开始加载...")
    
    for file_path in file_list:
        try:
            with open(file_path, 'rb') as f:
                # 每个分片解包出来都是一个 list [ep1, ep2, ...]
                chunk_data = pickle.load(f)
                
                if isinstance(chunk_data, list):
                    all_episodes.extend(chunk_data)
                    print(f"  ✅ 成功加载: {os.path.basename(file_path)} (包含 {len(chunk_data)} 个 Episode)")
                else:
                    print(f"  ⚠️ 警告: {os.path.basename(file_path)} 解析出的数据格式不是列表，跳过。")
        except Exception as e:
            print(f"  ❌ 读取文件 {os.path.basename(file_path)} 失败: {e}")
            
    print(f"整个序列加载完成，共合并了 {len(all_episodes)} 个 Episode。")
    return all_episodes


def analyze_episodes_data(episode_data_raw):
    """
    分析 Episode 数据，统计超调情况。
    """
    total_episodes = len(episode_data_raw)
    if total_episodes == 0:
        print("没有数据可供分析。")
        return

    invalid_count = 0            # 最后一步误差绝对值 > 2 kPa 的无效 episode
    invalid_high_flow = 0        # 无效 episode 中流量 > 200
    invalid_low_flow = 0         # 无效 episode 中流量 < 100
    all_steady_abs_errors = []   # 所有有效 episode 的稳态误差（绝对值）
    no_overshoot_count = 0
    no_overshoot_abs_errors = []  # 绝对值稳态误差
    no_overshoot_raw_errors = []  # 带符号稳态误差（+ = 高于目标, - = 低于目标）
    overshoot_lt_1_count = 0
    overshoot_1_to_2_count = 0
    overshoot_2_to_3_count = 0
    overshoot_3_to_4_count = 0
    overshoot_4_to_5_count = 0
    overshoot_5_to_10_count = 0
    overshoot_gt_10_count = 0
    overshoots_5_to_10 = []
    overshoots_gt_10 = []

    for idx, ep in enumerate(episode_data_raw):
        pressures = ep.get('pressures', [])
        target_p = ep.get('target_pressure', 0.0)
        
        if not pressures:
            continue

        # 最后一步误差绝对值 > 2 kPa → 无效 episode，跳过
        errors = ep.get('errors', [])
        if errors and abs(errors[-1]) > 2:
            invalid_count += 1
            q = ep.get('Q_in', 0)
            if q > 200:
                invalid_high_flow += 1
            elif q < 100:
                invalid_low_flow += 1
            continue

        initial_p = pressures[0]

        # 所有有效 episode 的稳态误差（最后 30 步绝对值均值）
        if errors:
            last_n = errors[-30:] if len(errors) >= 30 else errors
            all_steady_abs_errors.append(sum(abs(e) for e in last_n) / len(last_n))

        is_step_up = target_p >= initial_p  # 升压为 True，降压为 False
        overshoot = 0.0
        
        if is_step_up:
            # 升压：最大值大于目标压力为超调
            max_p = max(pressures)
            if max_p > target_p:
                overshoot = max_p - target_p
        else:
            # 降压：最小值小于目标压力为超调
            min_p = min(pressures)
            if min_p < target_p:
                overshoot = target_p - min_p

        # 统计区间
        if overshoot == 0:
            no_overshoot_count += 1
        elif overshoot < 1.0:
            overshoot_lt_1_count += 1
            # 最后 30 步的平均误差作为稳态误差（分别记录绝对值和带符号值）
            if len(errors) >= 30:
                last_30 = errors[-30:]
            elif errors:
                last_30 = errors
            else:
                last_30 = []
            if last_30:
                no_overshoot_abs_errors.append(sum(abs(e) for e in last_30) / len(last_30))
                no_overshoot_raw_errors.append(sum(last_30) / len(last_30))
        elif 1.0 <= overshoot < 2.0:
            overshoot_1_to_2_count += 1
        elif 2.0 <= overshoot < 3.0:
            overshoot_2_to_3_count += 1
        elif 3.0 <= overshoot < 4.0:
            overshoot_3_to_4_count += 1
        elif 4.0 <= overshoot <= 5.0:
            overshoot_4_to_5_count += 1
        else:
            item = {
                "index": idx,
                "direction": "升压" if is_step_up else "降压",
                "initial_p": initial_p,
                "target_p": target_p,
                "overshoot_value": round(overshoot, 3),
                "Q_in": ep.get("Q_in", 0),
            }
            if overshoot <= 10.0:
                overshoot_5_to_10_count += 1
                overshoots_5_to_10.append(item)
            else:
                overshoot_gt_10_count += 1
                overshoots_gt_10.append(item)

    # 打印报告
    def _pct(n): return f"{n / total_episodes * 100:.1f}%"

    print("\n" + "="*25 + " 离线数据分析 " + "="*25)
    valid_episodes = total_episodes - invalid_count
    print(f"合并后的总 Episode 数        : {total_episodes}")
    print(f"  - 无效 Episode（末步误差>2）: {invalid_count}  ({_pct(invalid_count)})")
    if invalid_count > 0:
        print(f"      ├ 流量 > 200 L/min : {invalid_high_flow}")
        print(f"      └ 流量 < 100 L/min : {invalid_low_flow}")
    print(f"  - 有效 Episode 数          : {valid_episodes}")
    print(f"  - 未超调的 Episode 数      : {no_overshoot_count}  ({_pct(no_overshoot_count)})")
    print(f"  - 超调 < 1 kPa             : {overshoot_lt_1_count}  ({_pct(overshoot_lt_1_count)})")
    print(f"  - 超调在 1 ~ 2 kPa 之间    : {overshoot_1_to_2_count}  ({_pct(overshoot_1_to_2_count)})")
    print(f"  - 超调在 2 ~ 3 kPa 之间    : {overshoot_2_to_3_count}  ({_pct(overshoot_2_to_3_count)})")
    print(f"  - 超调在 3 ~ 4 kPa 之间    : {overshoot_3_to_4_count}  ({_pct(overshoot_3_to_4_count)})")
    print(f"  - 超调在 4 ~ 5 kPa 之间    : {overshoot_4_to_5_count}  ({_pct(overshoot_4_to_5_count)})")
    print(f"  - 超调在 5 ~ 10 kPa 之间   : {overshoot_5_to_10_count}  ({_pct(overshoot_5_to_10_count)})")
    print(f"  - 超调 > 10 kPa           : {overshoot_gt_10_count}  ({_pct(overshoot_gt_10_count)})")
    print("=" * 68)

    def _print_detail(title, items):
        if items:
            print(f"\n[⚠️ {title}]:")
            for item in items:
                print(f"  * Episode [{item['index']}] ({item['direction']}): "
                      f"初始 {item['initial_p']:.2f} -> 目标 {item['target_p']:.2f} | "
                      f"超调量: {item['overshoot_value']:.2f} kPa | "
                      f"流量: {item['Q_in']:.1f} L/min")

    _print_detail("超调在 5 ~ 10 kPa", overshoots_5_to_10)
    _print_detail("超调大于 10 kPa", overshoots_gt_10)

    if not overshoots_5_to_10 and not overshoots_gt_10:
        print("\n🎉 极好！没有发现超调大于 5 kPa 的数据。")

    # ---- 流量分布统计 ----
    flow_bins = [
        (0, 10), (10, 50), (50, 100), (100, 150),
        (150, 200), (200, 250), (250, 300),
    ]
    flow_counts = {f"{lo}~{hi}": 0 for lo, hi in flow_bins}
    flow_counts["300+"] = 0

    for ep in episode_data_raw:
        q = ep.get('Q_in', 0)
        placed = False
        for lo, hi in flow_bins:
            if lo <= q < hi:
                flow_counts[f"{lo}~{hi}"] += 1
                placed = True
                break
        if not placed:
            flow_counts["300+"] += 1

    print(f"\n📊 流量分布统计 (共 {total_episodes} 个 Episode):")
    for lo, hi in flow_bins:
        label = f"{lo}~{hi}"
        print(f"    {label:>10} L/min : {flow_counts[label]:>5}  ({flow_counts[label]/total_episodes*100:5.1f}%)")
    print(f"    {'300+':>10} L/min : {flow_counts['300+']:>5}  ({flow_counts['300+']/total_episodes*100:5.1f}%)")

    if all_steady_abs_errors:
        avg_all = sum(all_steady_abs_errors) / len(all_steady_abs_errors)
        print(f"\n📊 所有有效 Episode 平均稳态误差（最后 30 步绝对值均值）: {avg_all:.3f} kPa"
              f"  ({len(all_steady_abs_errors)} 个 Episode)")

    if no_overshoot_abs_errors:
        avg_abs = sum(no_overshoot_abs_errors) / len(no_overshoot_abs_errors)
        avg_raw = sum(no_overshoot_raw_errors) / len(no_overshoot_raw_errors)
        print(f"\n📊 超调0～1kpa Episode 平均稳态误差（最后 30 步）:")
        print(f"    绝对值均值 : {avg_abs:.3f} kPa")
        print(f"    带符号均值 : {avg_raw:.3f} kPa  ({'偏高于目标' if avg_raw > 0 else '偏低' if avg_raw < 0 else '无偏'})"
              f"  ({no_overshoot_count} 个 Episode)")


def print_episode_detail(episode_data_raw, index):
    """打印指定 episode 的完整数据"""
    if index < 0 or index >= len(episode_data_raw):
        print(f"❌ Episode 索引 {index} 超出范围 (0~{len(episode_data_raw)-1})")
        return

    ep = episode_data_raw[index]
    print(f"\n{'='*60}")
    print(f"  Episode [{index}] 完整数据")
    print(f"{'='*60}")

    for key in ['Q_in', 'volume', 'target_pressure', 'mode']:
        if key in ep:
            print(f"  {key}: {ep[key]}")

    pressures = ep.get('pressures', [])
    errors = ep.get('errors', [])
    valve_openings = ep.get('valves', [])

    print(f"\n  步数: {len(pressures)}")
    if pressures:
        print(f"  初始压力: {pressures[0]:.2f} kPa")
        print(f"  最终压力: {pressures[-1]:.2f} kPa")
    print(f"  目标压力: {ep.get('target_pressure', 'N/A')} kPa")
    if errors:
        print(f"  最终误差: {errors[-1]:.3f} kPa")

    print(f"\n  {'步':>4s} {'压力(kPa)':>10s} {'误差(kPa)':>10s} {'开度(%)':>8s}")
    print(f"  {'-'*36}")
    n = len(pressures)
    for i in range(n):
        p = pressures[i]
        e = errors[i] if i < len(errors) else float('nan')
        vo = valve_openings[i] if i < len(valve_openings) else float('nan')
        print(f"  {i:4d} {p:10.2f} {e:10.3f} {vo:8.2f}")
    print(f"{'='*60}\n")


# --- 执行离线分析 ---
if __name__ == "__main__":
    # 💡 数据存放文件夹路径
    DATA_FOLDER = "/Users/menglingrui/Documents/DominatedConvergence/cloud_down_file/永久/data_8L"

    # 1. 读取并合并分片
    merged_data = load_and_merge_pickle_chunks(DATA_FOLDER, file_pattern="*part*.pkl")

    # 2. 执行分析
    if merged_data:
        analyze_episodes_data(merged_data)
        # 3. 找出无效 episode（末步误差绝对值 > 2 kPa），打印前 3 个的完整数据
        # invalid_indices = []
        # for idx, ep in enumerate(merged_data):
        #     errors = ep.get('errors', [])
        #     if errors and abs(errors[-1]) > 2:
        #         invalid_indices.append(idx)
        #         if len(invalid_indices) >= 3:
        #             break
        # if invalid_indices:
        #     print(f"\n找到 {len(invalid_indices)} 个无效 Episode，索引: {invalid_indices}")
        #     for idx in invalid_indices:
        #         print_episode_detail(merged_data, idx)
        # else:
        #     print("\n未找到无效 Episode")
        print_episode_detail(merged_data, 2500)