"""
평가 엔진 테스트 모듈
종합 평가 엔진의 기능을 검증합니다.
"""

import unittest
import numpy as np
import sys
import os
import tempfile
import shutil
from pathlib import Path

# 프로젝트 루트를 Python 경로에 추가
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))

from src.evaluator import Evaluator
from config.evaluation_config import EVALUATION_CONFIG


class TestEvaluator(unittest.TestCase):
    """평가 엔진 테스트"""
    
    def setUp(self):
        """테스트 설정"""
        # 임시 디렉토리 생성
        self.temp_dir = tempfile.mkdtemp()
        
        # 테스트용 더미 파일 생성
        self.create_dummy_files()
        
        # 평가 엔진 초기화
        self.evaluator = Evaluator()
    
    def tearDown(self):
        """테스트 정리"""
        # 임시 디렉토리 삭제
        shutil.rmtree(self.temp_dir)
    
    def create_dummy_files(self):
        """테스트용 더미 파일 생성"""
        # 더미 GLB 파일 생성 (실제로는 간단한 텍스트 파일)
        self.model_path = os.path.join(self.temp_dir, "test_model.glb")
        with open(self.model_path, 'w') as f:
            f.write("dummy glb content")
        
        # 더미 이미지 파일 생성 (실제로는 간단한 텍스트 파일)
        self.reference_path = os.path.join(self.temp_dir, "test_image.png")
        with open(self.reference_path, 'w') as f:
            f.write("dummy image content")
    
    def test_evaluator_initialization(self):
        """평가 엔진 초기화 테스트"""
        self.assertIsNotNone(self.evaluator)
        self.assertIsNotNone(self.evaluator.data_loader)
        self.assertIsNotNone(self.evaluator.renderer)
        self.assertIsNotNone(self.evaluator.map_2d_calculator)
        self.assertIsNotNone(self.evaluator.map_3d_calculator)
        self.assertIsNotNone(self.evaluator.chamfer_calculator)
        self.assertIsNotNone(self.evaluator.emd_calculator)
        self.assertIsNotNone(self.evaluator.class_accuracy_calculator)
    
    def test_calculate_comprehensive_score(self):
        """종합 점수 계산 테스트"""
        # 테스트용 메트릭 데이터
        metrics = {
            '2d_map': 0.8,
            '3d_map': 0.7,
            'chamfer_distance': 0.01,
            'emd': 0.02,
            'class_accuracy': 0.85
        }
        
        score, details = self.evaluator.calculate_comprehensive_score(metrics)
        
        # 종합 점수는 0과 100 사이의 값이어야 함
        self.assertGreaterEqual(score, 0.0)
        self.assertLessEqual(score, 100.0)
        
        # 세부사항은 딕셔너리여야 함
        self.assertIsInstance(details, dict)
        self.assertEqual(len(details), len(metrics))
    
    def test_determine_grade(self):
        """성능 등급 결정 테스트"""
        # A등급 테스트
        grade_a = self.evaluator.determine_grade(95.0)
        self.assertEqual(grade_a, 'A')
        
        # B등급 테스트
        grade_b = self.evaluator.determine_grade(85.0)
        self.assertEqual(grade_b, 'B')
        
        # C등급 테스트
        grade_c = self.evaluator.determine_grade(75.0)
        self.assertEqual(grade_c, 'C')
        
        # D등급 테스트
        grade_d = self.evaluator.determine_grade(65.0)
        self.assertEqual(grade_d, 'D')
        
        # F등급 테스트
        grade_f = self.evaluator.determine_grade(50.0)
        self.assertEqual(grade_f, 'F')
    
    def test_validate_input_files(self):
        """입력 파일 검증 테스트"""
        # 유효한 파일들
        valid = self.evaluator._validate_input_files(self.model_path, self.reference_path)
        self.assertTrue(valid)
        
        # 존재하지 않는 파일
        invalid = self.evaluator._validate_input_files("nonexistent.glb", "nonexistent.png")
        self.assertFalse(invalid)
    
    def test_create_3d_ground_truth(self):
        """3D Ground Truth 생성 테스트"""
        # 테스트용 3D 모델
        model_3d = {
            'vertices': np.random.rand(100, 3),
            'faces': np.random.randint(0, 100, (50, 3)),
            'center': np.array([0, 0, 0]),
            'scale': np.array([1, 1, 1]),
            'bounding_box': (np.array([-1, -1, -1]), np.array([1, 1, 1]))
        }
        
        reference_image = np.random.randint(0, 255, (512, 512, 3), dtype=np.uint8)
        
        ground_truth = self.evaluator._create_3d_ground_truth(model_3d, reference_image)
        
        # Ground Truth는 딕셔너리여야 함
        self.assertIsInstance(ground_truth, dict)
        self.assertIn('vertices', ground_truth)
        self.assertIn('faces', ground_truth)
        self.assertIn('classes', ground_truth)
        self.assertIn('bounding_boxes', ground_truth)
    
    def test_generate_report(self):
        """리포트 생성 테스트"""
        # 테스트용 결과 데이터
        results = {
            'comprehensive_score': 85.5,
            'grade': 'B',
            'evaluation_timestamp': '2024-01-01T00:00:00',
            'model_path': self.model_path,
            'reference_path': self.reference_path,
            'metrics': {
                '2d_map': 0.8,
                '3d_map': 0.7,
                'chamfer_distance': 0.01,
                'emd': 0.02,
                'class_accuracy': 0.85
            },
            'score_details': {
                '2d_map': {
                    'raw_value': 0.8,
                    'normalized_score': 80.0,
                    'weight': 0.25,
                    'weighted_score': 20.0,
                    'threshold': 0.8
                }
            }
        }
        
        # HTML 리포트 생성
        html_report = self.evaluator.generate_report(results)
        self.assertIsInstance(html_report, str)
        self.assertIn('<!DOCTYPE html>', html_report)
        self.assertIn('3D 객체인식 평가 결과', html_report)
        
        # JSON 리포트 생성
        self.evaluator.config['output']['report_format'] = 'json'
        json_report = self.evaluator.generate_report(results)
        self.assertIsInstance(json_report, str)
        
        # 텍스트 리포트 생성
        self.evaluator.config['output']['report_format'] = 'txt'
        text_report = self.evaluator.generate_report(results)
        self.assertIsInstance(text_report, str)
        self.assertIn('TRELLIS 모델 평가 결과', text_report)


