"""
数据库模块基础测试

测试 SQLite DAO、ChromaDB DAO 和 DatabaseManager 的基本功能。
"""

import os
import sys
import tempfile
import unittest
from pathlib import Path

# 将项目根目录添加到 Python 路径
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))

import numpy as np

from src.database import (
    SQLiteDAO,
    ChromaDAO,
    DatabaseManager,
    QuestionModel,
    QuestionCreateDTO
)
from src.utils.helpers import generate_uuid, get_current_timestamp


class TestSQLiteDAO(unittest.TestCase):
    """SQLite DAO 测试"""
    
    def setUp(self):
        """测试前准备"""
        # 使用临时文件
        self.temp_dir = tempfile.mkdtemp()
        self.db_path = os.path.join(self.temp_dir, "test.db")
        self.dao = SQLiteDAO(self.db_path)
        self.dao.initialize_schema()
    
    def tearDown(self):
        """测试后清理"""
        self.dao.close()
        # 清理临时文件
        if os.path.exists(self.db_path):
            os.remove(self.db_path)
        os.rmdir(self.temp_dir)
    
    def test_initialize_schema(self):
        """测试数据库初始化"""
        self.assertTrue(self.dao.check_schema_initialized())
    
    def test_create_and_get_question(self):
        """测试创建和获取题目"""
        # 创建测试题目
        question = QuestionModel(
            question_id=generate_uuid(),
            title="测试题目",
            content="这是一个测试题目的内容",
            question_type="单选",
            category="测试分类",
            difficulty="简单",
            status="草稿",
            tags=["测试", "Python"],
            created_at=get_current_timestamp(),
            updated_at=get_current_timestamp()
        )
        
        # 创建题目
        self.assertTrue(self.dao.create_question(question))
        
        # 获取题目
        retrieved = self.dao.get_question(question.question_id)
        self.assertIsNotNone(retrieved)
        if retrieved:
            self.assertEqual(retrieved['title'], "测试题目")
            self.assertIn("测试", retrieved['tags'])
    
    def test_update_question(self):
        """测试更新题目"""
        # 先创建题目
        question = QuestionModel(
            question_id=generate_uuid(),
            title="原标题",
            content="原内容",
            question_type="单选",
            category="测试",
            difficulty="简单",
            status="草稿",
            created_at=get_current_timestamp(),
            updated_at=get_current_timestamp()
        )
        self.dao.create_question(question)
        
        # 更新题目
        updates = {"title": "新标题", "difficulty": "困难"}
        self.assertTrue(self.dao.update_question(question.question_id, updates))
        
        # 验证更新
        updated = self.dao.get_question(question.question_id)
        self.assertIsNotNone(updated)
        if updated:
            self.assertEqual(updated['title'], "新标题")
            self.assertEqual(updated['difficulty'], "困难")
    
    def test_delete_question(self):
        """测试删除题目"""
        # 创建题目
        question = QuestionModel(
            question_id=generate_uuid(),
            title="要删除的题目",
            content="内容",
            question_type="单选",
            category="测试",
            difficulty="简单",
            status="草稿",
            created_at=get_current_timestamp(),
            updated_at=get_current_timestamp()
        )
        self.dao.create_question(question)
        
        # 软删除
        self.assertTrue(self.dao.delete_question(question.question_id, soft_delete=True))
        
        # 验证状态变为已归档
        deleted = self.dao.get_question(question.question_id)
        self.assertIsNotNone(deleted)
        if deleted:
            self.assertEqual(deleted['status'], "已归档")


