import pandas as pd
import numpy as np
from typing import Optional


def generate_report(rr_result_df, kcd_result_df=None, followup_years=None,
                   n_A=None, n_B=None, rr_threshold_high=1.20, rr_threshold_low=0.90,
                   stat_test_df=None, alpha=0.05, kcd_name_df=None,
                   language='ko', output_format='text'):
    """
    RR_calculator와 kcd_counter 결과를 바탕으로 위험도 리포트를 생성하는 함수.
    
    report.txt의 요구사항을 바탕으로 다음 내용을 포함:
    1. 위험도 세부 요약: 모든 7개 지표의 절대·상대 위험도 설명
    2. 전반적 위험도 비교: 전체 지표 중 유의한 차이 개수 요약
    3. 유의한 차이 요약: 유의하게 높거나 낮은 지표만 별도 강조 (통계적 검정 결과 포함)
    4. 실질적 영향 평가: 1000명당 발생 건수 등 실질적 영향
    5. KCD 추적결과 요약: (입력 있을 때만)
    6. 결론 및 시사점: 전반적 해석과 정책적 함의
    
    Parameters:
    -----------
    rr_result_df : pd.DataFrame
        RR_calculator 함수의 반환값
        컬럼: '지표', 'A발생수', 'A모수', 'A위험도', 'B발생수', 'B모수', 'B위험도', 
              '차이', '상대위험도', '변화율'
    kcd_result_df : pd.DataFrame, optional
        kcd_counter 함수의 반환값
        컬럼: 'kcd', 'ID수', 'cancer', 'brain', 'heart', 'disease_sur', 'injury_sur', 
              'disease_hos', 'injury_hos'
    followup_years : int, optional
        KCD 추적 기간 (년). kcd_result_df가 있을 때 사용
    n_A : int, optional
        그룹 A의 ID 개수. 표본수 경고에 사용.
        None이면 rr_result_df의 'A모수' 컬럼에서 자동 추출
    n_B : int, optional
        그룹 B의 ID 개수. 표본수 경고에 사용.
        None이면 rr_result_df의 'B모수' 컬럼에서 자동 추출
    rr_threshold_high : float, optional
        유의하게 높다고 판정하는 상대위험도 기준. 기본값: 1.20
    rr_threshold_low : float, optional
        유의하게 낮다고 판정하는 상대위험도 기준. 기본값: 0.90
    stat_test_df : pd.DataFrame, optional
        test_rr_significance 함수의 반환값
        컬럼: '지표', 'p_value', '유의함', '신뢰구간_하한', '신뢰구간_상한' 등
    alpha : float, optional
        통계적 유의수준. 기본값: 0.05
    kcd_name_df : pd.DataFrame, optional
        KCD 코드와 한글 이름 매핑 데이터프레임
        컬럼: 'kcd' (또는 'kcd_pattern'), 'kcd_name' (한글 이름)
        예: pd.DataFrame({'kcd': ['^A00', '^C[0-9]'], 'kcd_name': ['콜레라', '악성신생물']})
    language : str, optional
        언어 옵션. 'ko' 또는 'en'. 기본값: 'ko'
    output_format : str, optional
        출력 형식. 'text' 또는 'html'. 기본값: 'text'
    
    Returns:
    --------
    str
        생성된 리포트 문자열
    """
    
    # n_A, n_B 자동 추출 (rr_result_df에 'A모수', 'B모수' 컬럼이 있는 경우)
    if n_A is None and 'A모수' in rr_result_df.columns:
        n_A = int(rr_result_df['A모수'].iloc[0])
    if n_B is None and 'B모수' in rr_result_df.columns:
        n_B = int(rr_result_df['B모수'].iloc[0])
    
    # 지표명 한글 매핑
    indicator_names_ko = {
        'cancer': '암',
        'brain': '뇌혈관질환',
        'heart': '심장질환',
        'disease_sur': '질병수술',
        'injury_sur': '외상수술',
        'disease_hos': '질병입원',
        'injury_hos': '외상입원'
    }
    
    # 지표별 단위 및 표현 방식
    indicator_unit = {
        'cancer': ('위험률', '%'),
        'brain': ('위험률', '%'),
        'heart': ('위험률', '%'),
        'disease_sur': ('청구 횟수', '건'),
        'injury_sur': ('청구 횟수', '건'),
        'disease_hos': ('입원일수', '일'),
        'injury_hos': ('입원일수', '일')
    }
    
    # 지표 표시 순서 (고정)
    indicator_order = ['cancer', 'brain', 'heart', 'disease_sur', 'injury_sur', 
                       'disease_hos', 'injury_hos']
    
    # 지표별 중요도 가중치 (Phase 3)
    indicator_importance = {
        'cancer': 5,  # 매우 중요
        'brain': 5,
        'heart': 5,
        'disease_hos': 3,  # 중요
        'injury_hos': 3,
        'disease_sur': 2,  # 보통
        'injury_sur': 2
    }
    
    # 차이 크기 분류 기준 (Phase 2)
    DIFF_SIZE_THRESHOLDS = {
        'large': {'rr_high': 1.30, 'rr_low': 0.77, 'abs_diff_pct': 0.05},  # 30% 이상 차이
        'moderate': {'rr_high': 1.15, 'rr_low': 0.87, 'abs_diff_pct': 0.02},  # 15~30% 차이
        'small': {'rr_high': 1.05, 'rr_low': 0.95, 'abs_diff_pct': 0.01}  # 5~15% 차이
    }
    
    report_lines = []
    
    # ========================================================================
    # 1. 위험도 세부 요약
    # ========================================================================
    report_lines.append("[위험도 세부 요약]")
    
    for indicator in indicator_order:
        # 해당 지표의 행 찾기
        indicator_row = rr_result_df[rr_result_df['지표'] == indicator]
        
        if len(indicator_row) == 0:
            continue
        
        row = indicator_row.iloc[0]
        risk_A = row['A위험도']
        risk_B = row['B위험도']
        rr = row['상대위험도']
        
        # NaN, Inf 처리
        if pd.isna(risk_A) or pd.isna(risk_B) or pd.isna(rr):
            indicator_name = indicator_names_ko.get(indicator, indicator)
            unit_name, _ = indicator_unit.get(indicator, ('위험률', '%'))
            report_lines.append(f"{indicator_name} {unit_name}: 자료 없음")
            continue
        
        # Inf 문자열 처리
        if isinstance(rr, str) and rr == 'Inf':
            indicator_name = indicator_names_ko.get(indicator, indicator)
            unit_name, unit_symbol = indicator_unit.get(indicator, ('위험률', '%'))
            
            if unit_symbol == '%':
                risk_A_display = risk_A * 100
                risk_B_display = risk_B * 100
                diff_display = (risk_B - risk_A) * 100
                diff_unit = '%p'
            else:
                risk_A_display = risk_A
                risk_B_display = risk_B
                diff_display = risk_B - risk_A
                diff_unit = unit_symbol
            
            report_lines.append(
                f"{indicator_name} {unit_name}: A그룹 {risk_A_display:.1f}{unit_symbol} → "
                f"B그룹 {risk_B_display:.1f}{unit_symbol} "
                f"(상대위험도 무한대, 차이 {diff_display:+.1f}{diff_unit})"
            )
            continue
        
        # 정상 처리
        rr_value = float(rr)
        indicator_name = indicator_names_ko.get(indicator, indicator)
        unit_name, unit_symbol = indicator_unit.get(indicator, ('위험률', '%'))
        
        # 단위에 따라 표시 방식 다르게
        if unit_symbol == '%':
            # 위험률: 퍼센트로 표시
            risk_A_display = risk_A * 100
            risk_B_display = risk_B * 100
            diff_display = (risk_B - risk_A) * 100
            diff_unit = '%p'
        else:
            # 청구 횟수나 입원일수: 그대로 표시
            risk_A_display = risk_A
            risk_B_display = risk_B
            diff_display = risk_B - risk_A
            diff_unit = unit_symbol
        
        # 문장 생성
        report_lines.append(
            f"{indicator_name} {unit_name}: A그룹 {risk_A_display:.1f}{unit_symbol} → "
            f"B그룹 {risk_B_display:.1f}{unit_symbol} "
            f"(상대위험도 {rr_value:.2f}배, 차이 {diff_display:+.1f}{diff_unit})"
        )
    
    report_lines.append("")  # 빈 줄
    
    # ========================================================================
    # 1.5 전반적 위험도 비교 (Phase 1 추가)
    # ========================================================================
    report_lines.append("[전반적 위험도 비교]")
    
    # 먼저 유의한 차이를 카운트하기 위해 임시로 계산
    temp_significant_high = []
    temp_significant_low = []
    
    for indicator in indicator_order:
        indicator_row = rr_result_df[rr_result_df['지표'] == indicator]
        if len(indicator_row) == 0:
            continue
        
        row = indicator_row.iloc[0]
        risk_A = row['A위험도']
        risk_B = row['B위험도']
        rr = row['상대위험도']
        
        if pd.isna(risk_A) or pd.isna(risk_B) or pd.isna(rr):
            continue
        
        if isinstance(rr, str) and rr == 'Inf':
            temp_significant_high.append(indicator)
            continue
        
        rr_value = float(rr)
        if rr_value >= rr_threshold_high:
            temp_significant_high.append(indicator)
        elif rr_value <= rr_threshold_low and rr_value > 0:
            temp_significant_low.append(indicator)
    
    n_higher = len(temp_significant_high)
    n_lower = len(temp_significant_low)
    n_similar = 7 - n_higher - n_lower
    
    report_lines.append(
        f"분석 결과: 7개 지표 중 {n_higher}개↑ (B그룹 높음), "
        f"{n_lower}개↓ (B그룹 낮음), {n_similar}개→ (유사)"
    )
    
    # 전체적 경향 판정
    if n_higher > n_lower + 1:
        risk_direction = "B그룹의 전반적 위험도가 A그룹보다 높습니다"
        risk_level = "주의"
    elif n_lower > n_higher + 1:
        risk_direction = "B그룹의 전반적 위험도가 A그룹보다 낮습니다"
        risk_level = "양호"
    else:
        risk_direction = "두 그룹의 위험도는 전반적으로 유사합니다"
        risk_level = "보통"
    
    report_lines.append(f"종합 판정: {risk_direction} ({risk_level})")
    report_lines.append("")  # 빈 줄
    
    # ========================================================================
    # 2. 유의한 차이 요약 (통계적 검정 결과 통합)
    # ========================================================================
    report_lines.append("[유의한 차이 요약]")
    
    significant_high = []  # 유의하게 높은 항목
    significant_low = []   # 유의하게 낮은 항목
    has_stat_info = False  # 통계적 검정 정보가 있는지 확인
    
    for indicator in indicator_order:
        indicator_row = rr_result_df[rr_result_df['지표'] == indicator]
        
        if len(indicator_row) == 0:
            continue
        
        row = indicator_row.iloc[0]
        risk_A = row['A위험도']
        risk_B = row['B위험도']
        rr = row['상대위험도']
        
        # NaN, Inf 처리
        if pd.isna(risk_A) or pd.isna(risk_B) or pd.isna(rr):
            continue
        
        if isinstance(rr, str) and rr == 'Inf':
            # 무한대는 유의하게 높은 것으로 처리
            indicator_name = indicator_names_ko.get(indicator, indicator)
            unit_symbol = indicator_unit.get(indicator, ('위험률', '%'))[1]
            if unit_symbol == '%':
                diff_display = (risk_B - risk_A) * 100
                diff_unit = '%p'
            else:
                diff_display = risk_B - risk_A
                diff_unit = unit_symbol
            significant_high.append(f"{indicator_name}(무한대, {diff_display:+.1f}{diff_unit})")
            continue
        
        rr_value = float(rr)
        indicator_name = indicator_names_ko.get(indicator, indicator)
        unit_symbol = indicator_unit.get(indicator, ('위험률', '%'))[1]
        
        if unit_symbol == '%':
            diff_display = (risk_B - risk_A) * 100
            diff_unit = '%p'
        else:
            diff_display = risk_B - risk_A
            diff_unit = unit_symbol
        
        # 통계적 유의성 확인
        p_value = None
        is_stat_sig = False
        if stat_test_df is not None:
            stat_row = stat_test_df[stat_test_df['지표'] == indicator]
            if len(stat_row) > 0:
                p_value = stat_row.iloc[0].get('p_value', None)
                if p_value is not None and not pd.isna(p_value):
                    is_stat_sig = p_value < alpha
                    has_stat_info = True
        
        # 유의 판정 (통계적 + 실질적)
        is_rr_sig_high = (rr_value >= rr_threshold_high)
        is_rr_sig_low = (rr_value <= rr_threshold_low and rr_value > 0)
        
        # 판정 마커 결정
        if is_stat_sig and (is_rr_sig_high or is_rr_sig_low):
            sig_marker = "**"  # 강한 유의성 (통계적 + 실질적)
        elif is_stat_sig or (is_rr_sig_high or is_rr_sig_low):
            sig_marker = "*"   # 경계선 유의성
        else:
            sig_marker = ""
        
        # 문자열 생성
        if p_value is not None and not pd.isna(p_value):
            item_str = f"{indicator_name}({rr_value:.2f}배, {diff_display:+.1f}{diff_unit}, p={p_value:.3f}){sig_marker}"
        else:
            item_str = f"{indicator_name}({rr_value:.2f}배, {diff_display:+.1f}{diff_unit}){sig_marker}"
        
        # 유의 판정
        if is_rr_sig_high or (is_stat_sig and rr_value > 1.0):
            significant_high.append(item_str)
        elif is_rr_sig_low or (is_stat_sig and rr_value < 1.0):
            significant_low.append(item_str)
    
    # 유의한 차이 요약 문장 생성
    if significant_high:
        report_lines.append(f"유의하게 높은 항목: {', '.join(significant_high)}")
    
    if significant_low:
        report_lines.append(f"유의하게 낮은 항목: {', '.join(significant_low)}")
    
    if not significant_high and not significant_low:
        report_lines.append("유의한 차이가 관찰되지 않았습니다.")
    
    # 통계적 검정 정보가 있으면 설명 추가
    if has_stat_info:
        report_lines.append("")
        report_lines.append("** p<0.05 (통계적으로 유의), * p<0.10 (경향성) 또는 실질적 유의")
    
    report_lines.append("")  # 빈 줄
    
    # ========================================================================
    # 2.5 실질적 영향 평가 (Phase 1 추가)
    # ========================================================================
    report_lines.append("[실질적 영향 평가]")
    
    # 1000명 기준으로 계산
    base_population = 1000
    
    for indicator in indicator_order:
        indicator_row = rr_result_df[rr_result_df['지표'] == indicator]
        if len(indicator_row) == 0:
            continue
        
        row = indicator_row.iloc[0]
        risk_A = row['A위험도']
        risk_B = row['B위험도']
        rr = row['상대위험도']
        
        if pd.isna(risk_A) or pd.isna(risk_B) or pd.isna(rr):
            continue
        
        if isinstance(rr, str) and rr == 'Inf':
            continue
        
        indicator_name = indicator_names_ko.get(indicator, indicator)
        unit_name, unit_symbol = indicator_unit.get(indicator, ('위험률', '%'))
        
        # 발생 건수/일수 계산
        if unit_symbol == '%':
            # 위험률: 1000명당 발생 인원수
            count_A = risk_A * base_population
            count_B = risk_B * base_population
            diff_count = (risk_B - risk_A) * base_population
            
            if abs(diff_count) >= 1:  # 1명 이상 차이만 표시
                report_lines.append(
                    f"{indicator_name}: 1000명당 A그룹 {count_A:.1f}명 → B그룹 {count_B:.1f}명 "
                    f"(차이: {diff_count:+.1f}명)"
                )
        elif unit_symbol == '건':
            # 청구 횟수: 1000명당 발생 건수
            count_A = risk_A * base_population
            count_B = risk_B * base_population
            diff_count = (risk_B - risk_A) * base_population
            
            if abs(diff_count) >= 1:  # 1건 이상 차이만 표시
                report_lines.append(
                    f"{indicator_name}: 1000명당 A그룹 {count_A:.1f}건 → B그룹 {count_B:.1f}건 "
                    f"(차이: {diff_count:+.1f}건)"
                )
        else:  # 입원일수
            # 입원일수: 1000명당 총 입원일수
            days_A = risk_A * base_population
            days_B = risk_B * base_population
            diff_days = (risk_B - risk_A) * base_population
            
            if abs(diff_days) >= 1:  # 1일 이상 차이만 표시
                report_lines.append(
                    f"{indicator_name}: 1000명당 A그룹 {days_A:.1f}일 → B그룹 {days_B:.1f}일 "
                    f"(차이: {diff_days:+.1f}일, 1인당 평균 {diff_days/base_population:.3f}일)"
                )
    
    report_lines.append("")  # 빈 줄
    
    # ========================================================================
    # 2.6 차이 크기별 분류 (Phase 2)
    # ========================================================================
    report_lines.append("[차이 크기별 분류]")
    
    diff_by_size = {
        'large': [],
        'moderate': [],
        'small': [],
        'negligible': []
    }
    
    for indicator in indicator_order:
        indicator_row = rr_result_df[rr_result_df['지표'] == indicator]
        if len(indicator_row) == 0:
            continue
        
        row = indicator_row.iloc[0]
        risk_A = row['A위험도']
        risk_B = row['B위험도']
        rr = row['상대위험도']
        
        if pd.isna(risk_A) or pd.isna(risk_B) or pd.isna(rr):
            continue
        
        if isinstance(rr, str) and rr == 'Inf':
            diff_by_size['large'].append(indicator)
            continue
        
        rr_value = float(rr)
        indicator_name = indicator_names_ko.get(indicator, indicator)
        
        # 절대 차이 계산
        abs_diff = abs(risk_B - risk_A)
        unit_symbol = indicator_unit.get(indicator, ('위험률', '%'))[1]
        
        if unit_symbol == '%':
            abs_diff_pct = abs_diff
        else:
            # 건수나 일수의 경우 상대적 차이로 계산
            if risk_A > 0:
                abs_diff_pct = abs_diff / risk_A
            else:
                abs_diff_pct = abs_diff
        
        # 차이 크기 분류
        if rr_value >= DIFF_SIZE_THRESHOLDS['large']['rr_high'] or \
           rr_value <= DIFF_SIZE_THRESHOLDS['large']['rr_low'] or \
           abs_diff_pct >= DIFF_SIZE_THRESHOLDS['large']['abs_diff_pct']:
            diff_by_size['large'].append(indicator_name)
        elif rr_value >= DIFF_SIZE_THRESHOLDS['moderate']['rr_high'] or \
             rr_value <= DIFF_SIZE_THRESHOLDS['moderate']['rr_low'] or \
             abs_diff_pct >= DIFF_SIZE_THRESHOLDS['moderate']['abs_diff_pct']:
            diff_by_size['moderate'].append(indicator_name)
        elif rr_value >= DIFF_SIZE_THRESHOLDS['small']['rr_high'] or \
             rr_value <= DIFF_SIZE_THRESHOLDS['small']['rr_low'] or \
             abs_diff_pct >= DIFF_SIZE_THRESHOLDS['small']['abs_diff_pct']:
            diff_by_size['small'].append(indicator_name)
        else:
            diff_by_size['negligible'].append(indicator_name)
    
    if diff_by_size['large']:
        report_lines.append(f"큰 차이 (30% 이상): {', '.join(diff_by_size['large'])}")
    if diff_by_size['moderate']:
        report_lines.append(f"중간 차이 (15~30%): {', '.join(diff_by_size['moderate'])}")
    if diff_by_size['small']:
        report_lines.append(f"작은 차이 (5~15%): {', '.join(diff_by_size['small'])}")
    if diff_by_size['negligible']:
        report_lines.append(f"무시 가능 (<5%): {', '.join(diff_by_size['negligible'])}")
    
    report_lines.append("")  # 빈 줄
    
    # ========================================================================
    # 2.7 신뢰구간 표시 (Phase 2)
    # ========================================================================
    if stat_test_df is not None and '신뢰구간_하한' in stat_test_df.columns:
        report_lines.append("[신뢰구간 분석]")
        
        for indicator in indicator_order:
            indicator_row = rr_result_df[rr_result_df['지표'] == indicator]
            if len(indicator_row) == 0:
                continue
            
            row = indicator_row.iloc[0]
            rr = row['상대위험도']
            
            if pd.isna(rr) or isinstance(rr, str):
                continue
            
            stat_row = stat_test_df[stat_test_df['지표'] == indicator]
            if len(stat_row) == 0:
                continue
            
            ci_lower = stat_row.iloc[0].get('신뢰구간_하한', None)
            ci_upper = stat_row.iloc[0].get('신뢰구간_상한', None)
            
            if ci_lower is None or ci_upper is None or (pd.isna(ci_lower) or pd.isna(ci_upper)):
                continue
            
            indicator_name = indicator_names_ko.get(indicator, indicator)
            rr_value = float(rr)
            
            # 신뢰구간이 0을 포함하는지 확인
            if ci_lower > 0:
                ci_interpretation = "B그룹이 유의하게 높음"
            elif ci_upper < 0:
                ci_interpretation = "B그룹이 유의하게 낮음"
            else:
                ci_interpretation = "차이 없음 (신뢰구간이 0 포함)"
            
            report_lines.append(
                f"{indicator_name}: 상대위험도 {rr_value:.2f} "
                f"(95% CI: {ci_lower:.3f}~{ci_upper:.3f}) - {ci_interpretation}"
            )
        
        report_lines.append("")  # 빈 줄
    
    # ========================================================================
    # 2.8 위험도 차이 시각화 (텍스트 버전) (Phase 3)
    # ========================================================================
    report_lines.append("[위험도 차이 시각화]")
    
    # 변화율 기준으로 정렬
    change_rates = []
    for indicator in indicator_order:
        indicator_row = rr_result_df[rr_result_df['지표'] == indicator]
        if len(indicator_row) == 0:
            continue
        
        row = indicator_row.iloc[0]
        change_rate = row.get('변화율', 0)
        
        if pd.isna(change_rate) or isinstance(change_rate, str):
            continue
        
        indicator_name = indicator_names_ko.get(indicator, indicator)
        change_rates.append((indicator_name, float(change_rate)))
    
    # 변화율 기준 내림차순 정렬
    change_rates.sort(key=lambda x: x[1], reverse=True)
    
    for indicator_name, change_pct in change_rates:
        # 막대 그래프 텍스트 버전 (최대 20칸)
        if change_pct > 0:
            bar_length = min(int(abs(change_pct) / 2), 20)  # 2%당 한 칸
            bar = '█' * bar_length
            report_lines.append(f"{indicator_name:12s} +{change_pct:6.1f}% {bar}")
        elif change_pct < 0:
            bar_length = min(int(abs(change_pct) / 2), 20)
            bar = '█' * bar_length
            report_lines.append(f"{indicator_name:12s} {change_pct:6.1f}% {bar}")
        else:
            report_lines.append(f"{indicator_name:12s} {change_pct:6.1f}%")
    
    report_lines.append("")  # 빈 줄
    
    # ========================================================================
    # 2.9 중요도 가중 위험도 점수 (Phase 3)
    # ========================================================================
    report_lines.append("[중요도 가중 위험도 점수]")
    
    weighted_score_A = 0
    weighted_score_B = 0
    
    for indicator in indicator_order:
        indicator_row = rr_result_df[rr_result_df['지표'] == indicator]
        if len(indicator_row) == 0:
            continue
        
        row = indicator_row.iloc[0]
        risk_A = row['A위험도']
        risk_B = row['B위험도']
        
        if pd.isna(risk_A) or pd.isna(risk_B):
            continue
        
        importance = indicator_importance.get(indicator, 1)
        weighted_score_A += risk_A * importance
        weighted_score_B += risk_B * importance
    
    weighted_rr = weighted_score_B / weighted_score_A if weighted_score_A > 0 else 0
    weighted_change = (weighted_rr - 1) * 100
    
    report_lines.append(
        f"가중 위험점수: A그룹 {weighted_score_A:.2f}, B그룹 {weighted_score_B:.2f} "
        f"(상대비율: {weighted_rr:.2f}배, 차이: {weighted_change:+.1f}%)"
    )
    
    if weighted_rr >= 1.15:
        report_lines.append("→ B그룹의 가중 위험점수가 A그룹보다 15% 이상 높습니다 (주의 필요)")
    elif weighted_rr <= 0.90:
        report_lines.append("→ B그룹의 가중 위험점수가 A그룹보다 낮습니다 (양호)")
    else:
        report_lines.append("→ 두 그룹의 가중 위험점수가 유사합니다")
    
    report_lines.append("")  # 빈 줄
    
    # ========================================================================
    # 2.10 요약 테이블 (Phase 3)
    # ========================================================================
    report_lines.append("[요약 테이블]")
    report_lines.append("-" * 100)
    report_lines.append(f"{'지표':<12} {'A위험도':>10} {'B위험도':>10} {'차이':>10} {'상대RR':>8} {'p값':>8} {'판정':<15}")
    report_lines.append("-" * 100)
    
    for indicator in indicator_order:
        indicator_row = rr_result_df[rr_result_df['지표'] == indicator]
        if len(indicator_row) == 0:
            continue
        
        row = indicator_row.iloc[0]
        risk_A = row['A위험도']
        risk_B = row['B위험도']
        rr = row['상대위험도']
        
        if pd.isna(risk_A) or pd.isna(risk_B) or pd.isna(rr):
            continue
        
        indicator_name = indicator_names_ko.get(indicator, indicator)
        unit_symbol = indicator_unit.get(indicator, ('위험률', '%'))[1]
        
        # 표시 형식 결정
        if unit_symbol == '%':
            risk_A_display = risk_A * 100
            risk_B_display = risk_B * 100
            diff_display = (risk_B - risk_A) * 100
        else:
            risk_A_display = risk_A
            risk_B_display = risk_B
            diff_display = risk_B - risk_A
        
        # p-value 가져오기
        p_value_str = "-"
        if stat_test_df is not None:
            stat_row = stat_test_df[stat_test_df['지표'] == indicator]
            if len(stat_row) > 0:
                p_val = stat_row.iloc[0].get('p_value', None)
                if p_val is not None and not pd.isna(p_val):
                    p_value_str = f"{p_val:.4f}"
        
        # 판정
        if isinstance(rr, str) and rr == 'Inf':
            rr_display = "Inf"
            significance = "유의↑"
        else:
            rr_value = float(rr)
            rr_display = f"{rr_value:.2f}"
            
            if rr_value >= rr_threshold_high:
                significance = "유의↑"
            elif rr_value <= rr_threshold_low:
                significance = "유의↓"
            else:
                significance = "-"
        
        report_lines.append(
            f"{indicator_name:<12} {risk_A_display:10.3f} {risk_B_display:10.3f} "
            f"{diff_display:+10.3f} {rr_display:>8} {p_value_str:>8} {significance:<15}"
        )
    
    report_lines.append("-" * 100)
    report_lines.append("")  # 빈 줄
    
    # ========================================================================
    # 3. KCD 추적결과 요약 (kcd_name 매칭 기능 추가)
    # ========================================================================
    if kcd_result_df is not None and len(kcd_result_df) > 0:
        report_lines.append("[KCD 추적결과 요약]")
        
        if followup_years is not None:
            report_lines.append(f"사후 {followup_years}년 추적 기준 결과입니다.")
        
        # kcd_name_df와 매칭
        kcd_name_mapping = {}
        if kcd_name_df is not None and len(kcd_name_df) > 0:
            # kcd_name_df의 컬럼명 확인
            kcd_col = None
            name_col = None
            
            for col in kcd_name_df.columns:
                if 'kcd' in col.lower():
                    kcd_col = col
                if 'name' in col.lower() or '한글' in col.lower() or '질병' in col.lower():
                    name_col = col
            
            if kcd_col and name_col:
                for _, name_row in kcd_name_df.iterrows():
                    kcd_pattern = str(name_row[kcd_col]).strip()
                    kcd_name = str(name_row[name_col]).strip()
                    kcd_name_mapping[kcd_pattern] = kcd_name
                
                # 디버깅용 출력 (필요시 주석 해제)
                # print(f"  kcd_name_mapping: {kcd_name_mapping}")
        
        # 위험한 질병코드 필터링 (암, 뇌혈관질환, 심장질환 발생이 있는 것)
        kcd_with_risk = []
        for _, row in kcd_result_df.iterrows():
            cancer = int(row.get('cancer', 0))
            brain = int(row.get('brain', 0))
            heart = int(row.get('heart', 0))
            total_risk = cancer + brain + heart
            
            if total_risk > 0:  # 위험 질병 발생이 있는 경우만
                kcd_with_risk.append({
                    'kcd': row['kcd'],
                    'ID수': int(row['ID수']),
                    'cancer': cancer,
                    'brain': brain,
                    'heart': heart,
                    'disease_sur': int(row.get('disease_sur', 0)),
                    'injury_sur': int(row.get('injury_sur', 0)),
                    'disease_hos': int(row.get('disease_hos', 0)),
                    'injury_hos': int(row.get('injury_hos', 0)),
                    'total_risk': total_risk
                })
        
        # 위험도 순으로 정렬 (암+뇌+심장 합계 기준)
        kcd_with_risk.sort(key=lambda x: (x['total_risk'], x['ID수']), reverse=True)
        
        # 상위 10개 또는 모든 위험 질병코드 표시
        kcd_to_show = kcd_with_risk[:10] if len(kcd_with_risk) > 10 else kcd_with_risk
        
        if kcd_to_show:
            report_lines.append("")
            report_lines.append("[위험 질병코드 분석]")
            report_lines.append("(암, 뇌혈관질환, 심장질환 발생이 있는 질병코드)")
            report_lines.append("")
            
            for kcd_data in kcd_to_show:
                kcd_code = kcd_data['kcd']
                
                # kcd_name 매칭
                kcd_display_name = None
                if kcd_code in kcd_name_mapping:
                    kcd_display_name = kcd_name_mapping[kcd_code]
                else:
                    # 정규표현식 패턴을 더 읽기 쉽게 변환
                    if kcd_code.startswith('^'):
                        kcd_display = kcd_code[1:]  # ^ 제거
                        if kcd_display.endswith('[0-9]'):
                            kcd_display = kcd_display.replace('[0-9]', '코드')
                    else:
                        kcd_display = kcd_code
                    
                    if not kcd_display.endswith('코드') and not kcd_display.endswith(']'):
                        kcd_display_name = f"{kcd_display} 코드"
                    else:
                        kcd_display_name = kcd_display
                
                id_count = kcd_data['ID수']
                cancer = kcd_data['cancer']
                brain = kcd_data['brain']
                heart = kcd_data['heart']
                d_sur = kcd_data['disease_sur']
                i_sur = kcd_data['injury_sur']
                d_hos = kcd_data['disease_hos']
                i_hos = kcd_data['injury_hos']
                
                # 위험 질병 발생률 계산
                risk_rate = (cancer + brain + heart) / id_count * 100 if id_count > 0 else 0
                
                # 0인 항목은 생략
                items = []
                if cancer > 0:
                    items.append(f"암 {cancer}건")
                if brain > 0:
                    items.append(f"뇌혈관질환 {brain}건")
                if heart > 0:
                    items.append(f"심장질환 {heart}건")
                if d_sur > 0:
                    items.append(f"질병수술 {d_sur}건")
                if i_sur > 0:
                    items.append(f"외상수술 {i_sur}건")
                if d_hos > 0:
                    items.append(f"질병입원일수 {d_hos}일")
                if i_hos > 0:
                    items.append(f"외상입원일수 {i_hos}일")
                
                if items:
                    report_lines.append(
                        f"{kcd_display_name}({id_count}명, 위험질병 발생률 {risk_rate:.1f}%): "
                        f"{', '.join(items)}."
                    )
            
            report_lines.append("")
        
        # 기존 방식: 상위 5개 KCD (ID수 기준)
        report_lines.append("[전체 KCD 추적결과 (상위 5개)]")
        kcd_sorted = kcd_result_df.sort_values('ID수', ascending=False).head(5)
        
        for _, row in kcd_sorted.iterrows():
            kcd_code = row['kcd']
            
            # kcd_name 매칭
            kcd_display_name = None
            if kcd_code in kcd_name_mapping:
                kcd_display_name = kcd_name_mapping[kcd_code]
            else:
                # 정규표현식 패턴을 더 읽기 쉽게 변환
                if kcd_code.startswith('^'):
                    kcd_display = kcd_code[1:]  # ^ 제거
                    if kcd_display.endswith('[0-9]'):
                        kcd_display = kcd_display.replace('[0-9]', '코드')
                    elif '[' in kcd_display:
                        pass
                else:
                    kcd_display = kcd_code
                
                if not kcd_display.endswith('코드') and not kcd_display.endswith(']'):
                    kcd_display_name = f"{kcd_display} 코드"
                else:
                    kcd_display_name = kcd_display
            
            id_count = int(row['ID수'])
            cancer = int(row['cancer'])
            brain = int(row['brain'])
            heart = int(row['heart'])
            d_sur = int(row['disease_sur'])
            i_sur = int(row['injury_sur'])
            d_hos = int(row['disease_hos'])
            i_hos = int(row['injury_hos'])
            
            # 0인 항목은 생략
            items = []
            if cancer > 0:
                items.append(f"암 {cancer}건")
            if brain > 0:
                items.append(f"뇌혈관질환 {brain}건")
            if heart > 0:
                items.append(f"심장질환 {heart}건")
            if d_sur > 0:
                items.append(f"질병수술 {d_sur}건")
            if i_sur > 0:
                items.append(f"외상수술 {i_sur}건")
            if d_hos > 0:
                items.append(f"질병입원일수 {d_hos}일")
            if i_hos > 0:
                items.append(f"외상입원일수 {i_hos}일")
            
            if items:
                report_lines.append(f"{kcd_display_name}({id_count}명): {', '.join(items)}.")
        
        report_lines.append("")  # 빈 줄
    
    # ========================================================================
    # 4. 결론 및 시사점
    # ========================================================================
    report_lines.append("[결론 및 시사점]")
    
    conclusion_lines = []
    
    if significant_high:
        high_names = [item.split('(')[0] for item in significant_high]
        if len(high_names) == 1:
            conclusion_lines.append(
                f"B그룹은 {high_names[0]}에서 유의하게 높은 위험률을 보였습니다."
            )
        else:
            conclusion_lines.append(
                f"B그룹은 {', '.join(high_names[:-1])} 및 {high_names[-1]}에서 유의하게 높은 위험률을 보였습니다."
            )
        
        # 가장 높은 상대위험도 찾기
        max_rr_item = None
        max_rr_value = 0
        for item in significant_high:
            try:
                # "암(1.44배, +1.1%p)" 형식에서 1.44 추출
                rr_str = item.split('(')[1].split('배')[0]
                if rr_str != '무한대':
                    rr_val = float(rr_str)
                    if rr_val > max_rr_value:
                        max_rr_value = rr_val
                        max_rr_item = item
            except:
                pass
        
        if max_rr_item and max_rr_item != '무한대':
            indicator_name = max_rr_item.split('(')[0]
            conclusion_lines.append(
                f"특히 {indicator_name}의 위험률은 A그룹 대비 {max_rr_value:.2f}배 수준으로, "
                "지속적인 모니터링이 필요합니다."
            )
    
    if significant_low:
        low_names = [item.split('(')[0] for item in significant_low]
        if len(low_names) == 1:
            conclusion_lines.append(
                f"반면 {low_names[0]}은(는) 유의하게 낮은 수준으로 관찰됩니다."
            )
        else:
            conclusion_lines.append(
                f"반면 {', '.join(low_names[:-1])} 및 {low_names[-1]}은(는) 유의하게 낮은 수준으로 관찰됩니다."
            )
    
    if not significant_high and not significant_low:
        conclusion_lines.append(
            "B그룹과 A그룹 간에 통계적으로 유의한 차이는 관찰되지 않았습니다."
        )
        conclusion_lines.append(
            "두 그룹의 위험도는 유사한 수준으로 나타나며, 추가적인 분석이 필요할 수 있습니다."
        )
    else:
        conclusion_lines.append(
            "향후 언더라이팅 정책 수립 시 이러한 질병군 중심의 리스크 차별화 관리가 필요합니다."
        )
    
    report_lines.extend(conclusion_lines)
    report_lines.append("")  # 빈 줄
    
    # ========================================================================
    # 5. 주의 문구 (표본수 기준 미달 시)
    # ========================================================================
    if n_A is not None and n_A < 20:
        report_lines.append("(※ 그룹 A의 표본이 작아 해석에 주의가 필요합니다.)")
    if n_B is not None and n_B < 20:
        report_lines.append("(※ 그룹 B의 표본이 작아 해석에 주의가 필요합니다.)")
    
    # 리포트 문자열 생성
    report_text = "\n".join(report_lines)
    
    # HTML 형식 변환 (옵션)
    if output_format == 'html':
        report_text = convert_to_html(report_text)
    
    return report_text


def convert_to_html(report_text):
    """텍스트 리포트를 HTML 형식으로 변환"""
    lines = report_text.split('\n')
    html_lines = []
    
    for line in lines:
        if line.strip() == '':
            html_lines.append('<p></p>')
        elif line.startswith('[') and line.endswith(']'):
            # 섹션 제목
            title = line.strip('[]')
            html_lines.append(f'<h3><strong>{title}</strong></h3>')
        elif line.startswith('(※'):
            # 주의 문구
            html_lines.append(f'<p><em>{line}</em></p>')
        else:
            # 일반 문장
            html_lines.append(f'<p>{line}</p>')
    
    return '\n'.join(html_lines)

