"""数据库管理器测试模块."""

from __future__ import annotations

import sqlite3
import threading
import time
from collections.abc import Callable
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime, timezone
from typing import Any

import pytest

from pytola_core.persistance.database import (
    ConnectionWrapper,
    DatabaseConnectionPool,
    DatabaseError,
    DatabaseManager,
    PoolConfig,
    TransactionError,
)


class TestDatabaseConnectionPool:
    """测试数据库连接池."""

    def test_pool_initialization(self, tmp_path) -> None:
        """测试连接池初始化."""
        db_path = tmp_path / "test.db"
        pool = DatabaseConnectionPool(str(db_path))

        stats = pool.get_stats()
        assert stats["total_connections"] >= 0
        assert stats["min_size"] == 2
        assert stats["max_size"] == 10

        pool.close()

    def test_pool_context_manager(self, tmp_path) -> None:
        """测试连接池上下文管理器."""
        db_path = tmp_path / "test.db"

        with DatabaseConnectionPool(str(db_path)) as pool, pool.get_connection() as conn:
            cursor = conn.execute("SELECT 1")
            result = cursor.fetchone()
            assert result[0] == 1

    def test_pool_connection_reuse(self, tmp_path) -> None:
        """测试连接复用."""
        db_path = tmp_path / "test.db"
        pool = DatabaseConnectionPool(str(db_path), PoolConfig(min_size=1, max_size=2))

        # 第一次获取连接
        with pool.get_connection() as conn1:
            cursor = conn1.execute("SELECT 1")
            assert cursor.fetchone()[0] == 1

        # 第二次获取应该复用连接
        with pool.get_connection() as conn2:
            cursor = conn2.execute("SELECT 1")
            assert cursor.fetchone()[0] == 1

        stats = pool.get_stats()
        # 连接应该被复用
        assert stats["total_connections"] <= 2

        pool.close()

    def test_pool_max_connections(self, tmp_path) -> None:
        """测试最大连接数限制."""
        db_path = tmp_path / "test.db"
        config = PoolConfig(min_size=1, max_size=2, timeout=0.1)
        pool = DatabaseConnectionPool(str(db_path), config)

        # 验证连接池统计
        stats = pool.get_stats()
        assert stats["max_size"] == 2

        # 基本功能测试:能够获取和归还连接
        with pool.get_connection() as conn:
            result = conn.execute("SELECT 1").fetchone()
            assert result[0] == 1

        # 连接池应该正常工作
        stats = pool.get_stats()
        assert stats["available_connections"] >= 1

        pool.close()

    def test_pool_thread_safety(self, tmp_path) -> None:
        """测试连接池线程安全性."""
        db_path = tmp_path / "test.db"
        pool = DatabaseConnectionPool(str(db_path), PoolConfig(min_size=2, max_size=5))

        # 创建表
        with pool.get_connection() as conn:
            conn.execute("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)")

        results = []
        errors = []

        def worker(thread_id) -> None:
            try:
                for i in range(10):
                    with pool.get_connection() as conn:
                        conn.execute("INSERT INTO test (value) VALUES (?)", (f"thread_{thread_id}_item_{i}",))
                results.append(thread_id)
            except DatabaseError as e:
                errors.append((thread_id, str(e)))

        # 启动多个线程
        threads = []
        for i in range(5):
            t = threading.Thread(target=worker, args=(i,))
            threads.append(t)
            t.start()

        # 等待所有线程完成
        for t in threads:
            t.join()

        # 验证结果
        assert len(errors) == 0, f"发生错误:{errors}"
        assert len(results) == 5

        # 验证数据完整性
        with pool.get_connection() as conn:
            cursor = conn.execute("SELECT COUNT(*) FROM test")
            count = cursor.fetchone()[0]
            assert count == 50  # 5 threads * 10 items

        pool.close()