class TestChromaDAO(unittest.TestCase):
    """ChromaDB DAO 测试"""
    
    def setUp(self):
        """测试前准备"""
        self.temp_dir = tempfile.mkdtemp()
        self.dao = ChromaDAO(
            persist_dir=self.temp_dir,
            collection_name="test_collection"
        )
        self.dao.initialize_collection()
    
    def tearDown(self):
        """测试后清理"""
        self.dao.close()
        # 清理临时目录
        import shutil
        import time
        import gc

        # 多次触发垃圾回收以确保文件句柄释放
        for _ in range(3):
            gc.collect()
            time.sleep(0.1)

        # 增加重试逻辑以处理文件占用问题
        for i in range(10):  # 增加重试次数
            try:
                if os.path.exists(self.temp_dir):
                    shutil.rmtree(self.temp_dir)
                break
            except PermissionError as e:
                if i < 9:  # 最后一次重试时不再等待
                    time.sleep(0.8)  # 增加等待时间到0.8秒
            except Exception as e:
                # 其他异常也进行重试
                if i < 9:
                    time.sleep(0.8)
                raise
        else:
            # 如果重试后仍然失败，尝试强制删除
            if os.path.exists(self.temp_dir):
                try:
                    # 尝试使用Windows特定的强制删除
                    os.system(f'rmdir /s /q "{self.temp_dir}" 2>nul || true')
                    time.sleep(0.5)
                    if os.path.exists(self.temp_dir):
                        shutil.rmtree(self.temp_dir, ignore_errors=True)
                except Exception as e:
                    print(f"警告: 无法删除临时目录 {self.temp_dir}: {e}")
                    # 不抛出异常，避免测试失败
    
    def test_initialize_collection(self):
        """测试Collection初始化"""
        collection = self.dao.get_collection()
        self.assertIsNotNone(collection)
        self.assertEqual(collection.name, "test_collection")
    
    def test_add_and_count_documents(self):
        """测试添加文档"""
        question_id = generate_uuid()
        content = "测试题目内容"
        embedding = np.array([0.1] * 384, dtype=np.float32)  # 模拟384维向量
        metadata = {
            "category": "测试",
            "difficulty": "简单",
            "question_type": "单选",
            "status": "草稿"
        }
        
        # 添加文档
        self.assertTrue(self.dao.add_document(
            question_id=question_id,
            content=content,
            embedding=embedding,
            metadata=metadata
        ))
        
        # 验证文档数量
        self.assertEqual(self.dao.count_documents(), 1)
        
        # 验证文档存在
        self.assertTrue(self.dao.document_exists(question_id))
    
    def test_search_similar(self):
        """测试相似度检索"""
        # 添加几个文档
        for i in range(3):
            question_id = f"test_{i}"
            embedding = np.array([0.1 + i * 0.1] * 384, dtype=np.float32)
            metadata = {"category": "测试", "difficulty": "简单"}
            
            self.dao.add_document(
                question_id=question_id,
                content=f"题目{i}",
                embedding=embedding,
                metadata=metadata
            )
        
        # 搜索相似文档
        query_embedding = np.array([0.1] * 384, dtype=np.float32)
        results = self.dao.search_similar(
            query_embedding=query_embedding,
            top_k=2
        )
        
        # 验证结果
        self.assertEqual(len(results), 2)
        self.assertIn('similarity_score', results[0])


class TestDatabaseManager(unittest.TestCase):
    """DatabaseManager 测试"""
    
    def setUp(self):
        """测试前准备"""
        self.temp_dir = tempfile.mkdtemp()
        
        # 初始化SQLite DAO
        self.sqlite_path = os.path.join(self.temp_dir, "test.db")
        self.sqlite_dao = SQLiteDAO(self.sqlite_path)
        
        # 初始化ChromaDB DAO
        self.chroma_dir = os.path.join(self.temp_dir, "chroma")
        self.chroma_dao = ChromaDAO(self.chroma_dir, "test_collection")
        
        # 初始化DatabaseManager
        self.manager = DatabaseManager(
            sqlite_dao=self.sqlite_dao,
            chroma_dao=self.chroma_dao,
            embedding_service=None  # 阶段3实现
        )
        
        self.manager.initialize_databases()
    
    def tearDown(self):
        """测试后清理"""
        self.manager.close()
        # 清理临时目录
        import shutil
        import time
        import gc

        # 多次触发垃圾回收以确保文件句柄释放
        for _ in range(3):
            gc.collect()
            time.sleep(0.1)

        # 增加重试逻辑以处理文件占用问题
        for i in range(10):  # 增加重试次数
            try:
                if os.path.exists(self.temp_dir):
                    shutil.rmtree(self.temp_dir)
                break
            except PermissionError as e:
                if i < 9:  # 最后一次重试时不再等待
                    time.sleep(0.8)  # 增加等待时间到0.8秒
            except Exception as e:
                # 其他异常也进行重试
                if i < 9:
                    time.sleep(0.8)
                raise
        else:
            # 如果重试后仍然失败，尝试强制删除
            if os.path.exists(self.temp_dir):
                try:
                    # 尝试使用Windows特定的强制删除
                    os.system(f'rmdir /s /q "{self.temp_dir}" 2>nul || true')
                    time.sleep(0.5)
                    if os.path.exists(self.temp_dir):
                        shutil.rmtree(self.temp_dir, ignore_errors=True)
                except Exception as e:
                    print(f"警告: 无法删除临时目录 {self.temp_dir}: {e}")
                    # 不抛出异常，避免测试失败
    
    def test_initialize_databases(self):
        """测试数据库初始化"""
        self.assertTrue(self.sqlite_dao.check_schema_initialized())
        self.assertIsNotNone(self.chroma_dao.get_collection())
    
    def test_create_question_without_embedding(self):
        """测试创建题目（无向量）"""
        question_data = QuestionCreateDTO(
            title="测试题目",
            content="这是测试内容",
            question_type="单选",
            category="Python",
            difficulty="简单",
            tags=["测试"]
        )
        
        # 创建题目（无embedding服务，不会写入ChromaDB）
        question_id = self.manager.create_question(question_data)
        
        # 验证SQLite中已创建
        question = self.manager.get_question(question_id)
        self.assertIsNotNone(question)
        if question:
            self.assertEqual(question['title'], "测试题目")
    
    def test_get_statistics(self):
        """测试获取统计数据"""
        stats = self.manager.get_statistics()
        
        self.assertIn('total_questions', stats)
        self.assertIn('chroma_document_count', stats)


if __name__ == '__main__':
    unittest.main()