class TestConfiguration(unittest.TestCase):
    """설정 테스트"""
    
    def test_evaluation_config_structure(self):
        """평가 설정 구조 테스트"""
        config = EVALUATION_CONFIG
        
        # 필수 키들이 존재하는지 확인
        required_keys = [
            'weights', 'thresholds', 'rendering', 'pointcloud',
            'classification', 'grade_thresholds', 'iou_thresholds',
            'output', 'file_paths', 'logging', 'performance', 'validation'
        ]
        
        for key in required_keys:
            self.assertIn(key, config)
    
    def test_weights_sum(self):
        """가중치 합이 1.0인지 테스트"""
        weights = EVALUATION_CONFIG['weights']
        weight_sum = sum(weights.values())
        
        # 가중치 합은 1.0에 가까워야 함
        self.assertAlmostEqual(weight_sum, 1.0, places=6)
    
    def test_thresholds_range(self):
        """임계값이 유효한 범위인지 테스트"""
        thresholds = EVALUATION_CONFIG['thresholds']
        
        for metric, threshold in thresholds.items():
            # 임계값은 0과 1 사이의 값이어야 함
            self.assertGreaterEqual(threshold, 0.0)
            self.assertLessEqual(threshold, 1.0)
    
    def test_grade_thresholds_order(self):
        """등급 임계값이 올바른 순서인지 테스트"""
        grade_thresholds = EVALUATION_CONFIG['grade_thresholds']
        
        # 등급 임계값은 내림차순이어야 함
        values = list(grade_thresholds.values())
        self.assertEqual(values, sorted(values, reverse=True))


if __name__ == '__main__':
    # 테스트 실행
    unittest.main(verbosity=2)