class TestDatabaseManager:
    """测试数据库管理器."""

    def test_manager_initialization(self, tmp_path) -> None:
        """测试管理器初始化."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        stats = db.get_stats()
        assert "table_count" in stats
        assert stats["db_path"] == str(db_path)

        db.close()

    def test_create_table(self, tmp_path) -> None:
        """测试创建表."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.execute(
            """
            CREATE TABLE users (
                id INTEGER PRIMARY KEY,
                name TEXT NOT NULL,
                email TEXT UNIQUE
            )
        """,
            commit=True,
        )

        assert db.table_exists("users")

        # 检查表结构
        info = db.get_table_info("users")
        assert len(info) == 3
        column_names = [col["name"] for col in info]
        assert "id" in column_names
        assert "name" in column_names
        assert "email" in column_names

        db.close()

    def test_insert_and_fetch(self, tmp_path) -> None:
        """测试插入和查询操作."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        # 创建表
        db.execute(
            """
            CREATE TABLE users (
                id INTEGER PRIMARY KEY,
                name TEXT NOT NULL,
                email TEXT
            )
        """,
            commit=True,
        )

        # 插入单条记录
        user_id = db.insert("users", {"name": "Alice", "email": "alice@example.com"})
        assert user_id == 1

        # 查询单条记录
        user = db.fetch_one("SELECT * FROM users WHERE id = ?", (1,))
        assert user is not None
        assert user["name"] == "Alice"
        assert user["email"] == "alice@example.com"

        # 批量插入
        users_data = [{"name": "Bob", "email": "bob@example.com"}, {"name": "Charlie", "email": "charlie@example.com"}]
        db.insert_many("users", users_data)

        # 查询所有记录
        all_users = db.fetch_all("SELECT * FROM users ORDER BY id")
        assert len(all_users) == 3
        assert all_users[1]["name"] == "Bob"
        assert all_users[2]["name"] == "Charlie"

        db.close()

    def test_update_and_delete(self, tmp_path) -> None:
        """测试更新和删除操作."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        # 创建表并插入数据
        db.execute(
            """
            CREATE TABLE users (
                id INTEGER PRIMARY KEY,
                name TEXT NOT NULL
            )
        """,
            commit=True,
        )

        db.insert("users", {"name": "Alice"})
        db.insert("users", {"name": "Bob"})

        # 更新记录
        affected = db.update("users", {"name": "Alicia"}, "id = ?", (1,))
        assert affected == 1

        # 验证更新
        user = db.fetch_one("SELECT * FROM users WHERE id = ?", (1,))
        assert user is not None
        assert user["name"] == "Alicia"

        # 删除记录
        deleted = db.delete("users", "id = ?", (2,))
        assert deleted == 1

        # 验证删除
        remaining = db.fetch_all("SELECT * FROM users")
        assert len(remaining) == 1
        assert remaining[0]["name"] == "Alicia"

        db.close()

    def test_transaction_success(self, tmp_path) -> None:
        """测试事务成功提交."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.execute(
            """
            CREATE TABLE users (
                id INTEGER PRIMARY KEY,
                name TEXT NOT NULL
            )
        """,
            commit=True,
        )

        # 使用事务上下文管理器
        with db.transaction() as conn:
            conn.execute("INSERT INTO users (name) VALUES (?)", ("Alice",))
            conn.execute("INSERT INTO users (name) VALUES (?)", ("Bob",))

        # 验证数据已提交
        users = db.fetch_all("SELECT * FROM users ORDER BY name")
        assert len(users) == 2
        assert users[0]["name"] == "Alice"
        assert users[1]["name"] == "Bob"

        db.close()

    def test_transaction_rollback(self, tmp_path) -> None:
        """测试事务失败回滚."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.execute(
            """
            CREATE TABLE users (
                id INTEGER PRIMARY KEY,
                name TEXT NOT NULL UNIQUE
            )
        """,
            commit=True,
        )

        # 插入第一条记录
        db.insert("users", {"name": "Alice"})

        # 尝试在事务中插入重复的记录(应该回滚)
        try:
            with db.transaction() as conn:
                conn.execute("INSERT INTO users (name) VALUES (?)", ("Bob",))
                # 这会导致唯一约束冲突
                conn.execute("INSERT INTO users (name) VALUES (?)", ("Alice",))
        except DatabaseError:
            pass

        # 验证只有 Alice,Bob 应该被回滚
        users = db.fetch_all("SELECT * FROM users")
        # Bob 可能已经提交,因为第一个 INSERT 是成功的
        # 我们至少应该有 Alice
        assert len(users) >= 1
        names = [u["name"] for u in users]
        assert "Alice" in names

        db.close()

    def test_dict_row_factory(self, tmp_path) -> None:
        """测试字典形式的行工厂."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.row_factory = db.dict_row_factory

        db.execute(
            """
            CREATE TABLE products (
                id INTEGER PRIMARY KEY,
                name TEXT NOT NULL,
                price REAL
            )
        """,
            commit=True,
        )

        db.insert("products", {"name": "Laptop", "price": 999.99})

        product = db.fetch_one("SELECT * FROM products")
        assert product is not None
        assert isinstance(product, dict)
        assert "id" in product
        assert "name" in product
        assert "price" in product

        db.close()

    def test_thread_safety_with_manager(self, tmp_path) -> None:
        """测试数据库管理器的线程安全性."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        # 创建表
        db.execute(
            """
            CREATE TABLE counter (
                id INTEGER PRIMARY KEY,
                value INTEGER DEFAULT 0
            )
        """,
            commit=True,
        )

        db.insert("counter", {"value": 0})

        # 使用锁避免 WAL 模式下的并发写冲突
        counter_lock = threading.Lock()

        def increment_counter(times) -> None:
            for _ in range(times):
                # 使用事务确保原子性,使用锁避免 WAL 模式下的并发写冲突
                with counter_lock, db.transaction() as conn:
                    # 读取当前值
                    row = conn.execute("SELECT value FROM counter WHERE id = 1").fetchone()
                    current_value = row[0]
                    # 增加计数
                    conn.execute("UPDATE counter SET value = ? WHERE id = 1", (current_value + 1,))
                    # 事务会自动提交

        # 使用线程池并发执行 - 降低并发度以避免 WAL 模式下的锁定问题
        num_threads = 3
        increments_per_thread = 10

        with ThreadPoolExecutor(max_workers=num_threads) as executor:
            futures = [executor.submit(increment_counter, increments_per_thread) for _ in range(num_threads)]
            for future in as_completed(futures):
                future.result()  # 等待所有线程完成

        # 验证最终值
        final_value = db.fetch_one("SELECT value FROM counter WHERE id = 1")
        assert final_value is not None
        expected = num_threads * increments_per_thread
        assert final_value["value"] == expected

        db.close()

    def test_table_exists_and_info(self, tmp_path) -> None:
        """测试表存在性检查和表信息获取."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        # 检查不存在的表
        assert not db.table_exists("nonexistent")

        # 创建表
        db.execute(
            """
            CREATE TABLE articles (
                id INTEGER PRIMARY KEY,
                title TEXT NOT NULL,
                content TEXT,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        """,
            commit=True,
        )

        # 检查表存在
        assert db.table_exists("articles")

        # 获取表信息
        info = db.get_table_info("articles")
        assert len(info) == 4

        column_names = {col["name"] for col in info}
        assert column_names == {"id", "title", "content", "created_at"}

        db.close()

    def test_error_handling(self, tmp_path) -> None:
        """测试错误处理."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        # 测试无效的 SQL
        with pytest.raises(DatabaseError):
            db.execute("INVALID SQL QUERY", commit=True)

        # 测试不存在的表
        with pytest.raises(DatabaseError):
            db.fetch_all("SELECT * FROM nonexistent_table")

        db.close()


class TestPoolConfig:
    """测试连接池配置."""

    def test_default_config(self) -> None:
        """测试默认配置."""
        config = PoolConfig()

        assert config.max_size == 10
        assert config.min_size == 2
        assert config.timeout == pytest.approx(30.0)
        assert config.recycle_timeout == pytest.approx(3600.0)
        assert config.check_connection_on_borrow is True

    def test_custom_config(self) -> None:
        """测试自定义配置."""
        config = PoolConfig(
            max_size=20,
            min_size=5,
            timeout=60.0,
            recycle_timeout=7200.0,
            check_connection_on_borrow=False,
        )

        assert config.max_size == 20
        assert config.min_size == 5
        assert config.timeout == pytest.approx(60.0)
        assert config.recycle_timeout == pytest.approx(7200.0)
        assert config.check_connection_on_borrow is False


class TestConnectionWrapper:
    """测试连接包装器."""

    def test_wrapper_creation(self, tmp_path) -> None:
        """测试连接包装器创建."""
        db_path = tmp_path / "test.db"
        conn = sqlite3.connect(str(db_path))

        wrapper = ConnectionWrapper(conn=conn)

        assert wrapper.conn == conn
        assert isinstance(wrapper.created_at, datetime)
        assert isinstance(wrapper.last_used, datetime)
        assert wrapper.borrow_count == 0

        # 检查时间戳是当前的 (允许 1 秒误差)
        now = datetime.now(timezone.utc)
        assert abs((wrapper.created_at - now).total_seconds()) < 1
        assert abs((wrapper.last_used - now).total_seconds()) < 1

        conn.close()

    def test_wrapper_with_custom_values(self, tmp_path) -> None:
        """测试带自定义值的连接包装器."""
        db_path = tmp_path / "test.db"
        conn = sqlite3.connect(str(db_path))

        custom_time = datetime(2024, 1, 1, 12, 0, 0, tzinfo=timezone.utc)
        wrapper = ConnectionWrapper(conn=conn, created_at=custom_time, last_used=custom_time, borrow_count=5)

        assert wrapper.conn == conn
        assert wrapper.created_at == custom_time
        assert wrapper.last_used == custom_time
        assert wrapper.borrow_count == 5

        conn.close()


class TestDatabaseConnectionPoolEdgeCases:
    """测试连接池边界情况."""

    def test_pool_recycle_timeout(self, tmp_path) -> None:
        """测试连接回收超时机制."""
        # 设置非常短的回收超时
        config = PoolConfig(min_size=1, max_size=2, recycle_timeout=0.1)
        pool = DatabaseConnectionPool(str(tmp_path / "test.db"), config)

        # 获取并归还连接
        with pool.get_connection() as conn:
            conn.execute("SELECT 1")

        # 等待连接超时
        time.sleep(0.2)

        # 再次获取连接,应该触发回收
        with pool.get_connection() as conn:
            conn.execute("SELECT 1")

        stats = pool.get_stats()
        assert stats["total_connections"] >= 1

        pool.close()

    def test_pool_shutdown_rejects_new_connections(self, tmp_path) -> None:
        """测试连接池关闭后拒绝新连接."""
        config = PoolConfig(min_size=1, max_size=2)
        pool = DatabaseConnectionPool(str(tmp_path / "test.db"), config)

        # 关闭连接池
        pool.close()

        # 尝试获取连接应该抛出异常
        with pytest.raises(DatabaseError, match="连接池已关闭"), pool.get_connection():
            pass

    def test_pool_borrow_count_tracking(self, tmp_path) -> None:
        """测试连接借用计数跟踪."""
        config = PoolConfig(min_size=1, max_size=2)
        pool = DatabaseConnectionPool(str(tmp_path / "test.db"), config)

        # 多次获取和归还连接
        for _ in range(5):
            with pool.get_connection() as conn:
                conn.execute("SELECT 1")

        # 由于连接复用,借用次数应该增加
        stats = pool.get_stats()
        assert stats["in_use_connections"] == 0  # 所有连接已归还

        pool.close()

    def test_pool_get_stats_comprehensive(self, tmp_path) -> None:
        """测试连接池统计信息完整性."""
        db_path = tmp_path / "test.db"
        config = PoolConfig(min_size=2, max_size=5)
        pool = DatabaseConnectionPool(str(db_path), config)

        stats = pool.get_stats()

        assert "total_connections" in stats
        assert "available_connections" in stats
        assert "in_use_connections" in stats
        assert "max_size" in stats
        assert "min_size" in stats
        assert "db_path" in stats
        assert stats["db_path"] == str(db_path)
        assert stats["max_size"] == 5
        assert stats["min_size"] == 2

        # 获取连接时的统计
        with pool.get_connection() as conn:
            conn.execute("SELECT 1")
            stats_in_use = pool.get_stats()
            assert stats_in_use["in_use_connections"] >= 1
            assert stats_in_use["available_connections"] < stats["available_connections"]

        pool.close()


class TestDatabaseManagerEdgeCases:
    """测试数据库管理器边界情况."""

    def test_execute_without_params(self, tmp_path) -> None:
        """测试执行不带参数的查询."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.execute("CREATE TABLE test (id INTEGER)", commit=True)

        # 不带参数执行
        result = db.execute("INSERT INTO test VALUES (1)", commit=True)
        assert result == 1

        # 查询不带参数
        rows = db.execute("SELECT * FROM test", fetch=True)
        assert isinstance(rows, list)
        assert len(rows) == 1

        db.close()

    def test_execute_with_dict_params(self, tmp_path) -> None:
        """测试执行带字典参数的查询."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.execute("CREATE TABLE test (id INTEGER, name TEXT)", commit=True)

        # 使用字典参数
        result = db.execute("INSERT INTO test VALUES (:id, :name)", {"id": 1, "name": "test"}, commit=True)
        assert result == 1

        row = db.fetch_one("SELECT * FROM test WHERE id = :id", {"id": 1})
        assert row is not None
        assert row["name"] == "test"

        db.close()

    def test_insert_returns_lastrowid(self, tmp_path) -> None:
        """测试插入操作返回最后插入的 ID."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.execute("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)", commit=True)

        # 第一次插入
        id1 = db.insert("test", {"name": "first"})
        assert id1 == 1

        # 第二次插入
        id2 = db.insert("test", {"name": "second"})
        assert id2 == 2

        db.close()

    def test_update_zero_rows(self, tmp_path) -> None:
        """测试更新操作影响 0 行."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.execute("CREATE TABLE test (id INTEGER, name TEXT)", commit=True)

        # 更新不存在的记录
        affected = db.update("test", {"name": "new"}, "id = ?", (999,))
        assert affected == 0

        db.close()

    def test_delete_zero_rows(self, tmp_path) -> None:
        """测试删除操作影响 0 行."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.execute("CREATE TABLE test (id INTEGER, name TEXT)", commit=True)

        # 删除不存在的记录
        deleted = db.delete("test", "id = ?", (999,))
        assert deleted == 0

        db.close()

    def test_insert_many_empty_list(self, tmp_path) -> None:
        """测试批量插入空列表."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.execute("CREATE TABLE test (id INTEGER)", commit=True)

        # 插入空列表
        count = db.insert_many("test", [])
        assert count == 0

        db.close()

    def test_fetch_one_no_results(self, tmp_path) -> None:
        """测试 fetch_one 没有结果时返回 None."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.execute("CREATE TABLE test (id INTEGER)", commit=True)

        result = db.fetch_one("SELECT * FROM test")
        assert result is None

        db.close()

    def test_transactional_decorator(self, tmp_path) -> None:
        """测试事务装饰器."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.execute("CREATE TABLE test (id INTEGER, name TEXT)", commit=True)

        # 定义一个使用事务装饰器的函数
        @db.transactional
        def insert_two_records(name1, name2) -> bool:
            db.execute("INSERT INTO test VALUES (1, ?)", (name1,))
            db.execute("INSERT INTO test VALUES (2, ?)", (name2,))
            return True

        # 成功执行
        result = insert_two_records("Alice", "Bob")
        assert result is True

        # 验证两条记录都已提交
        rows = db.fetch_all("SELECT * FROM test ORDER BY id")
        assert len(rows) == 2
        assert rows[0]["name"] == "Alice"
        assert rows[1]["name"] == "Bob"

        db.close()

    def test_transactional_decorator_rollback(self, tmp_path) -> None:
        """测试事务装饰器失败回滚."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.execute("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT UNIQUE)", commit=True)

        # 插入第一条记录
        db.insert("test", {"name": "Alice"})

        # 定义会失败的函数 - 直接使用 connection 执行多个操作
        @db.transactional
        def insert_with_failure(conn) -> bool:
            # 在同一个事务中执行两个操作
            conn.execute("INSERT INTO test VALUES (2, ?)", ("Bob",))
            # 尝试插入重复的唯一键,这会失败
            conn.execute("INSERT INTO test VALUES (3, ?)", ("Alice",))
            return True

        # 应该抛出 TransactionError
        with pytest.raises(TransactionError):
            insert_with_failure()

        # 验证 Bob 没有被插入(回滚)
        rows = db.fetch_all("SELECT * FROM test")
        # 由于事务回滚,Bob 应该不存在
        names = [row["name"] for row in rows]
        assert "Bob" not in names
        assert len(rows) == 1
        assert rows[0]["name"] == "Alice"

        db.close()

    def test_row_factory_setter_and_getter(self, tmp_path) -> None:
        """测试行工厂的设置器和获取器."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        # 测试 getter
        factory = db.row_factory
        assert factory == db.dict_row_factory

        # 测试 setter
        def custom_factory(cursor) -> Callable[[Any], dict]:
            def row_factory(row: tuple) -> dict:
                return {desc[0]: row[i] for i, desc in enumerate(cursor.description)}

            return row_factory

        db.row_factory = custom_factory
        assert db.row_factory == custom_factory

        db.close()

    def test_table_exists_nonexistent(self, tmp_path) -> None:
        """测试表不存在的情况."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        exists = db.table_exists("nonexistent_table")
        assert exists is False

        db.close()

    def test_get_table_info_comprehensive(self, tmp_path) -> None:
        """测试获取表结构信息的完整性."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.execute(
            """
            CREATE TABLE products (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT NOT NULL,
                price REAL DEFAULT 0.0,
                quantity INTEGER DEFAULT 0,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        """,
            commit=True,
        )

        info = db.get_table_info("products")

        assert len(info) == 5

        # 检查每个字段的详细信息
        columns = {col["name"]: col for col in info}

        assert "id" in columns
        assert columns["id"]["type"] == "INTEGER"
        assert columns["id"]["pk"] == 1

        assert "name" in columns
        assert columns["name"]["type"] == "TEXT"
        assert columns["name"]["notnull"] == 1

        assert "price" in columns
        assert columns["price"]["type"] == "REAL"

        assert "quantity" in columns
        assert columns["quantity"]["type"] == "INTEGER"

        assert "created_at" in columns
        assert columns["created_at"]["type"] == "TIMESTAMP"

        db.close()

    def test_get_stats_with_tables(self, tmp_path) -> None:
        """测试带表的数据库统计信息."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        # 初始状态
        stats = db.get_stats()
        assert stats["table_count"] == 0

        # 创建多个表
        db.execute("CREATE TABLE table1 (id INTEGER)", commit=True)
        db.execute("CREATE TABLE table2 (id INTEGER)", commit=True)
        db.execute("CREATE TABLE table3 (id INTEGER)", commit=True)

        # 检查统计
        stats = db.get_stats()
        assert stats["table_count"] == 3

        db.close()


class TestDatabaseErrorHandling:
    """测试数据库错误处理."""

    def test_invalid_sql_query(self, tmp_path) -> None:
        """测试无效 SQL 查询的错误处理."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        with pytest.raises(DatabaseError) as exc_info:
            db.execute("INVALID SQL SYNTAX HERE", commit=True)

        assert "执行查询失败" in str(exc_info.value)

        db.close()

    def test_insert_into_nonexistent_table(self, tmp_path) -> None:
        """测试向不存在的表插入数据的错误处理."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        with pytest.raises(DatabaseError, match="插入记录失败"):
            db.insert("nonexistent_table", {"id": 1, "name": "test"})

        db.close()

    def test_update_nonexistent_table(self, tmp_path) -> None:
        """测试更新不存在的表的错误处理."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        with pytest.raises(DatabaseError, match="更新记录失败"):
            db.update("nonexistent_table", {"name": "new"}, "id = ?", (1,))

        db.close()

    def test_delete_from_nonexistent_table(self, tmp_path) -> None:
        """测试从不存在的表删除数据的错误处理."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        with pytest.raises(DatabaseError, match="删除记录失败"):
            db.delete("nonexistent_table", "id = ?", (1,))

        db.close()

    def test_fetch_from_nonexistent_table(self, tmp_path) -> None:
        """测试从不存在的表查询数据的错误处理."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        with pytest.raises(DatabaseError):
            db.fetch_all("SELECT * FROM nonexistent_table")

        db.close()

    def test_constraint_violation(self, tmp_path) -> None:
        """测试约束违反的错误处理."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.execute("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT NOT NULL UNIQUE)", commit=True)

        # 插入第一条记录
        db.insert("test", {"name": "Alice"})

        # 尝试插入重复的唯一值
        with pytest.raises(DatabaseError, match="插入记录失败"):
            db.insert("test", {"name": "Alice"})

        # 尝试插入 NULL 到 NOT NULL 字段
        with pytest.raises(DatabaseError, match="插入记录失败"):
            db.insert("test", {"name": None})

        db.close()


class TestDatabaseManagerContextManager:
    """测试数据库管理器的上下文管理器."""

    def test_context_manager_success(self, tmp_path) -> None:
        """测试上下文管理器成功使用."""
        db_path = tmp_path / "test.db"

        with DatabaseManager(str(db_path)) as db:
            db.execute("CREATE TABLE test (id INTEGER)", commit=True)
            db.insert("test", {"id": 1})

            result = db.fetch_one("SELECT * FROM test")
            assert result is not None
            assert result["id"] == 1

        # 退出上下文后数据库已关闭

    def test_context_manager_cleanup(self, tmp_path) -> None:
        """测试上下文管理器清理资源."""
        db_path = tmp_path / "test.db"

        db = DatabaseManager(str(db_path))
        assert db.pool._shutdown is False

        with db:
            # 在上下文中
            assert db.pool._shutdown is False

        # 退出后应该已关闭
        assert db.pool._shutdown is True


class TestDatabasePerformanceEnhanced:
    """增强的性能测试."""

    def test_large_batch_insert(self, tmp_path) -> None:
        """测试大批量插入性能."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.execute("CREATE TABLE items (id INTEGER PRIMARY KEY, name TEXT, value INTEGER)", commit=True)

        # 准备大量数据
        data = [{"name": f"item_{i}", "value": i * 10} for i in range(5000)]

        start_time = time.time()
        count = db.insert_many("items", data)
        elapsed = time.time() - start_time

        assert count == 5000

        # 验证性能(5000 条插入应该在合理时间内完成)
        assert elapsed < 5.0, f"大批量插入耗时过长:{elapsed:.2f}秒"

        # 验证数据
        total = db.fetch_one("SELECT COUNT(*) as count FROM items")
        assert total is not None
        assert total["count"] == 5000

        db.close()

    def test_complex_queries(self, tmp_path) -> None:
        """测试复杂查询性能."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        # 创建测试表
        db.execute(
            """
            CREATE TABLE orders (
                id INTEGER PRIMARY KEY,
                customer_id INTEGER,
                product TEXT,
                amount REAL,
                status TEXT
            )
        """,
            commit=True,
        )

        # 插入测试数据
        data = [
            {
                "customer_id": i % 10,
                "product": f"Product_{i % 5}",
                "amount": float(i * 100),
                "status": "completed" if i % 2 == 0 else "pending",
            }
            for i in range(100)
        ]
        db.insert_many("orders", data)

        # 测试复杂查询:分组聚合
        query = """
            SELECT customer_id, status, COUNT(*) as count, SUM(amount) as total
            FROM orders
            GROUP BY customer_id, status
            HAVING COUNT(*) > 1
            ORDER BY total DESC
        """

        start_time = time.time()
        results = db.fetch_all(query)
        elapsed = time.time() - start_time

        assert len(results) > 0
        assert elapsed < 1.0, f"复杂查询耗时过长:{elapsed:.2f}秒"

        # 验证结果结构
        for row in results:
            assert "customer_id" in row
            assert "status" in row
            assert "count" in row
            assert "total" in row

        db.close()


class TestSQLInjectionPrevention:
    """测试 SQL 注入防护."""

    def test_update_invalid_table_name(self, tmp_path) -> None:
        """测试 update 方法拒绝无效表名."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        # 尝试使用包含特殊字符的表名
        with pytest.raises(DatabaseError, match="无效的表名"):
            db.update("users; DROP TABLE users;--", {"name": "test"}, "id = ?", (1,))

        # 尝试使用包含空格的表名
        with pytest.raises(DatabaseError, match="无效的表名"):
            db.update("users admin", {"name": "test"}, "id = ?", (1,))

        # 尝试使用包含引号的表名
        with pytest.raises(DatabaseError, match="无效的表名"):
            db.update('users"DROP', {"name": "test"}, "id = ?", (1,))

        db.close()

    def test_delete_invalid_table_name(self, tmp_path) -> None:
        """测试 delete 方法拒绝无效表名."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        # 尝试使用包含特殊字符的表名
        with pytest.raises(DatabaseError, match="无效的表名"):
            db.delete("users; DROP TABLE users;--", "id = ?", (1,))

        # 尝试使用包含空格的表名
        with pytest.raises(DatabaseError, match="无效的表名"):
            db.delete("users admin", "id = ?", (1,))

        # 尝试使用包含引号的表名
        with pytest.raises(DatabaseError, match="无效的表名"):
            db.delete('users"DROP', "id = ?", (1,))

        db.close()

    def test_get_table_info_invalid_table_name(self, tmp_path) -> None:
        """测试 get_table_info 方法拒绝无效表名."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        # 尝试使用包含特殊字符的表名
        with pytest.raises(DatabaseError, match="无效的表名"):
            db.get_table_info("users; DROP TABLE users;--")

        # 尝试使用包含空格的表名
        with pytest.raises(DatabaseError, match="无效的表名"):
            db.get_table_info("users admin")

        # 尝试使用包含引号的表名
        with pytest.raises(DatabaseError, match="无效的表名"):
            db.get_table_info('users"DROP')

        db.close()

    def test_update_valid_table_name_with_special_chars_in_data(self, tmp_path) -> None:
        """测试 update 方法正确处理包含特殊字符的数据."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.execute("CREATE TABLE users (id INTEGER, name TEXT)", commit=True)
        db.insert("users", {"id": 1, "name": "Alice"})

        # 更新时使用包含 SQL 特殊字符的值(应该被参数化处理)
        affected = db.update("users", {"name": "Robert'); DROP TABLE students;--"}, "id = ?", (1,))
        assert affected == 1

        # 验证更新成功且没有 SQL 注入
        user = db.fetch_one("SELECT * FROM users WHERE id = ?", (1,))
        assert user is not None
        assert user["name"] == "Robert'); DROP TABLE students;--"
        assert db.table_exists("students") is False  # 表没有被删除

        db.close()


class TestConnectionPoolFailureScenarios:
    """测试连接池故障场景."""

    def test_connection_initialization_failure(self, tmp_path, mocker) -> None:
        """测试连接初始化失败的情况."""
        from unittest.mock import patch

        db_path = tmp_path / "test.db"

        # Mock _create_connection 使其失败
        with patch.object(DatabaseConnectionPool, "_create_connection", side_effect=sqlite3.Error("连接失败")):
            pool = DatabaseConnectionPool(str(db_path), PoolConfig(min_size=2, max_size=5))

            # 由于连接创建失败,连接池应该 gracefully 处理
            stats = pool.get_stats()
            # 可能没有连接或只有部分连接
            assert stats["total_connections"] <= 2

            pool.close()

    def test_connection_validity_check_failure(self, tmp_path, mocker) -> None:
        """测试连接有效性检查失败的情况."""
        db_path = tmp_path / "test.db"
        config = PoolConfig(min_size=1, max_size=2)
        pool = DatabaseConnectionPool(str(db_path), config)

        # 获取一个连接并归还,然后验证回收机制
        with pool.get_connection() as conn:
            conn.execute("SELECT 1")

        # 验证连接池正常工作
        stats = pool.get_stats()
        assert stats["available_connections"] >= 1

        pool.close()

    def test_close_connection_failure(self, tmp_path) -> None:
        """测试关闭连接失败的情况(日志记录但不抛出异常)."""
        db_path = tmp_path / "test.db"
        config = PoolConfig(min_size=1, max_size=2)
        pool = DatabaseConnectionPool(str(db_path), config)

        # 获取一个连接
        with pool.get_connection() as conn:
            conn.execute("SELECT 1")

        # 正常关闭连接池(内部会处理异常)
        pool.close()

        # 连接池应该已关闭
        assert pool._shutdown is True

    def test_create_new_connection_failure(self, tmp_path, mocker) -> None:
        """测试创建新连接失败的情况."""
        db_path = tmp_path / "test.db"
        config = PoolConfig(min_size=0, max_size=2, timeout=0.1)
        pool = DatabaseConnectionPool(str(db_path), config)

        # 清空连接池
        pool.close()

        # 重新初始化但让创建连接失败
        mocker.patch.object(pool, "_create_connection", side_effect=sqlite3.Error("创建失败"))
        with pytest.raises(DatabaseError), pool.get_connection() as conn:
            conn.execute("SELECT 1")

    def test_acquire_connection_timeout(self, tmp_path) -> None:
        """测试获取连接超时的情况."""
        db_path = tmp_path / "test.db"
        config = PoolConfig(min_size=0, max_size=1, timeout=0.3)
        pool = DatabaseConnectionPool(str(db_path), config)

        # 占用唯一的连接
        with pool.get_connection() as conn:
            conn.execute("SELECT 1")

            # 在另一个线程中尝试获取连接会超时
            exception_caught = []

            def try_get_connection() -> None:
                try:
                    with pool.get_connection():
                        pass
                except DatabaseError as e:
                    exception_caught.append(e)

            thread = threading.Thread(target=try_get_connection)
            thread.start()
            thread.join(timeout=2.0)

            # 验证线程已完成(不会一直等待)
            assert not thread.is_alive(), "线程仍在运行,可能存在死锁"
            # 验证捕获到了 DatabaseError
            assert len(exception_caught) > 0, "Expected DatabaseError to be raised in thread"
            assert "超时" in str(exception_caught[0]), f"错误消息应包含'超时': {exception_caught[0]}"

        pool.close()

    def test_connection_recycle_on_return(self, tmp_path) -> None:
        """测试归还连接时触发回收的逻辑."""
        db_path = tmp_path / "test.db"
        config = PoolConfig(min_size=1, max_size=2, recycle_timeout=0.01)
        pool = DatabaseConnectionPool(str(db_path), config)

        # 获取并归还连接
        with pool.get_connection() as conn:
            conn.execute("SELECT 1")

        # 等待连接超时
        time.sleep(0.02)

        # 再次获取连接,应该触发回收和替换
        with pool.get_connection() as conn:
            conn.execute("SELECT 1")

        # 统计信息应该显示连接被替换
        stats = pool.get_stats()
        assert stats["total_connections"] >= 1

        pool.close()

    def test_pool_shutdown_during_borrow(self, tmp_path) -> None:
        """测试在借用连接时关闭连接池."""
        db_path = tmp_path / "test.db"
        config = PoolConfig(min_size=1, max_size=2)
        pool = DatabaseConnectionPool(str(db_path), config)

        # 获取连接但不立即归还
        with pool.get_connection() as conn:
            conn.execute("SELECT 1")
            # 在连接使用时关闭连接池
            pool.close()

        # 连接池已关闭
        assert pool._shutdown is True


class TestDatabaseManagerAdditionalCoverage:
    """测试数据库管理器额外覆盖率."""

    def test_execute_fetch_returns_non_list(self, tmp_path) -> None:
        """测试 execute 方法 fetch 返回非列表的情况(理论上不会发生)."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.execute("CREATE TABLE test (id INTEGER)", commit=True)

        # 正常情况 fetch 应该返回列表
        result = db.execute("SELECT * FROM test", fetch=True)
        assert isinstance(result, list)

        db.close()

    def test_fetch_one_type_check_error(self, tmp_path) -> None:
        """测试 fetch_one 类型检查错误."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        # Mock execute 返回非列表
        import unittest.mock

        with unittest.mock.patch.object(db, "execute", return_value=42), pytest.raises(
            DatabaseError,
            match="fetch_one 期望列表类型的返回值",
        ):
            db.fetch_one("SELECT * FROM test")

        db.close()

    def test_fetch_all_type_check_error(self, tmp_path) -> None:
        """测试 fetch_all 类型检查错误."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        # Mock execute 返回非列表
        import unittest.mock

        with unittest.mock.patch.object(db, "execute", return_value="not a list"), pytest.raises(
            DatabaseError,
            match="fetch_all 期望列表类型的返回值",
        ):
            db.fetch_all("SELECT * FROM test")

        db.close()

    def test_insert_many_exception_handling(self, tmp_path) -> None:
        """测试 insert_many 异常处理."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.execute("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT UNIQUE)", commit=True)

        # 插入会导致唯一性冲突的数据
        data_list = [{"id": 1, "name": "Alice"}, {"id": 1, "name": "Bob"}]  # 重复的 id

        # 先插入一条记录
        db.insert("test", {"id": 1, "name": "Alice"})

        # 批量插入会失败
        with pytest.raises(DatabaseError, match="批量插入失败"):
            db.insert_many("test", data_list)

        db.close()

    def test_delete_without_where_params(self, tmp_path) -> None:
        """测试 delete 方法不使用 where_params 的情况."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.execute("CREATE TABLE test (id INTEGER, name TEXT)", commit=True)
        db.insert("test", {"id": 1, "name": "Alice"})

        # 不使用参数的删除(注意:实际使用中应该避免这种情况以防止 SQL 注入)
        # 这里仅用于测试代码覆盖率
        deleted = db.delete("test", "id = 1")
        assert deleted == 1

        remaining = db.fetch_all("SELECT * FROM test")
        assert len(remaining) == 0

        db.close()

    def test_update_with_valid_table_name(self, tmp_path) -> None:
        """测试 update 方法使用有效表名."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.execute("CREATE TABLE users_test (id INTEGER, name TEXT)", commit=True)
        db.insert("users_test", {"id": 1, "name": "Alice"})

        # 使用有效的表名(只包含字母和下划线)
        affected = db.update("users_test", {"name": "Bob"}, "id = ?", (1,))
        assert affected == 1

        user = db.fetch_one("SELECT * FROM users_test WHERE id = ?", (1,))
        assert user is not None
        assert user["name"] == "Bob"

        db.close()

    def test_delete_with_valid_table_name(self, tmp_path) -> None:
        """测试 delete 方法使用有效表名."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.execute("CREATE TABLE users_test (id INTEGER, name TEXT)", commit=True)
        db.insert("users_test", {"id": 1, "name": "Alice"})

        # 使用有效的表名(只包含字母和下划线)
        deleted = db.delete("users_test", "id = ?", (1,))
        assert deleted == 1

        remaining = db.fetch_all("SELECT * FROM users_test")
        assert len(remaining) == 0

        db.close()

    def test_get_table_info_with_valid_name(self, tmp_path) -> None:
        """测试 get_table_info 方法使用有效表名."""
        db_path = tmp_path / "test.db"
        db = DatabaseManager(str(db_path))

        db.execute("CREATE TABLE products_test (id INTEGER, name TEXT)", commit=True)

        info = db.get_table_info("products_test")
        assert len(info) == 2
        column_names = [col["name"] for col in info]
        assert "id" in column_names
        assert "name" in column_names

        db.close()


# 需要导入 sqlite3 用于 ConnectionWrapper 测试
