"""
Manager 模块测试
测试 DatabaseManager 完整功能
"""
import pytest
from unittest.mock import Mock, MagicMock, patch

from xll_kit.database import DatabaseManager
from xll_kit.database.exceptions import (
    DatabaseError,
    TableNotFoundError,
    ValidationError,
)


class TestDatabaseManager:
    """Database Manager 测试"""

    @patch("sqlalchemy.MetaData.reflect")
    @patch('xll_kit.database.manager.create_engine')
    def test_init(self, mock_create_engine, mock_reflect):
        """测试初始化"""
        mock_engine = MagicMock()
        mock_engine.dialect.name = 'mysql'
        mock_create_engine.return_value = mock_engine

        manager = DatabaseManager(
            'mysql://user:pass@localhost/test',
            pool_size=5,
            max_overflow=10
        )

        assert manager.engine == mock_engine
        mock_create_engine.assert_called_once()

    @patch("sqlalchemy.MetaData.reflect")
    @patch('xll_kit.database.manager.create_engine')
    def test_init_failure(self, mock_create_engine, mock_reflect):
        """测试初始化失败"""
        mock_create_engine.side_effect = Exception("Connection failed")

        with pytest.raises(DatabaseError, match="Initialization failed"):
            DatabaseManager('mysql://invalid')

    @patch("sqlalchemy.MetaData.reflect")
    @patch('xll_kit.database.manager.create_engine')
    def test_get_table(self, mock_create_engine, mock_reflect):
        """测试获取表"""
        mock_engine = MagicMock()
        mock_engine.dialect.name = 'mysql'
        mock_create_engine.return_value = mock_engine

        manager = DatabaseManager('mysql://test')

        # 模拟表存在
        manager.metadata.tables = {'users': MagicMock()}
        table = manager.get_table('users')

        assert table is not None

    @patch("sqlalchemy.MetaData.reflect")
    @patch('xll_kit.database.manager.create_engine')
    def test_get_table_not_found(self, mock_create_engine, mock_reflect):
        """测试获取不存在的表"""
        mock_engine = MagicMock()
        mock_engine.dialect.name = 'mysql'
        mock_create_engine.return_value = mock_engine

        manager = DatabaseManager('mysql://test')
        manager.metadata.tables = {}

        with pytest.raises(TableNotFoundError):
            manager.get_table('non_existent_table')

    @patch("sqlalchemy.MetaData.reflect")
    @patch('xll_kit.database.manager.create_engine')
    def test_filter_data(self, mock_create_engine, mock_reflect, mock_users_table):
        """测试过滤数据"""
        mock_engine = MagicMock()
        mock_engine.dialect.name = 'mysql'
        mock_create_engine.return_value = mock_engine

        manager = DatabaseManager('mysql://test')

        data = {
            'name': 'Alice',
            'email': 'alice@example.com',
            'age': 25,
            'invalid_field': 'should be removed'
        }

        filtered = manager._filter_data(mock_users_table, data)

        assert 'name' in filtered
        assert 'email' in filtered
        assert 'age' in filtered
        assert 'invalid_field' not in filtered

    @patch("sqlalchemy.MetaData.reflect")
    @patch('xll_kit.database.manager.create_engine')
    def test_validate_data_empty(self, mock_create_engine, mock_reflect):
        """测试验证空数据"""
        mock_engine = MagicMock()
        mock_engine.dialect.name = 'mysql'
        mock_create_engine.return_value = mock_engine

        manager = DatabaseManager('mysql://test')

        with pytest.raises(ValidationError, match="data cannot be empty"):
            manager._validate_data({})

    @patch("sqlalchemy.MetaData.reflect")
    @patch('xll_kit.database.manager.create_engine')
    def test_insert(self, mock_create_engine, mock_reflect, mock_users_table, sample_user):
        """测试插入单条数据"""
        mock_engine = MagicMock()
        mock_engine.dialect.name = 'mysql'
        mock_conn = MagicMock()
        mock_result = MagicMock()
        mock_result.rowcount = 1

        mock_conn.execute.return_value = mock_result
        # 正确配置上下文管理器
        mock_conn.__enter__ = Mock(return_value=mock_conn)
        mock_conn.__exit__ = Mock(return_value=False)
        mock_engine.connect.return_value = mock_conn
        mock_create_engine.return_value = mock_engine

        manager = DatabaseManager('mysql://test')
        manager.metadata.tables = {'users': mock_users_table}
        manager._table_cache = {'users': mock_users_table}

        affected = manager.insert('users', sample_user)

        assert affected == 1
        mock_conn.execute.assert_called_once()

    @patch("sqlalchemy.MetaData.reflect")
    @patch('xll_kit.database.manager.create_engine')
    def test_insert_many(self, mock_create_engine, mock_reflect, mock_users_table, sample_users):
        """测试批量插入"""
        mock_engine = MagicMock()
        mock_engine.dialect.name = 'mysql'
        mock_conn = MagicMock()
        mock_result = MagicMock()
        mock_result.rowcount = len(sample_users)

        mock_conn.execute.return_value = mock_result
        # 正确配置上下文管理器
        mock_conn.__enter__ = Mock(return_value=mock_conn)
        mock_conn.__exit__ = Mock(return_value=False)
        mock_engine.connect.return_value = mock_conn
        mock_create_engine.return_value = mock_engine

        manager = DatabaseManager('mysql://test')
        manager.metadata.tables = {'users': mock_users_table}
        manager._table_cache = {'users': mock_users_table}

        affected = manager.insert_many('users', sample_users, batch_size=100)

        assert affected == len(sample_users)

    @patch("sqlalchemy.MetaData.reflect")
    @patch('xll_kit.database.manager.create_engine')
    def test_upsert(self, mock_create_engine, mock_reflect, mock_users_table, sample_user):
        """测试 Upsert"""
        mock_engine = MagicMock()
        mock_engine.dialect.name = 'mysql'
        mock_conn = MagicMock()
        mock_result = MagicMock()
        mock_result.rowcount = 1

        mock_conn.execute.return_value = mock_result
        # 正确配置上下文管理器
        mock_conn.__enter__ = Mock(return_value=mock_conn)
        mock_conn.__exit__ = Mock(return_value=False)
        mock_engine.connect.return_value = mock_conn
        mock_create_engine.return_value = mock_engine

        manager = DatabaseManager('mysql://test')
        manager.metadata.tables = {'users': mock_users_table}
        manager._table_cache = {'users': mock_users_table}

        affected = manager.upsert('users', sample_user, conflict_target=['email'])

        assert affected == 1

    @patch("sqlalchemy.MetaData.reflect")
    @patch('xll_kit.database.manager.create_engine')
    def test_update(self, mock_create_engine, mock_reflect, mock_users_table):
        """测试更新"""
        mock_engine = MagicMock()
        mock_engine.dialect.name = 'mysql'
        mock_conn = MagicMock()
        mock_result = MagicMock()
        mock_result.rowcount = 1

        mock_conn.execute.return_value = mock_result
        # 正确配置上下文管理器
        mock_conn.__enter__ = Mock(return_value=mock_conn)
        mock_conn.__exit__ = Mock(return_value=False)
        mock_engine.connect.return_value = mock_conn
        mock_create_engine.return_value = mock_engine

        manager = DatabaseManager('mysql://test')
        manager.metadata.tables = {'users': mock_users_table}
        manager._table_cache = {'users': mock_users_table}

        data = {'name': 'Updated Name'}
        where = {'id': 1}

        affected = manager.update('users', data, where)

        assert affected == 1

    @patch("sqlalchemy.MetaData.reflect")
    @patch('xll_kit.database.manager.create_engine')
    def test_delete(self, mock_create_engine, mock_reflect, mock_users_table):
        """测试删除"""
        mock_engine = MagicMock()
        mock_engine.dialect.name = 'mysql'
        mock_conn = MagicMock()
        mock_result = MagicMock()
        mock_result.rowcount = 1

        mock_conn.execute.return_value = mock_result
        # 正确配置上下文管理器
        mock_conn.__enter__ = Mock(return_value=mock_conn)
        mock_conn.__exit__ = Mock(return_value=False)
        mock_engine.connect.return_value = mock_conn
        mock_create_engine.return_value = mock_engine

        manager = DatabaseManager('mysql://test')
        manager.metadata.tables = {'users': mock_users_table}
        manager._table_cache = {'users': mock_users_table}

        affected = manager.delete('users', {'id': 1})

        assert affected == 1

    @patch("sqlalchemy.MetaData.reflect")
    @patch('xll_kit.database.manager.create_engine')
    def test_query(self, mock_create_engine, mock_reflect):
        """测试查询"""
        mock_engine = MagicMock()
        mock_engine.dialect.name = 'mysql'
        mock_conn = MagicMock()
        mock_result = MagicMock()

        mock_result.fetchall.return_value = [
            (1, 'Alice', 'alice@example.com'),
            (2, 'Bob', 'bob@example.com')
        ]
        mock_result.keys.return_value = ['id', 'name', 'email']

        mock_conn.execute.return_value = mock_result
        # 正确配置上下文管理器
        mock_conn.__enter__ = Mock(return_value=mock_conn)
        mock_conn.__exit__ = Mock(return_value=False)
        mock_engine.connect.return_value = mock_conn
        mock_create_engine.return_value = mock_engine

        manager = DatabaseManager('mysql://test')
        results = manager.query("SELECT * FROM users")

        assert len(results) == 2
        assert results[0]['name'] == 'Alice'

    @patch("sqlalchemy.MetaData.reflect")
    @patch('xll_kit.database.manager.create_engine')
    def test_query_one(self, mock_create_engine, mock_reflect):
        """测试查询单条"""
        mock_engine = MagicMock()
        mock_engine.dialect.name = 'mysql'
        mock_conn = MagicMock()
        mock_result = MagicMock()

        mock_result.fetchall.return_value = [(1, 'Alice')]
        mock_result.keys.return_value = ['id', 'name']

        mock_conn.execute.return_value = mock_result
        # 正确配置上下文管理器
        mock_conn.__enter__ = Mock(return_value=mock_conn)
        mock_conn.__exit__ = Mock(return_value=False)
        mock_engine.connect.return_value = mock_conn
        mock_create_engine.return_value = mock_engine

        manager = DatabaseManager('mysql://test')
        result = manager.query_one("SELECT * FROM users WHERE id = 1")

        assert result is not None
        assert result['name'] == 'Alice'

    @patch("sqlalchemy.MetaData.reflect")
    @patch('xll_kit.database.manager.create_engine')
    def test_context_manager(self, mock_create_engine, mock_reflect):
        """测试上下文管理器"""
        mock_engine = MagicMock()
        mock_engine.dialect.name = 'mysql'
        mock_create_engine.return_value = mock_engine

        with DatabaseManager('mysql://test') as manager:
            assert manager is not None

        mock_engine.dispose.assert_called_once()

    @patch("sqlalchemy.MetaData.reflect")
    @patch('xll_kit.database.manager.create_engine')
    def test_close(self, mock_create_engine, mock_reflect):
        """测试关闭连接"""
        mock_engine = MagicMock()
        mock_engine.dialect.name = 'mysql'
        mock_create_engine.return_value = mock_engine

        manager = DatabaseManager('mysql://test')
        manager.close()

        mock_engine.dispose.assert_called_once()