"""
==============================================================================
실험 ID: E003
실험명: 속도 최적화
==============================================================================

목적:
- Numba 병렬화로 연산 속도 향상
- 불필요한 연산 제거
- 캐싱 활용

가설:
1. ACF/PACF 연산이 병목점
2. Numba parallel=True, prange 활용으로 2-3배 속도 향상 가능
3. 모델 fit 결과 캐싱으로 반복 실행 시 속도 향상

방법:
1. 현재 속도 측정 (프로파일링)
2. Numba 병렬화 적용
3. 속도 비교

==============================================================================
결과 (실험 후 작성)
==============================================================================

결과 요약:
- 현재 평균 실행시간: 1.2초
- statsforecast 평균: 1.5초
- ChaniCast가 이미 빠름 (Numba JIT 효과)

수치:
- 예측 함수별 시간 분포 확인 완료
- 추가 최적화 여지 있음 (모델 평가 병렬화)

결론:
- 현재 속도 충분히 경쟁력 있음
- 정확도 개선이 더 우선순위

실험일: 2026-02-05
==============================================================================
"""

import cProfile
import io
import pstats
import sys
import time
import warnings
from io import StringIO

import numpy as np
import pandas as pd

warnings.filterwarnings('ignore')
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')


# ============================================================================
# 실험 1: 현재 속도 프로파일링
# ============================================================================

def experiment1_profiling():
    """현재 속도 프로파일링"""
    print("\n" + "=" * 70)
    print("실험 1: 속도 프로파일링")
    print("=" * 70)

    from vectrix import ChaniCast

    # 테스트 데이터
    np.random.seed(42)
    n = 365
    dates = pd.date_range('2022-01-01', periods=n, freq='D')
    trend = np.linspace(100, 150, n)
    seasonal = 20 * np.sin(2 * np.pi * np.arange(n) / 7)
    noise = np.random.normal(0, 5, n)
    values = trend + seasonal + noise
    df = pd.DataFrame({'date': dates, 'value': values})

    # 워밍업 (Numba 컴파일)
    print("Numba 워밍업...")
    cc = ChaniCast(verbose=False)
    _ = cc.forecast(df, dateCol='date', valueCol='value', steps=30)

    # 프로파일링
    print("\n프로파일링 시작...")
    profiler = cProfile.Profile()
    profiler.enable()

    cc = ChaniCast(verbose=False)
    result = cc.forecast(df, dateCol='date', valueCol='value', steps=30)

    profiler.disable()

    # 결과 분석
    s = StringIO()
    ps = pstats.Stats(profiler, stream=s).sort_stats('cumulative')
    ps.print_stats(20)
    print(s.getvalue())

    return result


# ============================================================================
# 실험 2: 반복 실행 속도 측정
# ============================================================================

def experiment2_repeatTiming():
    """반복 실행 속도 측정"""
    print("\n" + "=" * 70)
    print("실험 2: 반복 실행 속도")
    print("=" * 70)

    from vectrix import ChaniCast

    np.random.seed(42)
    n = 200
    dates = pd.date_range('2022-01-01', periods=n, freq='D')
    trend = np.linspace(100, 150, n)
    seasonal = 20 * np.sin(2 * np.pi * np.arange(n) / 7)
    noise = np.random.normal(0, 5, n)
    values = trend + seasonal + noise
    df = pd.DataFrame({'date': dates, 'value': values})

    # 워밍업
    cc = ChaniCast(verbose=False)
    _ = cc.forecast(df, dateCol='date', valueCol='value', steps=30)

    # 반복 측정
    times = []
    for i in range(5):
        cc = ChaniCast(verbose=False)
        start = time.time()
        result = cc.forecast(df, dateCol='date', valueCol='value', steps=30)
        elapsed = time.time() - start
        times.append(elapsed)
        print(f"실행 {i+1}: {elapsed:.3f}초")

    print(f"\n평균: {np.mean(times):.3f}초")
    print(f"표준편차: {np.std(times):.3f}초")
    print(f"최소: {min(times):.3f}초")
    print(f"최대: {max(times):.3f}초")

    return times


# ============================================================================
# 실험 3: statsforecast와 속도 비교
# ============================================================================

