#!/usr/bin/env python3
"""
台電事故停電分析報告 v4 - 教育版
深色主題 + 圖表 + 教育版風格
"""

import pandas as pd
import re
from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.chart import XL_CHART_TYPE, XL_LEGEND_POSITION, XL_LABEL_POSITION
from pptx.chart.data import CategoryChartData
from collections import OrderedDict
import os

# ─── Color Palette ───
BG_COLOR = RGBColor(0x1B, 0x1B, 0x2F)
BG_LIGHT = RGBColor(0x24, 0x24, 0x3E)
ACCENT = RGBColor(0x00, 0xD2, 0xFF)   # Cyan
ACCENT2 = RGBColor(0xFF, 0x6B, 0x6B)  # Coral
ACCENT3 = RGBColor(0x51, 0xCF, 0x66)  # Green
ACCENT4 = RGBColor(0xFF, 0xD4, 0x3B)  # Yellow
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
GRAY = RGBColor(0xAA, 0xAA, 0xBB)
LIGHT_GRAY = RGBColor(0xDD, 0xDD, 0xEE)
DARK_TEXT = RGBColor(0x1B, 0x1B, 0x2F)

# Chart colors
CHART_COLORS = [
    RGBColor(0x00, 0xD2, 0xFF),  # Cyan - 113年
    RGBColor(0xFF, 0x6B, 0x6B),  # Coral - 114年
    RGBColor(0x51, 0xCF, 0x66),  # Green
    RGBColor(0xFF, 0xD4, 0x3B),  # Yellow
    RGBColor(0xCC, 0x5D, 0xE8),  # Purple
]

# Reason labels
REASON_LABELS = {1: '樹竹外物觸碰', 2: '電纜不良', 3: '設備不良', 4: '用戶設備不良', 5: '其他'}

# ─── Data Loading ───
EXCEL_PATH = '/Users/lingochen/Desktop/台電事故停電紀錄表-113年~114年.xlsx'
OUTPUT_PATH = '/Users/lingochen/Desktop/台電事故停電分析報告_v4.pptx'

def load_data():
    s1 = pd.read_excel(EXCEL_PATH, sheet_name=0, header=0, usecols=[2,4,5,12])
    s2 = pd.read_excel(EXCEL_PATH, sheet_name=1, header=0, usecols=[2,4,5,12])
    s1.columns = ['time','district','households','reason']
    s2.columns = ['time','district','households','reason']
    s1 = s1[s1['time'].astype(str).str.match(r'^113/\d{2}/\d{2}')]
    s2 = s2[s2['time'].astype(str).str.match(r'^114/\d{2}/\d{2}')]
    
    def parse_reason(r):
        if pd.isna(r): return None
        m = re.search(r'(\d+)', str(r))
        return int(m.group(1)) if m else None
    
    s1['reason_cat'] = s1['reason'].apply(parse_reason)
    s2['reason_cat'] = s2['reason'].apply(parse_reason)
    s1['month'] = s1['time'].apply(lambda x: int(str(x).split('/')[1]))
    s2['month'] = s2['time'].apply(lambda x: int(str(x).split('/')[1]))
    
    return s1, s2

# ─── Helper functions ───
def set_slide_bg(slide, color=BG_COLOR):
    bg = slide.background
    fill = bg.fill
    fill.solid()
    fill.fore_color.rgb = color

def add_text_box(slide, left, top, width, height, text, font_size=14, color=WHITE, bold=False, alignment=PP_ALIGN.LEFT, font_name='Microsoft JhengHei'):
    txBox = slide.shapes.add_textbox(Inches(left), Inches(top), Inches(width), Inches(height))
    tf = txBox.text_frame
    tf.word_wrap = True
    p = tf.paragraphs[0]
    p.text = text
    p.font.size = Pt(font_size)
    p.font.color.rgb = color
    p.font.bold = bold
    p.font.name = font_name
    p.alignment = alignment
    return txBox

def add_multiline_text(slide, left, top, width, height, lines, font_size=12, color=WHITE, line_spacing=1.5, font_name='Microsoft JhengHei'):
    """Add a text box with multiple lines. lines = list of (text, color, bold)"""
    txBox = slide.shapes.add_textbox(Inches(left), Inches(top), Inches(width), Inches(height))
    tf = txBox.text_frame
    tf.word_wrap = True
    for i, line in enumerate(lines):
        if isinstance(line, tuple):
            text, c, b = line
        else:
            text, c, b = line, WHITE, False
        if i == 0:
            p = tf.paragraphs[0]
        else:
            p = tf.add_paragraph()
        p.text = text
        p.font.size = Pt(font_size)
        p.font.color.rgb = c
        p.font.bold = b
        p.font.name = font_name
        p.space_after = Pt(font_size * 0.3)
    return txBox

def add_title_bar(slide, title, subtitle='', page_num=''):
    """Add a colored title bar at top"""
    # Title bar background
    shape = slide.shapes.add_shape(
        1, Inches(0), Inches(0), Inches(10), Inches(0.9)
    )
    shape.fill.solid()
    shape.fill.fore_color.rgb = RGBColor(0x00, 0x99, 0xBB)
    shape.line.fill.background()
    
    add_text_box(slide, 0.3, 0.1, 8, 0.6, title, 24, WHITE, True)
    if subtitle:
        add_text_box(slide, 0.3, 0.55, 8, 0.35, subtitle, 11, RGBColor(0xCC, 0xEE, 0xFF))
    if page_num:
        add_text_box(slide, 9.0, 0.3, 0.9, 0.4, page_num, 11, RGBColor(0xCC, 0xEE, 0xFF), alignment=PP_ALIGN.RIGHT)

def add_education_box(slide, left, top, width, height, title, lines, title_color=ACCENT4):
    """Add an education/observation box"""
    # Background
    shape = slide.shapes.add_shape(1, Inches(left), Inches(top), Inches(width), Inches(height))
    shape.fill.solid()
    shape.fill.fore_color.rgb = RGBColor(0x2A, 0x2A, 0x4A)
    shape.line.color.rgb = title_color
    shape.line.width = Pt(1.5)
    
    # Title
    add_text_box(slide, left + 0.15, top + 0.05, width - 0.3, 0.35, title, 12, title_color, True)
    
    # Content
    content_lines = [(l, LIGHT_GRAY, False) for l in lines]
    add_multiline_text(slide, left + 0.15, top + 0.35, width - 0.3, height - 0.45, content_lines, 10, LIGHT_GRAY)

