#!/usr/bin/env python3
"""
AI 전문가 양성 과정 - 통합 테스트 (Integration Tests)

이 파일은 전체 AI 강의 과정의 모든 파트를 통합적으로 테스트합니다.
각 파트의 주요 기능들이 서로 연동되어 올바르게 작동하는지 확인합니다.
"""

import unittest
import sys
import os
import tempfile
import shutil
from pathlib import Path
import json
import subprocess
from typing import Dict, Any, List

# 프로젝트 루트 디렉토리 추가
project_root = Path(__file__).parent
sys.path.insert(0, str(project_root))

# 각 파트별 모듈 import 시도
try:
    # 숫자로 시작하는 모듈명은 직접 import할 수 없으므로 importlib를 사용
    import importlib.util
    
    # Part 2-4: Python 기초
    spec = importlib.util.spec_from_file_location(
        "part_2", 
        os.path.join(project_root, "02_python_core_syntax", "part_2_python_core_syntax.py")
    )
    part_2_module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(part_2_module)
    
    spec = importlib.util.spec_from_file_location(
        "part_3", 
        os.path.join(project_root, "03_python_collections", "part_3_python_collections.py")
    )
    part_3_module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(part_3_module)
    
    spec = importlib.util.spec_from_file_location(
        "part_4", 
        os.path.join(project_root, "04_object_oriented_programming", "part_4_object_oriented_programming.py")
    )
    part_4_module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(part_4_module)
    
    # Part 5-7: AI/ML 라이브러리
    spec = importlib.util.spec_from_file_location(
        "part_5", 
        os.path.join(project_root, "05_ai_core_libraries", "part_5_ai_core_libraries.py")
    )
    part_5_module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(part_5_module)
    
    spec = importlib.util.spec_from_file_location(
        "part_6", 
        os.path.join(project_root, "06_machine_learning", "part_6_machine_learning.py")
    )
    part_6_module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(part_6_module)
    
    spec = importlib.util.spec_from_file_location(
        "part_7", 
        os.path.join(project_root, "07_deep_learning", "part_7_deep_learning.py")
    )
    part_7_module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(part_7_module)
    
    # Part 10, 15: 고급 기능
    spec = importlib.util.spec_from_file_location(
        "part_10", 
        os.path.join(project_root, "10_expert_path", "part_10_expert_path.py")
    )
    part_10_module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(part_10_module)
    
    spec = importlib.util.spec_from_file_location(
        "part_15", 
        os.path.join(project_root, "15_capstone_project", "part_15_capstone_project.py")
    )
    part_15_module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(part_15_module)
    
    MODULES_AVAILABLE = True
except ImportError as e:
    print(f"Warning: 일부 모듈을 import할 수 없습니다: {e}")
    MODULES_AVAILABLE = False