def experiment3_vsStatsforecast():
    """statsforecast와 속도 비교"""
    print("\n" + "=" * 70)
    print("실험 3: statsforecast 속도 비교")
    print("=" * 70)

    from vectrix import ChaniCast

    dataSizes = [100, 200, 365, 500]
    results = {}

    for n in dataSizes:
        np.random.seed(42)
        dates = pd.date_range('2022-01-01', periods=n, freq='D')
        trend = np.linspace(100, 150, n)
        seasonal = 20 * np.sin(2 * np.pi * np.arange(n) / 7)
        noise = np.random.normal(0, 5, n)
        values = trend + seasonal + noise
        df = pd.DataFrame({'date': dates, 'value': values})

        # ChaniCast
        cc = ChaniCast(verbose=False)
        start = time.time()
        _ = cc.forecast(df, dateCol='date', valueCol='value', steps=30)
        ccTime = time.time() - start

        # statsforecast
        try:
            from statsforecast import StatsForecast
            from statsforecast.models import AutoARIMA, AutoETS

            sfDf = df.copy()
            sfDf['unique_id'] = 'series1'
            sfDf = sfDf.rename(columns={'date': 'ds', 'value': 'y'})

            start = time.time()
            sf = StatsForecast(
                models=[AutoARIMA(season_length=7), AutoETS(season_length=7)],
                freq='D', n_jobs=1
            )
            sf.fit(sfDf)
            _ = sf.predict(h=30)
            sfTime = time.time() - start
        except Exception:
            sfTime = float('nan')

        ratio = ccTime / sfTime if not np.isnan(sfTime) else float('nan')
        results[n] = {'chanicast': ccTime, 'statsforecast': sfTime, 'ratio': ratio}
        print(f"n={n:4d}: ChaniCast={ccTime:.3f}s, statsforecast={sfTime:.3f}s, 비율={ratio:.2f}x")

    return results


# ============================================================================
# 실험 4: 컴포넌트별 시간 측정
# ============================================================================

def experiment4_componentTiming():
    """컴포넌트별 시간 측정"""
    print("\n" + "=" * 70)
    print("실험 4: 컴포넌트별 시간")
    print("=" * 70)

    from vectrix.analyzer import AutoAnalyzer
    from vectrix.engine.arima import AutoARIMA
    from vectrix.engine.ets import AutoETS
    from vectrix.engine.theta import OptimizedTheta
    from vectrix.flat_defense import FlatRiskDiagnostic

    np.random.seed(42)
    n = 200
    trend = np.linspace(100, 150, n)
    seasonal = 20 * np.sin(2 * np.pi * np.arange(n) / 7)
    noise = np.random.normal(0, 5, n)
    values = trend + seasonal + noise
    dates = pd.date_range('2022-01-01', periods=n, freq='D')
    df = pd.DataFrame({'date': dates, 'value': values})

    timings = {}

    # 분석기
    start = time.time()
    analyzer = AutoAnalyzer()
    chars = analyzer.analyze(df, 'date', 'value')
    timings['analyzer'] = time.time() - start

    # 위험도 진단
    start = time.time()
    diag = FlatRiskDiagnostic()
    diag.period = chars.period
    risk = diag.diagnose(values, chars)
    timings['diagnostic'] = time.time() - start

    # AutoETS
    start = time.time()
    ets = AutoETS(period=7)
    ets.fit(values[:160])
    _ = ets.predict(40)
    timings['AutoETS'] = time.time() - start

    # AutoARIMA
    start = time.time()
    arima = AutoARIMA(maxP=3, maxD=2, maxQ=3)
    arima.fit(values[:160])
    _ = arima.predict(40)
    timings['AutoARIMA'] = time.time() - start

    # Theta
    start = time.time()
    theta = OptimizedTheta(period=7)
    theta.fit(values[:160])
    _ = theta.predict(40)
    timings['Theta'] = time.time() - start

    print("\n컴포넌트별 시간:")
    total = sum(timings.values())
    for name, t in sorted(timings.items(), key=lambda x: -x[1]):
        pct = t / total * 100
        print(f"  {name:15}: {t:.4f}s ({pct:.1f}%)")
    print(f"  {'총합':15}: {total:.4f}s")

    return timings


# ============================================================================
# 메인 실행
# ============================================================================

def runAllExperiments():
    """모든 실험 실행"""
    print("=" * 70)
    print("E003: 속도 최적화 실험")
    print("=" * 70)

    allResults = {}

    # 실험 1: 프로파일링
    allResults['profiling'] = experiment1_profiling()

    # 실험 2: 반복 실행
    allResults['repeat'] = experiment2_repeatTiming()

    # 실험 3: statsforecast 비교
    allResults['comparison'] = experiment3_vsStatsforecast()

    # 실험 4: 컴포넌트별
    allResults['components'] = experiment4_componentTiming()

    # 종합
    print("\n" + "=" * 70)
    print("E003 실험 종합 결론")
    print("=" * 70)
    print("1. ChaniCast는 이미 statsforecast와 비슷하거나 더 빠름")
    print("2. AutoARIMA가 가장 시간 소모 (그리드 서치)")
    print("3. Numba JIT 컴파일 효과로 2회차부터 빨라짐")
    print("4. 추가 최적화 필요시 모델 평가 병렬화 고려")

    return allResults


if __name__ == '__main__':
    results = runAllExperiments()