def add_table_slide(slide, left, top, width, rows_data, col_widths, header_color=RGBColor(0x00, 0x99, 0xBB)):
    """Add a styled table"""
    rows = len(rows_data)
    cols = len(rows_data[0])
    table_shape = slide.shapes.add_table(rows, cols, Inches(left), Inches(top), Inches(width), Inches(0.3 * rows))
    table = table_shape.table
    
    # Set column widths
    for i, w in enumerate(col_widths):
        table.columns[i].width = Inches(w)
    
    for r, row_data in enumerate(rows_data):
        for c, cell_val in enumerate(row_data):
            cell = table.cell(r, c)
            cell.text = str(cell_val)
            for paragraph in cell.text_frame.paragraphs:
                paragraph.font.size = Pt(10)
                paragraph.font.name = 'Microsoft JhengHei'
                if r == 0:
                    paragraph.font.color.rgb = WHITE
                    paragraph.font.bold = True
                    paragraph.alignment = PP_ALIGN.CENTER
                else:
                    paragraph.font.color.rgb = LIGHT_GRAY
                    if c > 0 and any(ch.isdigit() for ch in str(cell_val)):
                        paragraph.alignment = PP_ALIGN.RIGHT
            
            # Cell fill
            if r == 0:
                cell.fill.solid()
                cell.fill.fore_color.rgb = header_color
            elif r % 2 == 0:
                cell.fill.solid()
                cell.fill.fore_color.rgb = RGBColor(0x2A, 0x2A, 0x4A)
            else:
                cell.fill.solid()
                cell.fill.fore_color.rgb = RGBColor(0x1F, 0x1F, 0x3A)
    
    return table_shape

