"""
Part 3: Python 컬렉션 실습 테스트
"""

import unittest
import sys
import os

# 상위 디렉토리를 Python 경로에 추가
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

# Part 3 모듈 import
try:
    import part_3_python_collections as part3
except ImportError:
    print("Warning: part_3_python_collections 모듈을 import할 수 없습니다.")
    part3 = None


class TestPart3PythonCollections(unittest.TestCase):
    """Part 3 Python 컬렉션 실습 테스트 클래스"""

    def setUp(self):
        """테스트 설정"""
        self.test_list = [1, 2, "three", 4.0]
        self.test_tuple = (1, 2, "three", 4.0)
        self.test_dict = {"name": "Alice", "age": 25, "city": "New York"}
        self.test_set = {1, 2, 3, 2, 1}

    def test_list_operations(self):
        """리스트 연산 테스트"""
        # 기본 리스트 생성
        my_list = [1, 2, "three", 4.0]
        self.assertEqual(len(my_list), 4)
        self.assertEqual(my_list[0], 1)
        self.assertEqual(my_list[2], "three")

        # append 연산
        my_list.append(5)
        self.assertEqual(len(my_list), 5)
        self.assertEqual(my_list[-1], 5)

        # 슬라이싱
        sliced = my_list[1:3]
        self.assertEqual(sliced, [2, "three"])

        # 리스트 컴프리헨션
        squares = [x**2 for x in range(5)]
        self.assertEqual(squares, [0, 1, 4, 9, 16])

    def test_tuple_operations(self):
        """튜플 연산 테스트"""
        my_tuple = (1, 2, "three", 4.0)
        self.assertEqual(len(my_tuple), 4)
        self.assertEqual(my_tuple[0], 1)
        self.assertEqual(my_tuple[2], "three")

        # 튜플은 불변 객체이므로 수정 시도 시 TypeError 발생
        with self.assertRaises(TypeError):
            my_tuple[0] = 99

    def test_dictionary_operations(self):
        """딕셔너리 연산 테스트"""
        my_dict = {"name": "Alice", "age": 25, "city": "New York"}
        
        # 값 접근
        self.assertEqual(my_dict["name"], "Alice")
        self.assertEqual(my_dict["age"], 25)

        # 새로운 키-값 쌍 추가
        my_dict["email"] = "alice@example.com"
        self.assertIn("email", my_dict)
        self.assertEqual(my_dict["email"], "alice@example.com")

        # 키와 값 확인
        self.assertIn("name", my_dict.keys())
        self.assertIn("Alice", my_dict.values())

    def test_set_operations(self):
        """집합 연산 테스트"""
        my_set = {1, 2, 3, 2, 1}  # 중복 제거
        self.assertEqual(len(my_set), 3)
        self.assertEqual(my_set, {1, 2, 3})

        # 요소 추가
        my_set.add(4)
        self.assertIn(4, my_set)

        # 집합 연산
        other_set = {3, 4, 5, 6}
        
        # 합집합
        union_set = my_set.union(other_set)
        self.assertEqual(union_set, {1, 2, 3, 4, 5, 6})

        # 교집합
        intersection_set = my_set.intersection(other_set)
        self.assertEqual(intersection_set, {3, 4})

        # 차집합
        difference_set = my_set.difference(other_set)
        self.assertEqual(difference_set, {1, 2})

    def test_control_flow(self):
        """제어 흐름 테스트"""
        # if-elif-else
        def get_grade(score):
            if score >= 90:
                return "A"
            elif score >= 80:
                return "B"
            else:
                return "C"

        self.assertEqual(get_grade(95), "A")
        self.assertEqual(get_grade(85), "B")
        self.assertEqual(get_grade(75), "C")

        # for 반복문
        test_list = [1, 2, 3]
        result = []
        for item in test_list:
            result.append(item * 2)
        self.assertEqual(result, [2, 4, 6])

        # while 반복문
        count = 3
        result = []
        while count > 0:
            result.append(count)
            count -= 1
        self.assertEqual(result, [3, 2, 1])

    def test_functions(self):
        """함수 테스트"""
        def greet(name):
            return f"안녕하세요, {name}님!"

        def calculate_area(width, height):
            return width * height

        # greet 함수 테스트
        greeting = greet("파이썬")
        self.assertEqual(greeting, "안녕하세요, 파이썬님!")

        # calculate_area 함수 테스트
        area = calculate_area(10, 5)
        self.assertEqual(area, 50)

    def test_data_types(self):
        """데이터 타입 테스트"""
        # 정수
        my_integer = 10
        self.assertIsInstance(my_integer, int)
        self.assertEqual(my_integer, 10)

        # 실수
        my_float = 3.14
        self.assertIsInstance(my_float, float)
        self.assertEqual(my_float, 3.14)

        # 문자열
        my_string = "Hello, Python!"
        self.assertIsInstance(my_string, str)
        self.assertEqual(my_string, "Hello, Python!")

        # 불리언
        my_boolean = True
        self.assertIsInstance(my_boolean, bool)
        self.assertTrue(my_boolean)

    def test_list_comprehension(self):
        """리스트 컴프리헨션 테스트"""
        # 기본 컴프리헨션
        squares = [x**2 for x in range(5)]
        self.assertEqual(squares, [0, 1, 4, 9, 16])

        # 조건부 컴프리헨션
        even_squares = [x**2 for x in range(10) if x % 2 == 0]
        self.assertEqual(even_squares, [0, 4, 16, 36, 64])

        # 문자열 컴프리헨션
        words = ["hello", "world", "python"]
        upper_words = [word.upper() for word in words]
        self.assertEqual(upper_words, ["HELLO", "WORLD", "PYTHON"])

    def test_dictionary_comprehension(self):
        """딕셔너리 컴프리헨션 테스트"""
        # 기본 딕셔너리 컴프리헨션
        square_dict = {x: x**2 for x in range(5)}
        expected = {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
        self.assertEqual(square_dict, expected)

        # 조건부 딕셔너리 컴프리헨션
        even_square_dict = {x: x**2 for x in range(10) if x % 2 == 0}
        expected_even = {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}
        self.assertEqual(even_square_dict, expected_even)

    def test_set_comprehension(self):
        """집합 컴프리헨션 테스트"""
        # 기본 집합 컴프리헨션
        square_set = {x**2 for x in range(5)}
        self.assertEqual(square_set, {0, 1, 4, 9, 16})

        # 조건부 집합 컴프리헨션
        even_square_set = {x**2 for x in range(10) if x % 2 == 0}
        self.assertEqual(even_square_set, {0, 4, 16, 36, 64})

    def test_error_handling(self):
        """에러 처리 테스트"""
        # 리스트 인덱스 에러
        my_list = [1, 2, 3]
        with self.assertRaises(IndexError):
            _ = my_list[10]

        # 딕셔너리 키 에러
        my_dict = {"name": "Alice"}
        with self.assertRaises(KeyError):
            _ = my_dict["age"]

        # 타입 에러 (튜플 수정 시도)
        my_tuple = (1, 2, 3)
        with self.assertRaises(TypeError):
            my_tuple[0] = 99


class TestPart3Integration(unittest.TestCase):
    """Part 3 통합 테스트"""

    def test_collections_integration(self):
        """컬렉션 통합 테스트"""
        # 리스트에서 딕셔너리 생성
        names = ["Alice", "Bob", "Charlie"]
        ages = [25, 30, 35]
        people = {name: age for name, age in zip(names, ages)}
        
        self.assertEqual(people["Alice"], 25)
        self.assertEqual(people["Bob"], 30)
        self.assertEqual(people["Charlie"], 35)

        # 집합을 사용한 중복 제거
        numbers = [1, 2, 2, 3, 3, 4, 5, 5]
        unique_numbers = list(set(numbers))
        unique_numbers.sort()
        self.assertEqual(unique_numbers, [1, 2, 3, 4, 5])

    def test_function_with_collections(self):
        """함수와 컬렉션 통합 테스트"""
        def process_student_scores(scores_dict):
            """학생 점수를 처리하는 함수"""
            total = sum(scores_dict.values())
            average = total / len(scores_dict)
            highest = max(scores_dict.values())
            lowest = min(scores_dict.values())
            
            return {
                "total": total,
                "average": average,
                "highest": highest,
                "lowest": lowest,
                "count": len(scores_dict)
            }

        scores = {"Alice": 85, "Bob": 92, "Charlie": 78, "Diana": 95}
        result = process_student_scores(scores)
        
        self.assertEqual(result["total"], 350)
        self.assertEqual(result["average"], 87.5)
        self.assertEqual(result["highest"], 95)
        self.assertEqual(result["lowest"], 78)
        self.assertEqual(result["count"], 4)


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