class TestAIExpertCourseIntegration(unittest.TestCase):
    """AI 전문가 양성 과정 전체 통합 테스트"""
    
    def setUp(self):
        """테스트 환경 설정"""
        self.temp_dir = tempfile.mkdtemp()
        self.test_data_dir = Path(self.temp_dir) / "test_data"
        self.test_data_dir.mkdir(exist_ok=True)
        
    def tearDown(self):
        """테스트 환경 정리"""
        shutil.rmtree(self.temp_dir, ignore_errors=True)
    
    def test_01_python_core_syntax_integration(self):
        """Part 2: Python 핵심 문법 통합 테스트"""
        # Python 핵심 문법이 다른 파트에서 사용되는지 확인
        test_code = """
def test_function():
    # 변수 할당
    x = 10
    y = 20
    
    # 조건문
    if x < y:
        result = x + y
    else:
        result = x - y
    
    # 반복문
    numbers = []
    for i in range(5):
        numbers.append(i * 2)
    
    # 함수 정의 및 호출
    def multiply(a, b):
        return a * b
    
    return multiply(result, len(numbers))
"""
        # 코드 실행 테스트
        try:
            exec(test_code)
            self.assertTrue(True, "Python 핵심 문법 통합 테스트 통과")
        except Exception as e:
            self.fail(f"Python 핵심 문법 통합 테스트 실패: {e}")
    
    def test_02_python_collections_integration(self):
        """Part 3: Python 컬렉션 통합 테스트"""
        # 컬렉션들이 실제 프로젝트에서 사용되는 시나리오
        test_data = {
            "users": [
                {"id": 1, "name": "Alice", "skills": ["Python", "ML"]},
                {"id": 2, "name": "Bob", "skills": ["Python", "DL"]},
                {"id": 3, "name": "Charlie", "skills": ["Python", "ML", "DL"]}
            ],
            "projects": {
                "ml_project": {"type": "Machine Learning", "status": "active"},
                "dl_project": {"type": "Deep Learning", "status": "planning"},
                "api_project": {"type": "API Development", "status": "completed"}
            }
        }
        
        # 리스트 컴프리헨션
        python_users = [user for user in test_data["users"] if "Python" in user["skills"]]
        self.assertEqual(len(python_users), 3)
        
        # 딕셔너리 컴프리헨션
        user_skills = {user["name"]: user["skills"] for user in test_data["users"]}
        self.assertIn("Alice", user_skills)
        
        # 집합 연산
        all_skills = set()
        for user in test_data["users"]:
            all_skills.update(user["skills"])
        self.assertIn("Python", all_skills)
        self.assertIn("ML", all_skills)
        self.assertIn("DL", all_skills)
    
    def test_03_oop_integration(self):
        """Part 4: 객체지향 프로그래밍 통합 테스트"""
        # AI 모델을 위한 클래스 구조 테스트
        class AIModel:
            def __init__(self, name: str, model_type: str):
                self.name = name
                self.model_type = model_type
                self.parameters = {}
                self.is_trained = False
            
            def add_parameter(self, key: str, value: Any):
                self.parameters[key] = value
            
            def train(self):
                self.is_trained = True
                return f"{self.name} 모델 훈련 완료"
            
            def predict(self, data):
                if not self.is_trained:
                    raise ValueError("모델이 훈련되지 않았습니다")
                return f"{self.name} 예측 결과"
        
        # 모델 인스턴스 생성 및 테스트
        model = AIModel("TestModel", "Classification")
        model.add_parameter("learning_rate", 0.001)
        model.add_parameter("epochs", 100)
        
        self.assertEqual(model.name, "TestModel")
        self.assertFalse(model.is_trained)
        
        # 훈련 및 예측 테스트
        result = model.train()
        self.assertTrue(model.is_trained)
        self.assertIn("훈련 완료", result)
        
        prediction = model.predict("test_data")
        self.assertIn("예측 결과", prediction)
    
    def test_04_ai_libraries_integration(self):
        """Part 5: AI 핵심 라이브러리 통합 테스트"""
        # NumPy, Pandas, Matplotlib 통합 사용 시나리오
        try:
            import numpy as np
            import pandas as pd
            import matplotlib.pyplot as plt
            
            # 데이터 생성
            np.random.seed(42)
            data = np.random.normal(0, 1, 1000)
            
            # Pandas DataFrame 생성
            df = pd.DataFrame({
                'values': data,
                'category': np.random.choice(['A', 'B', 'C'], 1000)
            })
            
            # 기본 통계 계산
            mean_val = df['values'].mean()
            std_val = df['values'].std()
            
            self.assertIsInstance(mean_val, float)
            self.assertIsInstance(std_val, float)
            self.assertAlmostEqual(mean_val, 0, delta=0.1)
            self.assertAlmostEqual(std_val, 1, delta=0.1)
            
            # 그룹별 통계
            group_stats = df.groupby('category')['values'].agg(['mean', 'std'])
            self.assertEqual(len(group_stats), 3)
            
        except ImportError:
            self.skipTest("AI 라이브러리가 설치되지 않았습니다")
    
    def test_05_machine_learning_integration(self):
        """Part 6: 머신러닝 통합 테스트"""
        try:
            from sklearn.datasets import make_classification
            from sklearn.model_selection import train_test_split
            from sklearn.ensemble import RandomForestClassifier
            from sklearn.metrics import accuracy_score
            
            # 데이터 생성
            X, y = make_classification(n_samples=100, n_features=4, random_state=42)
            X_train, X_test, y_train, y_test = train_test_split(
                X, y, test_size=0.2, random_state=42
            )
            
            # 모델 훈련
            model = RandomForestClassifier(n_estimators=10, random_state=42)
            model.fit(X_train, y_train)
            
            # 예측 및 평가
            y_pred = model.predict(X_test)
            accuracy = accuracy_score(y_test, y_pred)
            
            self.assertIsInstance(accuracy, float)
            self.assertGreaterEqual(accuracy, 0.0)
            self.assertLessEqual(accuracy, 1.0)
            
        except ImportError:
            self.skipTest("scikit-learn이 설치되지 않았습니다")
    
    def test_06_deep_learning_integration(self):
        """Part 7: 딥러닝 통합 테스트"""
        try:
            import torch
            import torch.nn as nn
            
            # 간단한 신경망 정의
            class SimpleNN(nn.Module):
                def __init__(self, input_size, hidden_size, output_size):
                    super(SimpleNN, self).__init__()
                    self.layer1 = nn.Linear(input_size, hidden_size)
                    self.layer2 = nn.Linear(hidden_size, output_size)
                    self.relu = nn.ReLU()
                
                def forward(self, x):
                    x = self.relu(self.layer1(x))
                    x = self.layer2(x)
                    return x
            
            # 모델 생성
            model = SimpleNN(input_size=10, hidden_size=5, output_size=1)
            
            # 테스트 데이터 생성
            x = torch.randn(32, 10)  # 배치 크기 32, 입력 크기 10
            
            # 순전파
            output = model(x)
            
            self.assertEqual(output.shape, (32, 1))
            self.assertIsInstance(output, torch.Tensor)
            
        except ImportError:
            self.skipTest("PyTorch가 설치되지 않았습니다")
    
    def test_07_api_integration(self):
        """Part 8-9: API 통합 테스트"""
        try:
            from pydantic import BaseModel
            from typing import Optional
            
            # API 스키마 정의
            class User(BaseModel):
                id: int
                name: str
                email: str
                skills: List[str]
            
            class Project(BaseModel):
                id: int
                name: str
                description: Optional[str] = None
                status: str = "active"
            
            # 데이터 검증 테스트
            user_data = {
                "id": 1,
                "name": "Alice",
                "email": "alice@example.com",
                "skills": ["Python", "ML", "API"]
            }
            
            user = User(**user_data)
            self.assertEqual(user.name, "Alice")
            self.assertIn("Python", user.skills)
            
            # 프로젝트 데이터
            project_data = {
                "id": 1,
                "name": "AI Project",
                "description": "Machine Learning API"
            }
            
            project = Project(**project_data)
            self.assertEqual(project.name, "AI Project")
            self.assertEqual(project.status, "active")  # 기본값
            
        except ImportError:
            self.skipTest("Pydantic이 설치되지 않았습니다")
    
    def test_08_mlops_integration(self):
        """Part 11: MLOps 통합 테스트"""
        # MLOps 워크플로우 시뮬레이션
        class MLOpsPipeline:
            def __init__(self):
                self.data_version = "v1.0"
                self.model_version = "v1.0"
                self.deployment_status = "development"
            
            def data_validation(self, data):
                return len(data) > 0
            
            def model_training(self, data):
                return {"accuracy": 0.85, "version": self.model_version}
            
            def model_deployment(self, model_info):
                if model_info["accuracy"] > 0.8:
                    self.deployment_status = "production"
                    return True
                return False
        
        # 파이프라인 테스트
        pipeline = MLOpsPipeline()
        test_data = [1, 2, 3, 4, 5]
        
        # 데이터 검증
        self.assertTrue(pipeline.data_validation(test_data))
        
        # 모델 훈련
        model_info = pipeline.model_training(test_data)
        self.assertGreater(model_info["accuracy"], 0.8)
        
        # 모델 배포
        deployment_success = pipeline.model_deployment(model_info)
        self.assertTrue(deployment_success)
        self.assertEqual(pipeline.deployment_status, "production")
    
    def test_09_model_optimization_integration(self):
        """Part 12: 모델 최적화 통합 테스트"""
        # 모델 최적화 시뮬레이션
        class ModelOptimizer:
            def __init__(self, original_size: float):
                self.original_size = original_size
                self.optimized_size = original_size * 0.6  # 40% 감소
            
            def get_size_reduction(self):
                return 100 * (1 - self.optimized_size / self.original_size)
            
            def get_performance_improvement(self, original_latency: float):
                optimized_latency = original_latency * 0.8  # 20% 개선
                return 100 * (original_latency - optimized_latency) / original_latency
        
        # 최적화 테스트
        optimizer = ModelOptimizer(100.0)  # 100MB 모델
        
        size_reduction = optimizer.get_size_reduction()
        self.assertAlmostEqual(size_reduction, 40.0, places=1)
        
        performance_improvement = optimizer.get_performance_improvement(100.0)  # 100ms
        self.assertAlmostEqual(performance_improvement, 20.0, places=1)
    
    def test_10_generative_ai_integration(self):
        """Part 13: 생성형 AI 통합 테스트"""
        # 생성형 AI 워크플로우 시뮬레이션
        class GenerativeAI:
            def __init__(self):
                self.model_type = "transformer"
                self.context_length = 512
            
            def generate_text(self, prompt: str, max_length: int = 50):
                # 간단한 텍스트 생성 시뮬레이션
                return f"Generated text based on: {prompt[:20]}..."
            
            def summarize_text(self, text: str):
                # 간단한 요약 시뮬레이션
                words = text.split()
                return " ".join(words[:10]) + "..."
        
        # 생성형 AI 테스트
        gen_ai = GenerativeAI()
        
        # 텍스트 생성
        prompt = "Explain machine learning in simple terms"
        generated = gen_ai.generate_text(prompt)
        self.assertIn("Generated text based on", generated)
        
        # 텍스트 요약
        long_text = "This is a very long text that needs to be summarized for better understanding and comprehension."
        summary = gen_ai.summarize_text(long_text)
        self.assertIn("...", summary)
        self.assertLess(len(summary), len(long_text))
    
    def test_11_ai_ethics_integration(self):
        """Part 14: AI 윤리 통합 테스트"""
        # AI 윤리 검증 시스템 시뮬레이션
        class AIEthicsChecker:
            def __init__(self):
                self.bias_threshold = 0.1
                self.fairness_metrics = ["demographic_parity", "equal_opportunity"]
            
            def check_bias(self, predictions, sensitive_attributes):
                # 편향 검사 시뮬레이션
                bias_score = 0.05  # 낮은 편향
                return bias_score < self.bias_threshold
            
            def check_fairness(self, model_outputs):
                # 공정성 검사 시뮬레이션
                fairness_scores = {
                    "demographic_parity": 0.95,
                    "equal_opportunity": 0.92
                }
                return all(score > 0.9 for score in fairness_scores.values())
        
        # 윤리 검사 테스트
        ethics_checker = AIEthicsChecker()
        
        # 편향 검사
        predictions = [0, 1, 0, 1, 0]
        sensitive_attrs = ["A", "B", "A", "B", "A"]
        is_unbiased = ethics_checker.check_bias(predictions, sensitive_attrs)
        self.assertTrue(is_unbiased)
        
        # 공정성 검사
        model_outputs = {"accuracy": 0.85, "precision": 0.82}
        is_fair = ethics_checker.check_fairness(model_outputs)
        self.assertTrue(is_fair)
    
    def test_12_capstone_project_integration(self):
        """Part 15: 캡스톤 프로젝트 통합 테스트"""
        if not MODULES_AVAILABLE:
            self.skipTest("필요한 모듈을 import할 수 없습니다")
        
        # 캡스톤 프로젝트 워크플로우 시뮬레이션
        class CapstoneWorkflow:
            def __init__(self):
                self.project_name = "AI_Expert_Project"
                self.project_type = "machine_learning"
                self.components = ["data_preprocessing", "model_training", "evaluation"]
            
            def create_project_structure(self):
                return {
                    "name": self.project_name,
                    "type": self.project_type,
                    "components": self.components,
                    "status": "created"
                }
            
            def evaluate_model(self, predictions, true_values):
                # 간단한 모델 평가
                correct = sum(1 for p, t in zip(predictions, true_values) if p == t)
                accuracy = correct / len(true_values) if true_values else 0
                return {"accuracy": accuracy, "total_samples": len(true_values)}
        
        # 워크플로우 테스트
        workflow = CapstoneWorkflow()
        
        # 프로젝트 구조 생성
        project_info = workflow.create_project_structure()
        self.assertEqual(project_info["name"], "AI_Expert_Project")
        self.assertEqual(project_info["status"], "created")
        
        # 모델 평가
        predictions = [0, 1, 0, 1, 1]
        true_values = [0, 1, 0, 1, 0]
        evaluation = workflow.evaluate_model(predictions, true_values)
        
        self.assertIn("accuracy", evaluation)
        self.assertEqual(evaluation["total_samples"], 5)
        self.assertEqual(evaluation["accuracy"], 0.8)  # 4/5 정확도
    
    def test_13_end_to_end_workflow(self):
        """전체 AI 워크플로우 엔드투엔드 테스트"""
        # 전체 AI 프로젝트 워크플로우 시뮬레이션
        class SimpleModel:
            def __init__(self):
                self.is_trained = False
                self.accuracy = 0.0
            
            def fit(self, X, y):
                # 간단한 평균 기반 예측
                self.mean_value = sum(y) / len(y)
                self.is_trained = True
                self.accuracy = 0.75  # 가상 정확도
                return self
            
            def predict(self, X):
                if not self.is_trained:
                    raise ValueError("모델이 훈련되지 않았습니다")
                return [self.mean_value] * len(X)
        
        # 데이터 준비
        X_train = [[1], [2], [3], [4], [5]]
        y_train = [10, 20, 30, 40, 50]
        X_test = [[6], [7]]
        
        # 모델 훈련 및 예측
        model = SimpleModel()
        model.fit(X_train, y_train)
        
        self.assertTrue(model.is_trained)
        self.assertGreater(model.accuracy, 0.7)
        
        predictions = model.predict(X_test)
        self.assertEqual(len(predictions), 2)
        self.assertEqual(predictions[0], predictions[1])  # 평균 기반이므로 동일
    
    def test_14_performance_benchmark(self):
        """성능 벤치마크 통합 테스트"""
        import time
        
        # 성능 측정 함수
        def measure_performance(func, *args, **kwargs):
            start_time = time.time()
            result = func(*args, **kwargs)
            end_time = time.time()
            execution_time = end_time - start_time
            return result, execution_time
        
        # 테스트 함수
        def test_function(n):
            return sum(range(n))
        
        # 성능 측정
        result, execution_time = measure_performance(test_function, 1000)
        
        self.assertEqual(result, 499500)  # 0부터 999까지의 합
        self.assertIsInstance(execution_time, float)
        self.assertGreater(execution_time, 0)
        self.assertLess(execution_time, 1.0)  # 1초 미만이어야 함
    
    def test_15_error_handling_integration(self):
        """에러 처리 통합 테스트"""
        # 커스텀 예외 정의
        class CustomError(Exception):
            pass
        
        def function_with_error():
            raise CustomError("테스트 에러")
        
        # 에러 처리 테스트
        with self.assertRaises(CustomError):
            function_with_error()
        
        # 정상적인 에러 처리
        try:
            function_with_error()
        except CustomError as e:
            self.assertEqual(str(e), "테스트 에러")
        else:
            self.fail("예외가 발생하지 않았습니다")


def run_integration_tests():
    """통합 테스트 실행 함수"""
    # 테스트 스위트 생성
    loader = unittest.TestLoader()
    suite = loader.loadTestsFromTestCase(TestAIExpertCourseIntegration)
    
    # 테스트 실행
    runner = unittest.TextTestRunner(verbosity=2)
    result = runner.run(suite)
    
    return result.wasSuccessful()


if __name__ == "__main__":
    success = run_integration_tests()
    sys.exit(0 if success else 1) 