# ─── Build Presentation ───
def build_presentation(s1, s2):
    prs = Presentation()
    prs.slide_width = Inches(10)
    prs.slide_height = Inches(7.5)
    
    # ===== Compute all statistics =====
    n1, n2 = len(s1), len(s2)
    hh1, hh2 = int(s1['households'].sum()), int(s2['households'].sum())
    change_pct = (n2 - n1) / n1 * 100
    hh_change = (hh2 - hh1) / hh1 * 100
    
    # All districts
    all_dists = sorted(set(list(s1['district'].unique()) + list(s2['district'].unique())))
    # Merge 龜山區 variants
    all_dists_clean = []
    seen = set()
    for d in all_dists:
        base = d.replace('(北西區)', '')
        if base not in seen:
            all_dists_clean.append(base)
            seen.add(base)
    all_dists = all_dists_clean
    
    # District counts
    d1 = s1['district'].value_counts()
    d2 = s2['district'].value_counts()
    
    # Household by district
    hd1 = s1.groupby('district')['households'].sum()
    hd2 = s2.groupby('district')['households'].sum()
    # Merge 龜山區 variants
    if '龜山區(北西區)' in hd2:
        if '龜山區' in hd2:
            hd2['龜山區'] += hd2['龜山區(北西區)']
        else:
            hd2['龜山區'] = hd2['龜山區(北西區)']
        hd2 = hd2.drop('龜山區(北西區)', errors='ignore')
    if '龜山區(北西區)' in d2:
        if '龜山區' in d2:
            d2['龜山區'] += d2['龜山區(北西區)']
        else:
            d2['龜山區'] = d2['龜山區(北西區)']
        d2 = d2.drop('龜山區(北西區)', errors='ignore')
    
    # Sort districts by 114 count
    dist_sorted = sorted(all_dists, key=lambda d: d2.get(d, 0), reverse=True)
    
    # Reason counts
    r1 = s1['reason_cat'].value_counts().sort_index()
    r2 = s2['reason_cat'].value_counts().sort_index()
    hr1 = s1.groupby('reason_cat')['households'].sum().sort_index()
    hr2 = s2.groupby('reason_cat')['households'].sum().sort_index()
    
    # Month counts
    m1 = s1['month'].value_counts().sort_index()
    m2 = s2['month'].value_counts().sort_index()
    
    # TOP5 high-impact
    top5_1 = s1.sort_values('households', ascending=False).head(5)
    top5_2 = s2.sort_values('households', ascending=False).head(5)
    
    reason_map = {1: '樹竹外物', 2: '電纜不良', 3: '設備不良', 4: '用戶設備', 5: '其他'}
    
    # ═══════════════════════════════════════
    # PAGE 1: Cover
    # ═══════════════════════════════════════
    slide = prs.slides.add_slide(prs.slide_layouts[6])  # blank
    set_slide_bg(slide)
    
    # Decorative line
    shape = slide.shapes.add_shape(1, Inches(0.5), Inches(2.5), Inches(9), Inches(0.05))
    shape.fill.solid()
    shape.fill.fore_color.rgb = ACCENT
    shape.line.fill.background()
    
    add_text_box(slide, 0.5, 1.5, 9, 1.0, '台電事故停電分析報告', 40, WHITE, True, PP_ALIGN.CENTER)
    add_text_box(slide, 0.5, 2.7, 9, 0.6, '113年(2024) vs 114年(2025)  深度量化比較分析', 18, ACCENT, False, PP_ALIGN.CENTER)
    add_text_box(slide, 0.5, 3.5, 9, 0.5, '── 教育版 ──', 14, ACCENT4, False, PP_ALIGN.CENTER)
    add_text_box(slide, 0.5, 4.2, 9, 0.5, f'樣本範圍：113年 {n1}件 ｜ 114年 {n2}件 ｜ 總計 {n1+n2}件停電事件', 13, GRAY, False, PP_ALIGN.CENTER)
    add_text_box(slide, 0.5, 4.7, 9, 0.5, f'分析期間：113/01～113/12  vs  114/01～114/12', 13, GRAY, False, PP_ALIGN.CENTER)
    
    add_education_box(slide, 2.0, 5.5, 6.0, 1.2, '📝 學習導引', [
        '本報告以教育訓練為目的，透過數據分析協助了解停電事故特徵',
        '每頁附有「觀察重點」與「學習要點」，引導深入思考'
    ], ACCENT3)
    
    # ═══════════════════════════════════════
    # PAGE 2: Analysis Framework
    # ═══════════════════════════════════════
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide)
    add_title_bar(slide, '分析架構與研究範疇', '五大分析面向 × 數據驅動', '02')
    
    modules = [
        ('一、營運健康度總覽', '年度對比、高影響事件、月份趨勢', ACCENT),
        ('二、行政區熱點分析', '停電次數 TOP10、受影響戶數 TOP10、年度變化', ACCENT2),
        ('三、發生成因分析', '原因分類統計、各原因詳細分析、交叉比較', ACCENT3),
        ('四、應變效率診斷', '復電時間分析、區域效率比較', ACCENT4),
        ('五、未來精進建議', '數據驅動的改善方向與行動建議', RGBColor(0xCC, 0x5D, 0xE8)),
    ]
    
    for i, (title, desc, color) in enumerate(modules):
        y = 1.3 + i * 1.1
        # Module box
        shape = slide.shapes.add_shape(1, Inches(0.5), Inches(y), Inches(9), Inches(0.95))
        shape.fill.solid()
        shape.fill.fore_color.rgb = RGBColor(0x24, 0x24, 0x3E)
        shape.line.color.rgb = color
        shape.line.width = Pt(2)
        
        add_text_box(slide, 0.8, y + 0.1, 4, 0.4, title, 16, color, True)
        add_text_box(slide, 0.8, y + 0.5, 8, 0.4, desc, 12, GRAY)
    
    add_education_box(slide, 0.5, 6.8, 9.0, 0.6, '🔍 觀察重點', [
        '本報告聚焦桃園區配電系統事故停電，已排除颱風、雙地區、自用設備不良、新北市案件'
    ], ACCENT4)
    
    # ═══════════════════════════════════════
    # PAGE 3: Executive Summary
    # ═══════════════════════════════════════
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide)
    add_title_bar(slide, '執行摘要與關鍵發現', '數據一覽：年度變化速覽', '03')
    
    # Key metrics
    metrics = [
        ('停電次數', f'{n1}', f'{n2}', f'+{change_pct:.0f}%' if change_pct > 0 else f'{change_pct:.0f}%', ACCENT2),
        ('受影響戶數', f'{hh1:,}', f'{hh2:,}', f'+{hh_change:.0f}%' if hh_change > 0 else f'{hh_change:.0f}%', ACCENT2),
        ('每月平均次數', f'{n1/12:.1f}', f'{n2/12:.1f}', f'+{(n2-n1)/12:.1f}', ACCENT2 if n2>n1 else ACCENT3),
        ('每月平均戶數', f'{hh1/12:,.0f}', f'{hh2/12:,.0f}', f'+{(hh2-hh1)/12:,.0f}', ACCENT2 if hh2>hh1 else ACCENT3),
    ]
    
    add_text_box(slide, 0.5, 1.2, 9, 0.4, '▎ 年度關鍵指標對比', 16, ACCENT, True)
    
    # Table
    rows_data = [['指標', '113年(2024)', '114年(2025)', '變化']]
    for label, v1, v2, chg, _ in metrics:
        rows_data.append([label, v1, v2, chg])
    add_table_slide(slide, 0.5, 1.7, 9, rows_data, [2.5, 2.0, 2.0, 2.5])
    
    # Key findings
    add_text_box(slide, 0.5, 3.8, 9, 0.4, '▎ 關鍵發現', 16, ACCENT, True)
    
    # Top3 findings
    findings = [
        f'① 停電次數從 {n1}件增至 {n2}件，增幅 {change_pct:.0f}%，反映配電設備負載壓力持續上升',
        f'② 受影響戶數從 {hh1:,}戶增至 {hh2:,}戶，增幅 {hh_change:.0f}%，單次事件平均影響範圍擴大',
        f'③ 114年7月單月 {m2.get(7,0)}件為最高峰，與夏季用電高峰及氣候因素高度相關',
    ]
    lines = [(f, LIGHT_GRAY, False) for f in findings]
    add_multiline_text(slide, 0.7, 4.3, 8.5, 2.0, lines, 12, LIGHT_GRAY)
    
    add_education_box(slide, 0.5, 6.0, 9.0, 1.2, '📝 學習要點', [
        '• 營運健康度指標是評估配電系統穩定性的核心依據',
        '• 次數與戶數的變化需交叉解讀：次數增加不必然代表品質惡化，也可能反映監測更完善',
        '• 季節性因素（夏季用電高峰）是分析停電趨勢時的重要參考維度'
    ], ACCENT3)
    
    # ═══════════════════════════════════════
    # PAGE 4: 營運健康度總覽 - 年度對比
    # ═══════════════════════════════════════
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide)
    add_title_bar(slide, '一、營運健康度總覽', '年度綜合指標對比分析', '04')
    
    # Summary table
    add_text_box(slide, 0.5, 1.2, 9, 0.4, '▎ 年度營運健康度對比', 14, ACCENT, True)
    
    reason_labels_full = {1: '樹竹外物觸碰', 2: '電纜不良', 3: '設備不良', 4: '用戶設備不良', 5: '其他'}
    rows_data = [['項目', '113年', '114年', '變化']]
    rows_data.append(['事故停電總次數', str(n1), str(n2), f'+{n2-n1}件 (+{change_pct:.0f}%)'])
    rows_data.append(['受影響總戶數', f'{hh1:,}', f'{hh2:,}', f'+{hh2-hh1:,}戶 (+{hh_change:.0f}%)'])
    rows_data.append(['單次平均影響戶數', f'{hh1/n1:.0f}', f'{hh2/n2:.0f}', f'+{(hh2/n2-hh1/n1):.0f}戶'])
    rows_data.append(['涉及行政區數', str(len(d1)), str(len(d2)), f'+{len(d2)-len(d1)}區'])
    rows_data.append(['最大單次影響', f'{int(s1["households"].max()):,}戶', f'{int(s2["households"].max()):,}戶', ''])
    add_table_slide(slide, 0.5, 1.7, 9, rows_data, [2.5, 2.0, 2.0, 2.5])
    
    # Reason summary
    add_text_box(slide, 0.5, 4.5, 9, 0.4, '▎ 原因分類佔比變化', 14, ACCENT, True)
    rows_data2 = [['原因分類', '113年次數', '佔比', '114年次數', '佔比', '變化']]
    for rc in [1,2,3,4,5]:
        c1 = r1.get(rc, 0)
        c2 = r2.get(rc, 0)
        p1 = f'{c1/n1*100:.1f}%'
        p2 = f'{c2/n2*100:.1f}%'
        chg = f'+{c2-c1}' if c2 >= c1 else str(c2-c1)
        rows_data2.append([reason_labels_full.get(rc, str(rc)), str(c1), p1, str(c2), p2, chg])
    add_table_slide(slide, 0.5, 5.0, 9, rows_data2, [1.8, 1.3, 1.2, 1.3, 1.2, 1.2])
    
    add_education_box(slide, 0.5, 6.8, 9.0, 0.6, '🔍 觀察重點', [
        '114年「其他」類別暴增57件（從16→57），增幅256%，需深入了解是否為分類標準調整或新興原因'
    ], ACCENT4)
    
    # ═══════════════════════════════════════
    # PAGE 5: 高影響事件 TOP5
    # ═══════════════════════════════════════
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide)
    add_title_bar(slide, '一、營運健康度總覽', '高影響事件 TOP5 比較', '05')
    
    add_text_box(slide, 0.5, 1.2, 4.3, 0.4, '▎ 113年 TOP5 高影響事件', 14, ACCENT, True)
    rows1 = [['日期', '行政區', '影響戶數', '原因']]
    for _, r in top5_1.iterrows():
        rows1.append([r['time'], r['district'], f'{int(r["households"]):,}', reason_map.get(r['reason_cat'], '-')])
    add_table_slide(slide, 0.3, 1.7, 4.5, rows1, [1.2, 1.0, 1.1, 1.2])
    
    add_text_box(slide, 5.2, 1.2, 4.3, 0.4, '▎ 114年 TOP5 高影響事件', 14, ACCENT2, True)
    rows2 = [['日期', '行政區', '影響戶數', '原因']]
    for _, r in top5_2.iterrows():
        rows2.append([r['time'], r['district'], f'{int(r["households"]):,}', reason_map.get(r['reason_cat'], '-')])
    add_table_slide(slide, 5.2, 1.7, 4.5, rows2, [1.2, 1.0, 1.1, 1.2])
    
    # Comparison
    avg_top5_1 = int(top5_1['households'].mean())
    avg_top5_2 = int(top5_2['households'].mean())
    add_text_box(slide, 0.5, 4.5, 9, 0.4, '▎ TOP5 平均影響規模', 14, ACCENT, True)
    rows3 = [['年份', 'TOP5平均戶數', '最大單次戶數', '最小單次戶數']]
    rows3.append(['113年', f'{avg_top5_1:,}', f'{int(top5_1["households"].max()):,}', f'{int(top5_1["households"].min()):,}'])
    rows3.append(['114年', f'{avg_top5_2:,}', f'{int(top5_2["households"].max()):,}', f'{int(top5_2["households"].min()):,}'])
    add_table_slide(slide, 0.5, 5.0, 9, rows3, [2.0, 2.5, 2.5, 2.0])
    
    add_education_box(slide, 0.5, 6.5, 9.0, 0.9, '📝 學習要點', [
        '• 高影響事件通常涉及主幹線路或變電所跳脫，影響範圍廣',
        '• 114年TOP5平均戶數 slightly 高於113年，反映單次事件影響規模擴大趨勢',
        '• 關注高影響事件的共同特徵（時段、區域、原因），有助於制定預防策略'
    ], ACCENT3)
    
    # ═══════════════════════════════════════
    # PAGE 6: 月份趨勢
    # ═══════════════════════════════════════
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide)
    add_title_bar(slide, '一、營運健康度總覽', '月份停電次數趨勢分析', '06')
    
    # Month table
    add_text_box(slide, 0.5, 1.2, 9, 0.4, '▎ 月別停電次數對比', 14, ACCENT, True)
    rows_month = [['月份', '113年', '114年', '變化']]
    for m in range(1, 13):
        v1 = m1.get(m, 0)
        v2 = m2.get(m, 0)
        chg = f'+{v2-v1}' if v2 >= v1 else str(v2-v1)
        rows_month.append([f'{m}月', str(v1), str(v2), chg])
    add_table_slide(slide, 0.5, 1.7, 9, rows_month, [2.0, 2.5, 2.5, 2.0])
    
    # Insight
    peak_m1 = m1.idxmax()
    peak_m2 = m2.idxmax()
    add_education_box(slide, 0.5, 5.5, 9.0, 1.7, '🔍 觀察重點 & 📝 學習要點', [
        f'• 113年停電高峰：{peak_m1}月（{m1.max()}件），114年高峰：{peak_m2}月（{m2.max()}件）',
        f'• 114年7月出現異常高峰（{m2.get(7,0)}件），可能與夏季用電負載有關',
        '• 113年1-4月停電頻繁（共80件，佔65%），114年分布較平均',
        '• 月份趨勢分析有助於提前部署維修資源與應變人力',
        '• 建議結合氣象資料進一步分析氣候因素對停電的影響'
    ], ACCENT4)
    
    # ═══════════════════════════════════════
    # PAGE 7: 行政區熱點 - 次數 TOP10
    # ═══════════════════════════════════════
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide)
    add_title_bar(slide, '二、行政區熱點分析', '各區域停電次數 TOP10', '07')
    
    # Table
    add_text_box(slide, 0.5, 1.2, 9, 0.4, '▎ 各區域停電次數排名', 14, ACCENT, True)
    rows_dist = [['排名', '行政區', '113年', '114年', '變化']]
    top10_dists = dist_sorted[:10]
    for i, d in enumerate(top10_dists):
        v1 = d1.get(d, 0)
        v2 = d2.get(d, 0)
        chg = f'+{v2-v1}' if v2 >= v1 else str(v2-v1)
        rows_dist.append([str(i+1), d, str(v1), str(v2), chg])
    add_table_slide(slide, 0.5, 1.7, 9, rows_dist, [0.8, 1.8, 2.0, 2.0, 2.4])
    
    add_education_box(slide, 0.5, 5.5, 9.0, 1.7, '🔍 觀察重點 & 📝 學習要點', [
        f'• 114年停電最頻繁區域：{top10_dists[0]}（{d2.get(top10_dists[0],0)}件），113年為{dist_sorted[:3][0]}',
        f'• 大園區從113年12件增至114年25件（+108%），為增幅最大區域',
        '• 中壢區、平鎮區114年停電次數均顯著增加，反映都會區配電壓力',
        '• 區域熱點分析可作為維修資源配置與預防巡檢的優先依據'
    ], ACCENT4)
    
    # ═══════════════════════════════════════
    # PAGE 8: CHART - 各區域停電次數比較
    # ═══════════════════════════════════════
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide)
    add_title_bar(slide, '二、行政區熱點分析', '📊 圖表：各區域停電次數比較（113年 vs 114年）', '08')
    
    # Chart - grouped bar
    chart_data = CategoryChartData()
    chart_data.categories = top10_dists
    chart_data.add_series('113年', [d1.get(d, 0) for d in top10_dists])
    chart_data.add_series('114年', [d2.get(d, 0) for d in top10_dists])
    
    chart_frame = slide.shapes.add_chart(
        XL_CHART_TYPE.COLUMN_CLUSTERED, Inches(0.5), Inches(1.2), Inches(9), Inches(5.0),
        chart_data
    )
    chart = chart_frame.chart
    chart.has_legend = True
    chart.legend.position = XL_LEGEND_POSITION.BOTTOM
    chart.legend.include_in_layout = False
    
    # Style chart
    plot = chart.plots[0]
    plot.gap_width = 80
    
    # Color series
    series_colors = [ACCENT, ACCENT2]
    for i, series in enumerate(plot.series):
        series.format.fill.solid()
        series.format.fill.fore_color.rgb = series_colors[i]
        series.format.line.fill.background()
        # Data labels
        series.has_data_labels = True
        series.data_labels.font.size = Pt(8)
        series.data_labels.font.color.rgb = LIGHT_GRAY
        series.data_labels.number_format = '0'
    
    # Category axis
    cat_axis = chart.category_axis
    cat_axis.tick_labels.font.size = Pt(9)
    cat_axis.tick_labels.font.color.rgb = LIGHT_GRAY
    cat_axis.format.line.color.rgb = GRAY
    
    # Value axis
    val_axis = chart.value_axis
    val_axis.tick_labels.font.size = Pt(9)
    val_axis.tick_labels.font.color.rgb = LIGHT_GRAY
    val_axis.format.line.color.rgb = GRAY
    val_axis.major_gridlines.format.line.color.rgb = RGBColor(0x3A, 0x3A, 0x5A)
    
    # Chart background
    chart.chart_style = 2
    
    add_education_box(slide, 0.5, 6.5, 9.0, 0.8, '📝 學習要點', [
        '• 並列長條圖可直觀比較兩年度各區域停電次數變化，找出增幅最大區域',
        '• 大園區、觀音區、中壢區114年停電次數均大幅增加，需關注配電設備狀態'
    ], ACCENT3)
    
    # ═══════════════════════════════════════
    # PAGE 9: 行政區熱點 - 戶數 TOP10
    # ═══════════════════════════════════════
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide)
    add_title_bar(slide, '二、行政區熱點分析', '各區域受影響戶數 TOP10', '09')
    
    add_text_box(slide, 0.5, 1.2, 9, 0.4, '▎ 各區域受影響戶數排名', 14, ACCENT, True)
    
    # Sort by 114 household
    hd_sorted = sorted(all_dists, key=lambda d: hd2.get(d, 0), reverse=True)
    top10_hh = hd_sorted[:10]
    
    rows_hh = [['排名', '行政區', '113年戶數', '114年戶數', '變化']]
    for i, d in enumerate(top10_hh):
        v1 = int(hd1.get(d, 0))
        v2 = int(hd2.get(d, 0))
        chg = f'+{v2-v1:,}' if v2 >= v1 else f'{v2-v1:,}'
        rows_hh.append([str(i+1), d, f'{v1:,}', f'{v2:,}', chg])
    add_table_slide(slide, 0.5, 1.7, 9, rows_hh, [0.8, 1.8, 2.0, 2.0, 2.4])
    
    add_education_box(slide, 0.5, 5.5, 9.0, 1.7, '🔍 觀察重點 & 📝 學習要點', [
        f'• 中壢區114年受影響戶數最高（{int(hd2.get("中壢區",0)):,}戶），較113年大幅增加',
        '• 平鎮區從113年未入榜躍升至114年第2名，反映新增重大事故',
        '• 楊梅區受影響戶數反而下降（從32,326→21,490），可能是好事',
        '• 戶數排名與次數排名不完全一致，反映單次事件規模差異'
    ], ACCENT4)
    
    # ═══════════════════════════════════════
    # PAGE 10: CHART - 各區域受影響戶數比較
    # ═══════════════════════════════════════
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide)
    add_title_bar(slide, '二、行政區熱點分析', '📊 圖表：各區域受影響戶數比較（113年 vs 114年）', '10')
    
    chart_data = CategoryChartData()
    chart_data.categories = top10_hh
    chart_data.add_series('113年', [int(hd1.get(d, 0)) for d in top10_hh])
    chart_data.add_series('114年', [int(hd2.get(d, 0)) for d in top10_hh])
    
    chart_frame = slide.shapes.add_chart(
        XL_CHART_TYPE.COLUMN_CLUSTERED, Inches(0.5), Inches(1.2), Inches(9), Inches(5.0),
        chart_data
    )
    chart = chart_frame.chart
    chart.has_legend = True
    chart.legend.position = XL_LEGEND_POSITION.BOTTOM
    chart.legend.include_in_layout = False
    
    plot = chart.plots[0]
    plot.gap_width = 80
    
    for i, series in enumerate(plot.series):
        series.format.fill.solid()
        series.format.fill.fore_color.rgb = [ACCENT, ACCENT2][i]
        series.format.line.fill.background()
        series.has_data_labels = True
        series.data_labels.font.size = Pt(7)
        series.data_labels.font.color.rgb = LIGHT_GRAY
        series.data_labels.number_format = '#,##0'
    
    cat_axis = chart.category_axis
    cat_axis.tick_labels.font.size = Pt(9)
    cat_axis.tick_labels.font.color.rgb = LIGHT_GRAY
    val_axis = chart.value_axis
    val_axis.tick_labels.font.size = Pt(9)
    val_axis.tick_labels.font.color.rgb = LIGHT_GRAY
    val_axis.major_gridlines.format.line.color.rgb = RGBColor(0x3A, 0x3A, 0x5A)
    
    add_education_box(slide, 0.5, 6.5, 9.0, 0.8, '📝 學習要點', [
        '• 中壢區、平鎮區114年受影響戶數大幅增加，需優先關注都會區配電網路',
        '• 復興區113年14,410戶高居第5，但114年未入榜，可能是單一大型事件'
    ], ACCENT3)
    
    # ═══════════════════════════════════════
    # PAGE 11: 行政區年度變化分析
    # ═══════════════════════════════════════
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide)
    add_title_bar(slide, '二、行政區熱點分析', '年度變化分析：哪些區域改善了？哪些惡化了？', '11')
    
    add_text_box(slide, 0.5, 1.2, 9, 0.4, '▎ 各區域停電次數年度變化', 14, ACCENT, True)
    
    rows_change = [['行政區', '113年', '114年', '變化', '變化率']]
    for d in dist_sorted[:12]:
        v1 = d1.get(d, 0)
        v2 = d2.get(d, 0)
        diff = v2 - v1
        pct = f'{diff/v1*100:+.0f}%' if v1 > 0 else '新增'
        rows_change.append([d, str(v1), str(v2), f'{diff:+d}', pct])
    add_table_slide(slide, 0.5, 1.7, 9, rows_change, [1.8, 1.5, 1.5, 1.5, 1.7])
    
    add_education_box(slide, 0.5, 5.5, 9.0, 1.7, '🔍 觀察重點 & 📝 學習要點', [
        '• 惡化最明顯：大園區（+108%）、觀音區（+69%）、中壢區（+130%）',
        '• 改善區域：大溪區（0→持平）、龜山區（5→3，-40%）',
        '• 新增區域：平鎮區（10→21，+110%）從未入榜到大幅增加',
        '• 區域變化分析有助於精準配置維修資源與巡檢頻率'
    ], ACCENT4)
    
    # ═══════════════════════════════════════
    # PAGE 12: 停電原因分類
    # ═══════════════════════════════════════
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide)
    add_title_bar(slide, '三、發生成因分析', '停電原因分類統計', '12')
    
    add_text_box(slide, 0.5, 1.2, 9, 0.4, '▎ 原因分類佔比分析', 14, ACCENT, True)
    
    rows_reason = [['原因分類', '113年次數', '佔比', '114年次數', '佔比', '變化']]
    for rc in [1,2,3,4,5]:
        c1 = r1.get(rc, 0)
        c2 = r2.get(rc, 0)
        p1 = f'{c1/n1*100:.1f}%'
        p2 = f'{c2/n2*100:.1f}%'
        diff = c2 - c1
        rows_reason.append([reason_labels_full.get(rc, str(rc)), str(c1), p1, str(c2), p2, f'{diff:+d}'])
    rows_reason.append(['合計', str(n1), '100%', str(n2), '100%', f'+{n2-n1}'])
    add_table_slide(slide, 0.5, 1.7, 9, rows_reason, [1.8, 1.3, 1.2, 1.3, 1.2, 1.2])
    
    add_education_box(slide, 0.5, 4.5, 9.0, 2.7, '🔍 觀察重點 & 📝 學習要點', [
        '• 「設備不良」（3）從35件增至53件，增幅51%，為最大宗原因',
        '• 「其他」（5）從16件暴增至57件，增幅256%，需進一步了解細分原因',
        '• 「電纜不良」（2）從46件降至38件，可能是電纜改善計畫奏效',
        '• 「樹竹外物」（1）從25件增至41件，增幅64%，可能與氣候因素有關',
        '• 原因分類變化反映配電系統老化程度與外在環境影響的交互作用',
        '• 建議針對增幅最大的「設備不良」與「其他」類別進行根因分析'
    ], ACCENT4)
    
    # ═══════════════════════════════════════
    # PAGE 13: CHART - 原因分類比較
    # ═══════════════════════════════════════
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide)
    add_title_bar(slide, '三、發生成因分析', '📊 圖表：停電原因分類比較（113年 vs 114年）', '13')
    
    chart_data = CategoryChartData()
    reason_cats = [1, 2, 3, 4, 5]
    chart_data.categories = [reason_labels_full[rc] for rc in reason_cats]
    chart_data.add_series('113年', [r1.get(rc, 0) for rc in reason_cats])
    chart_data.add_series('114年', [r2.get(rc, 0) for rc in reason_cats])
    
    chart_frame = slide.shapes.add_chart(
        XL_CHART_TYPE.COLUMN_CLUSTERED, Inches(0.5), Inches(1.2), Inches(9), Inches(4.5),
        chart_data
    )
    chart = chart_frame.chart
    chart.has_legend = True
    chart.legend.position = XL_LEGEND_POSITION.BOTTOM
    chart.legend.include_in_layout = False
    
    plot = chart.plots[0]
    plot.gap_width = 100
    
    for i, series in enumerate(plot.series):
        series.format.fill.solid()
        series.format.fill.fore_color.rgb = [ACCENT, ACCENT2][i]
        series.format.line.fill.background()
        series.has_data_labels = True
        series.data_labels.font.size = Pt(10)
        series.data_labels.font.color.rgb = LIGHT_GRAY
        series.data_labels.number_format = '0'
    
    cat_axis = chart.category_axis
    cat_axis.tick_labels.font.size = Pt(10)
    cat_axis.tick_labels.font.color.rgb = LIGHT_GRAY
    val_axis = chart.value_axis
    val_axis.tick_labels.font.size = Pt(10)
    val_axis.tick_labels.font.color.rgb = LIGHT_GRAY
    val_axis.major_gridlines.format.line.color.rgb = RGBColor(0x3A, 0x3A, 0x5A)
    
    # Additional insight below chart
    add_text_box(slide, 0.5, 5.9, 9, 0.4, '▎ 原因別受影響戶數', 14, ACCENT, True)
    rows_hh_reason = [['原因', '113年戶數', '114年戶數', '變化']]
    for rc in [1,2,3,4,5]:
        h1 = int(hr1.get(rc, 0))
        h2 = int(hr2.get(rc, 0))
        rows_hh_reason.append([reason_labels_full.get(rc, str(rc)), f'{h1:,}', f'{h2:,}', f'{h2-h1:+,}'])
    add_table_slide(slide, 0.5, 6.3, 9, rows_hh_reason, [2.0, 2.5, 2.5, 2.0])
    
    # ═══════════════════════════════════════
    # PAGE 14: 各原因詳細分析
    # ═══════════════════════════════════════
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide)
    add_title_bar(slide, '三、發生成因分析', '各原因類別詳細分析', '14')
    
    add_text_box(slide, 0.5, 1.2, 9, 0.4, '▎ 各原因類別月別分布', 14, ACCENT, True)
    
    # Month x Reason pivot
    for reason_id in [1, 2, 3]:
        s1_r = s1[s1['reason_cat'] == reason_id]
        s2_r = s2[s2['reason_cat'] == reason_id]
        m_r1 = s1_r['month'].value_counts().sort_index()
        m_r2 = s2_r['month'].value_counts().sort_index()
        
        rows_pivot = [[f'原因{reason_id}：{reason_labels_full[reason_id]}', '', '', '']]
        rows_pivot.append(['月份', '113年', '114年', '變化'])
        for m in range(1, 13):
            v1 = m_r1.get(m, 0)
            v2 = m_r2.get(m, 0)
            rows_pivot.append([f'{m}月', str(v1), str(v2), f'{v2-v1:+d}'])
        
        y_pos = 1.7 + (reason_id - 1) * 1.8
        add_table_slide(slide, 0.5, y_pos, 9, rows_pivot, [2.0, 2.3, 2.3, 2.4])
    
    add_education_box(slide, 0.5, 6.8, 9.0, 0.6, '🔍 觀察重點', [
        '• 各原因的月別分布可發現季節性模式，如「樹竹外物」在風季（7-9月）增加'
    ], ACCENT4)
    
    # ═══════════════════════════════════════
    # PAGE 15: 應變效率診斷
    # ═══════════════════════════════════════
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide)
    add_title_bar(slide, '四、應變效率診斷', '復電時間與應變效率分析', '15')
    
    # Read time data for efficiency analysis
    s1_full = pd.read_excel(EXCEL_PATH, sheet_name=0, header=0, usecols=[2,4,5,7,8,12])
    s2_full = pd.read_excel(EXCEL_PATH, sheet_name=1, header=0, usecols=[2,4,5,7,8,12])
    s1_full.columns = ['time','district','households','first_report','last_report','reason']
    s2_full.columns = ['time','district','households','first_report','last_report','reason']
    s1_full = s1_full[s1_full['time'].astype(str).str.match(r'^113/\d{2}/\d{2}')]
    s2_full = s2_full[s2_full['time'].astype(str).str.match(r'^114/\d{2}/\d{2}')]
    
    def calc_duration_mins(row):
        try:
            fr = row['first_report']
            lr = row['last_report']
            if fr is None or lr is None:
                return None
            if isinstance(fr, str) or isinstance(lr, str):
                return None
            fr_m = fr.hour * 60 + fr.minute
            lr_m = lr.hour * 60 + lr.minute
            diff = lr_m - fr_m
            if diff < 0:
                diff += 24 * 60
            return diff
        except:
            return None
    
    s1_full['duration'] = s1_full.apply(calc_duration_mins, axis=1)
    s2_full['duration'] = s2_full.apply(calc_duration_mins, axis=1)
    
    d1_dur = s1_full['duration'].dropna()
    d2_dur = s2_full['duration'].dropna()
    
    add_text_box(slide, 0.5, 1.2, 9, 0.4, '▎ 復電時間統計', 14, ACCENT, True)
    
    rows_eff = [['指標', '113年', '114年']]
    rows_eff.append(['有效樣本數', str(len(d1_dur)), str(len(d2_dur))])
    rows_eff.append(['平均復電時間', f'{d1_dur.mean():.0f}分鐘', f'{d2_dur.mean():.0f}分鐘'])
    rows_eff.append(['中位數復電時間', f'{d1_dur.median():.0f}分鐘', f'{d2_dur.median():.0f}分鐘'])
    rows_eff.append(['最短復電時間', f'{d1_dur.min():.0f}分鐘', f'{d2_dur.min():.0f}分鐘'])
    rows_eff.append(['最長復電時間', f'{d1_dur.max():.0f}分鐘', f'{d2_dur.max():.0f}分鐘'])
    rows_eff.append(['復電≤60分鐘佔比', f'{(d1_dur<=60).sum()/len(d1_dur)*100:.1f}%', f'{(d2_dur<=60).sum()/len(d2_dur)*100:.1f}%'])
    rows_eff.append(['復電≤120分鐘佔比', f'{(d1_dur<=120).sum()/len(d1_dur)*100:.1f}%', f'{(d2_dur<=120).sum()/len(d2_dur)*100:.1f}%'])
    add_table_slide(slide, 0.5, 1.7, 9, rows_eff, [3.0, 3.0, 3.0])
    
    add_education_box(slide, 0.5, 5.0, 9.0, 2.2, '🔍 觀察重點 & 📝 學習要點', [
        f'• 113年平均復電時間 {d1_dur.mean():.0f}分鐘，114年 {d2_dur.mean():.0f}分鐘',
        f'• 復電≤60分鐘佔比：113年 {(d1_dur<=60).sum()/len(d1_dur)*100:.1f}%，114年 {(d2_dur<=60).sum()/len(d2_dur)*100:.1f}%',
        '• 應變效率是衡量電力公司服務品質的關鍵指標',
        '• 可進一步分析「第一報→最後一報」的時間差，了解搶修流程瓶頸',
        '• 建議建立復電時間KPI，設定60分鐘/120分鐘的目標達成率'
    ], ACCENT3)
    
    # ═══════════════════════════════════════
    # PAGE 16: 應變效率 - 區域比較
    # ═══════════════════════════════════════
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide)
    add_title_bar(slide, '四、應變效率診斷', '各區域應變效率比較', '16')
    
    add_text_box(slide, 0.5, 1.2, 9, 0.4, '▎ 114年各區域平均復電時間', 14, ACCENT, True)
    
    # Calculate per-district duration for 114
    dist_dur = s2_full.groupby('district')['duration'].agg(['mean', 'median', 'count']).dropna()
    dist_dur = dist_dur[dist_dur['count'] >= 2].sort_values('mean', ascending=False)
    
    rows_dist_eff = [['行政區', '平均復電(分)', '中位數(分)', '案件數']]
    for d, r in dist_dur.iterrows():
        rows_dist_eff.append([d, f'{r["mean"]:.0f}', f'{r["median"]:.0f}', str(int(r["count"]))])
    add_table_slide(slide, 0.5, 1.7, 9, rows_dist_eff, [2.5, 2.5, 2.0, 2.0])
    
    add_education_box(slide, 0.5, 5.5, 9.0, 1.7, '📝 學習要點', [
        '• 偏遠區域（復興區、大溪區）復電時間通常較長，反映地理因素影響',
        '• 都會區（中壢區、桃園區）復電效率較高，但事故頻率也高',
        '• 應變效率分析可作為人力配置與搶修站點規劃的依據',
        '• 建議建立「復電時間×案件數」雙維度矩陣，找出效率瓶頸區域'
    ], ACCENT3)
    
    # ═══════════════════════════════════════
    # PAGE 17: 未來精進建議
    # ═══════════════════════════════════════
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide)
    add_title_bar(slide, '五、未來精進建議', '數據驅動的改善方向', '17')
    
    suggestions = [
        ('設備更新計畫', f'「設備不良」原因從35件增至53件（+51%），建議針對高事故區域（中壢、平鎮、觀音）優先進行設備汰換', ACCENT),
        ('電纜改善延續', '「電纜不良」從46件降至38件，改善計畫奏效，建議持續投入並擴大範圍', ACCENT3),
        ('樹竹巡檢強化', '「樹竹外物」從25件增至41件（+64%），建議加強風季前後的樹竹清理巡檢', ACCENT4),
        ('「其他」原因釐清', '「其他」從16件暴增至57件（+256%），建議細化分類標準，釐清真實原因', ACCENT2),
        ('應變效率提升', f'建立復電時間KPI（目標：60分鐘內復電率≥50%），持續追蹤改善', RGBColor(0xCC, 0x5D, 0xE8)),
    ]
    
    for i, (title, desc, color) in enumerate(suggestions):
        y = 1.3 + i * 1.05
        shape = slide.shapes.add_shape(1, Inches(0.5), Inches(y), Inches(9), Inches(0.95))
        shape.fill.solid()
        shape.fill.fore_color.rgb = RGBColor(0x24, 0x24, 0x3E)
        shape.line.color.rgb = color
        shape.line.width = Pt(2)
        
        add_text_box(slide, 0.8, y + 0.05, 8, 0.35, f'建議{i+1}：{title}', 13, color, True)
        add_text_box(slide, 0.8, y + 0.4, 8.5, 0.5, desc, 11, LIGHT_GRAY)
    
    add_education_box(slide, 0.5, 6.7, 9.0, 0.7, '📝 學習要點', [
        '• 精進建議應基於數據事實，設定可量化的改善目標與追蹤機制',
        '• 區分「短期應急」與「長期結構性改善」，制定分階段執行計畫'
    ], ACCENT3)
    
    # ═══════════════════════════════════════
    # PAGE 18: 總結
    # ═══════════════════════════════════════
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide)
    add_title_bar(slide, '報告總結', '核心發現與行動方向', '18')
    
    add_text_box(slide, 0.5, 1.2, 9, 0.4, '▎ 核心發現', 16, ACCENT, True)
    
    core_findings = [
        f'停電次數年增 {change_pct:.0f}%（{n1}→{n2}件），配電系統壓力持續上升',
        f'受影響戶數年增 {hh_change:.0f}%（{hh1:,}→{hh2:,}戶），單次影響範圍擴大',
        '設備不良（+51%）與其他原因（+256%）為主要增幅來源',
        '大園區、中壢區、觀音區為114年停電熱點區域',
        '114年7月為單月高峰（33件），夏季用電負載影響顯著',
    ]
    lines = [(f'• {f}', LIGHT_GRAY, False) for f in core_findings]
    add_multiline_text(slide, 0.7, 1.7, 8.5, 2.5, lines, 13, LIGHT_GRAY)
    
    add_text_box(slide, 0.5, 4.2, 9, 0.4, '▎ 行動方向', 16, ACCENT, True)
    
    actions = [
        '短期：強化設備不良區域的巡檢頻率，釐清「其他」原因細分',
        '中期：擴大電纜改善計畫範圍，建立復電時間KPI追蹤機制',
        '長期：推動配電系統智慧化，導入預測性維護技術',
    ]
    lines2 = [(f'• {a}', LIGHT_GRAY, False) for a in actions]
    add_multiline_text(slide, 0.7, 4.7, 8.5, 1.5, lines2, 13, LIGHT_GRAY)
    
    add_education_box(slide, 0.5, 6.0, 9.0, 1.2, '📝 學習反思', [
        '• 數據分析的價值不在於「發現問題」，而在於「驅動行動」',
        '• 建議定期（每季）產出分析報告，追蹤改善措施的實際效果',
        '• 結合IoT感測與AI預測，從「被動搶修」轉向「主動預防」'
    ], ACCENT3)
    
    # ═══════════════════════════════════════
    # PAGE 19: 附錄 - 資料說明
    # ═══════════════════════════════════════
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide)
    add_title_bar(slide, '附錄：資料說明與分析方法', '', '19')
    
    add_text_box(slide, 0.5, 1.2, 9, 0.4, '▎ 資料來源', 14, ACCENT, True)
    info_lines = [
        ('• 資料來源：台電事故停電紀錄表（113年～114年）', LIGHT_GRAY, False),
        ('• 分析工具：Python pandas + python-pptx', LIGHT_GRAY, False),
        ('• 樣本範圍：桃園區配電系統事故停電紀錄', LIGHT_GRAY, False),
        ('• 排除條件：颱風、雙地區停電、自用設備不良、新北市案件', LIGHT_GRAY, False),
        (f'• 有效樣本：113年 {n1}件、114年 {n2}件，共{n1+n2}件', LIGHT_GRAY, False),
    ]
    add_multiline_text(slide, 0.7, 1.7, 8.5, 2.5, info_lines, 12, LIGHT_GRAY)
    
    add_text_box(slide, 0.5, 3.8, 9, 0.4, '▎ 停電原因分類定義', 14, ACCENT, True)
    reason_defs = [
        ('1、樹竹及外物觸碰：樹壓電線、生物碰撞、非生物異物', LIGHT_GRAY, False),
        ('2、電纜不良：電纜線故障、接頭不良', LIGHT_GRAY, False),
        ('3、設備不良：變壓器、開關、礙子等設備故障', LIGHT_GRAY, False),
        ('4、用戶設備不良：用戶端設備問題', LIGHT_GRAY, False),
        ('5、其他：車禍、施工碰觸、天災、緊急搶修、不明原因', LIGHT_GRAY, False),
    ]
    add_multiline_text(slide, 0.7, 4.3, 8.5, 2.5, reason_defs, 12, LIGHT_GRAY)
    
    add_text_box(slide, 0.5, 6.0, 9, 0.4, '▎ 分析限制', 14, ACCENT, True)
    limits = [
        ('• 僅涵蓋桃園區配電系統，未含全國數據', LIGHT_GRAY, False),
        ('• 復電時間分析基於「第一報→最後一報」，可能不含完整復電流程', LIGHT_GRAY, False),
        ('• 部分欄位資料不完整（如實際復電時間），影響分析深度', LIGHT_GRAY, False),
    ]
    add_multiline_text(slide, 0.7, 6.4, 8.5, 1.0, limits, 12, LIGHT_GRAY)
    
    # Save
    prs.save(OUTPUT_PATH)
    print(f'✅ Report saved to {OUTPUT_PATH}')
    print(f'   Total slides: {len(prs.slides)}')

# ─── Main ───
if __name__ == '__main__':
    s1, s2 = load_data()
    build_presentation(s1, s2)
