"""Integration tests for SQLCipher with real encryption (not mocked).

These tests verify the actual SQLCipher encryption flow, including:
- Registration followed by immediate login (the critical failing case)
- Thread-safe sessions for metrics
- Password verification (wrong password should fail)
- Multi-user isolation
"""

import pytest
from loguru import logger


def _sqlcipher_available():
    """Check if SQLCipher is available."""
    try:
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        get_sqlcipher_module()
        return True
    except ImportError:
        return False


@pytest.fixture
def isolated_db_manager(tmp_path, monkeypatch):
    """Create DatabaseManager with isolated temp directory.

    Forces QueuePool (not StaticPool) so concurrent-write integration
    tests exercise the same pooling path used in production. Under the
    default TESTING=1 StaticPool all threads would share a single
    underlying SQLite connection, which can't serialize concurrent
    writes at the driver level.
    """
    monkeypatch.setenv("LDR_DB_KDF_ITERATIONS", "1000")  # Fast for testing
    monkeypatch.setenv("LDR_ALLOW_UNENCRYPTED", "false")  # Require encryption

    from local_deep_research.database.encrypted_db import DatabaseManager
    from sqlalchemy.pool import QueuePool

    manager = DatabaseManager()
    manager._use_static_pool = False
    manager._pool_class = QueuePool
    # Point to temp directory
    manager.data_dir = tmp_path / "encrypted_databases"
    manager.data_dir.mkdir(parents=True, exist_ok=True)

    yield manager

    # Cleanup
    for username in list(manager.connections.keys()):
        manager.close_user_database(username)


@pytest.mark.skipif(
    not _sqlcipher_available(), reason="SQLCipher not installed"
)
class TestSQLCipherIntegration:
    """Real SQLCipher tests (not mocked)."""

    def test_register_then_immediate_login(self, isolated_db_manager):
        """
        Critical test: Registration followed by immediate login.
        This is the exact flow that failed in UI tests.
        """
        username = "testuser"
        password = "SecurePassword123!"

        # Register (create database)
        engine = isolated_db_manager.create_user_database(username, password)
        assert engine is not None

        # Close the connection (simulates end of registration request)
        isolated_db_manager.close_user_database(username)

        # Login (open existing database) - THIS IS WHERE IT FAILED
        engine = isolated_db_manager.open_user_database(username, password)
        assert engine is not None, "Failed to open database after registration"

        # Verify database is functional
        from sqlalchemy import text

        with engine.connect() as conn:
            result = conn.execute(text("SELECT 1"))
            assert result.fetchone()[0] == 1

    def test_thread_safe_session_after_create(self, isolated_db_manager):
        """Test thread-safe session works after database creation."""
        username = "testuser"
        password = "SecurePassword123!"

        # Create database
        isolated_db_manager.create_user_database(username, password)

        # Create thread-safe session (used for metrics)
        session = isolated_db_manager.create_thread_safe_session_for_metrics(
            username, password
        )
        assert session is not None

        # Verify session works
        from sqlalchemy import text

        result = session.execute(text("SELECT 1"))
        assert result.fetchone()[0] == 1
        session.close()

    def test_wrong_password_fails(self, isolated_db_manager):
        """Verify that wrong password actually fails (encryption works)."""
        username = "testuser"
        password = "CorrectPassword123!"

        # Create database
        isolated_db_manager.create_user_database(username, password)
        isolated_db_manager.close_user_database(username)

        # Attempt to open with wrong password
        engine = isolated_db_manager.open_user_database(
            username, "WrongPassword!"
        )
        assert engine is None, "Should fail with wrong password"

    def test_multiple_users_isolated(self, isolated_db_manager):
        """Test that multiple users have separate encrypted databases."""
        users = [
            ("user1", "Password1!"),
            ("user2", "Password2!"),
            ("user3", "Password3!"),
        ]

        # Create all users
        for username, password in users:
            isolated_db_manager.create_user_database(username, password)

        # Close all connections
        for username, _ in users:
            isolated_db_manager.close_user_database(username)

        # Reopen each user - verify each password only works for its database
        for username, password in users:
            # Correct password works
            engine = isolated_db_manager.open_user_database(username, password)
            assert engine is not None, f"Failed to open {username}'s database"
            isolated_db_manager.close_user_database(username)

            # Wrong passwords fail
            for other_user, other_pass in users:
                if other_user != username:
                    engine = isolated_db_manager.open_user_database(
                        username, other_pass
                    )
                    assert engine is None, (
                        f"{other_user}'s password should not work for {username}"
                    )

    def test_reopen_multiple_times(self, isolated_db_manager):
        """Test database can be opened and closed multiple times."""
        username = "testuser"
        password = "SecurePassword123!"

        # Create database
        isolated_db_manager.create_user_database(username, password)

        # Open and close multiple times
        for i in range(5):
            isolated_db_manager.close_user_database(username)
            engine = isolated_db_manager.open_user_database(username, password)
            assert engine is not None, (
                f"Failed to reopen database on iteration {i}"
            )

            # Verify it works each time
            from sqlalchemy import text

            with engine.connect() as conn:
                result = conn.execute(text("SELECT 1"))
                assert result.fetchone()[0] == 1

    def test_database_persists_data(self, isolated_db_manager):
        """Test that data persists across close/open cycles."""
        username = "testuser"
        password = "SecurePassword123!"

        # Create database
        isolated_db_manager.create_user_database(username, password)

        # Insert data
        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]
        with engine.connect() as conn:
            conn.execute(
                text(
                    "CREATE TABLE IF NOT EXISTS test_data (id INTEGER, value TEXT)"
                )
            )
            conn.execute(text("INSERT INTO test_data VALUES (1, 'test_value')"))
            conn.commit()

        # Close and reopen
        isolated_db_manager.close_user_database(username)
        engine = isolated_db_manager.open_user_database(username, password)
        assert engine is not None

        # Verify data persists
        with engine.connect() as conn:
            result = conn.execute(
                text("SELECT value FROM test_data WHERE id = 1")
            )
            row = result.fetchone()
            assert row is not None
            assert row[0] == "test_value"

    def test_thread_safe_session_reuse(self, isolated_db_manager):
        """Test that thread-safe sessions can be created multiple times."""
        username = "testuser"
        password = "SecurePassword123!"

        # Create database
        isolated_db_manager.create_user_database(username, password)

        # Create multiple thread-safe sessions
        from sqlalchemy import text

        for i in range(3):
            session = (
                isolated_db_manager.create_thread_safe_session_for_metrics(
                    username, password
                )
            )
            assert session is not None, (
                f"Failed to create session on iteration {i}"
            )

            # Verify session works
            result = session.execute(text("SELECT 1"))
            assert result.fetchone()[0] == 1
            session.close()

    def test_concurrent_access_same_user(self, isolated_db_manager):
        """Test concurrent access to the same user's database."""
        import threading

        username = "testuser"
        password = "SecurePassword123!"

        # Create database
        isolated_db_manager.create_user_database(username, password)

        errors = []
        success_count = [0]

        def worker():
            try:
                session = (
                    isolated_db_manager.create_thread_safe_session_for_metrics(
                        username, password
                    )
                )
                from sqlalchemy import text

                result = session.execute(text("SELECT 1"))
                assert result.fetchone()[0] == 1
                session.close()
                success_count[0] += 1
            except Exception as e:
                errors.append(str(e))

        # Run multiple threads
        threads = [threading.Thread(target=worker) for _ in range(5)]
        for t in threads:
            t.start()
        for t in threads:
            t.join(timeout=10)

        assert len(errors) == 0, f"Errors during concurrent access: {errors}"
        assert success_count[0] == 5, (
            f"Only {success_count[0]}/5 threads succeeded"
        )

    def test_change_password_works(self, isolated_db_manager):
        """Test changing password and reopening with new password."""
        username = "testuser"
        old_password = "OldPassword123!"
        new_password = "NewPassword456!"

        # Create database with old password
        isolated_db_manager.create_user_database(username, old_password)

        # Insert some data to verify it persists after password change
        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]
        with engine.connect() as conn:
            conn.execute(
                text(
                    "CREATE TABLE IF NOT EXISTS test_data (id INTEGER, value TEXT)"
                )
            )
            conn.execute(
                text("INSERT INTO test_data VALUES (1, 'secret_data')")
            )
            conn.commit()

        # Change password
        result = isolated_db_manager.change_password(
            username, old_password, new_password
        )
        assert result is True, "Password change should succeed"

        # Old password should no longer work
        engine = isolated_db_manager.open_user_database(username, old_password)
        assert engine is None, "Old password should not work after change"

        # New password should work
        engine = isolated_db_manager.open_user_database(username, new_password)
        assert engine is not None, "New password should work after change"

        # Verify data persists after password change
        with engine.connect() as conn:
            result = conn.execute(
                text("SELECT value FROM test_data WHERE id = 1")
            )
            row = result.fetchone()
            assert row is not None
            assert row[0] == "secret_data"

    def test_check_database_integrity(self, isolated_db_manager):
        """Test check_database_integrity() returns True for valid DB."""
        username = "testuser"
        password = "SecurePassword123!"

        # Create database
        isolated_db_manager.create_user_database(username, password)

        # Check integrity - should pass for fresh database
        result = isolated_db_manager.check_database_integrity(username)
        assert result is True, "Integrity check should pass for valid database"

    def test_special_characters_in_password(self, isolated_db_manager):
        """Test passwords with quotes, backslashes, and special chars."""
        username = "testuser"
        # Password with quotes, backslashes, and special characters
        password = "P@ss'w\"ord\\123!#$%^&*()"

        # Create database with special character password
        engine = isolated_db_manager.create_user_database(username, password)
        assert engine is not None

        # Close and reopen
        isolated_db_manager.close_user_database(username)

        # Should be able to reopen with same password
        engine = isolated_db_manager.open_user_database(username, password)
        assert engine is not None, "Should open with special character password"

        # Verify database is functional
        from sqlalchemy import text

        with engine.connect() as conn:
            result = conn.execute(text("SELECT 1"))
            assert result.fetchone()[0] == 1

    def test_unicode_in_password(self, isolated_db_manager):
        """Test passwords with unicode characters."""
        username = "testuser"
        # Password with unicode characters
        password = "密码Пароль🔐Senha123!"

        # Create database
        engine = isolated_db_manager.create_user_database(username, password)
        assert engine is not None

        # Close and reopen
        isolated_db_manager.close_user_database(username)

        # Should be able to reopen with same unicode password
        engine = isolated_db_manager.open_user_database(username, password)
        assert engine is not None, "Should open with unicode password"

    def test_empty_password_rejected(self, isolated_db_manager):
        """Test that empty password raises ValueError."""
        username = "testuser"

        with pytest.raises(
            ValueError, match="password cannot be None or empty"
        ):
            isolated_db_manager.create_user_database(username, "")

    def test_none_password_rejected(self, isolated_db_manager):
        """Test that None password raises ValueError."""
        username = "testuser"

        with pytest.raises(
            ValueError, match="password cannot be None or empty"
        ):
            isolated_db_manager.create_user_database(username, None)

    def test_empty_password_rejected_on_open(self, isolated_db_manager):
        """Test that empty password raises ValueError on open."""
        username = "testuser"
        password = "SecurePassword123!"

        # Create database first
        isolated_db_manager.create_user_database(username, password)
        isolated_db_manager.close_user_database(username)

        # Attempt to open with empty password
        with pytest.raises(
            ValueError, match="password cannot be None or empty"
        ):
            isolated_db_manager.open_user_database(username, "")

    def test_corrupted_database_returns_none(self, isolated_db_manager):
        """Test opening corrupted DB file returns None gracefully."""
        username = "testuser"
        password = "SecurePassword123!"

        # Create database
        isolated_db_manager.create_user_database(username, password)
        isolated_db_manager.close_user_database(username)

        # Corrupt the database file by overwriting with garbage
        db_path = isolated_db_manager._get_user_db_path(username)
        with open(db_path, "wb") as f:
            f.write(b"THIS IS NOT A VALID SQLITE DATABASE FILE" * 100)

        # Attempt to open - should return None, not crash
        engine = isolated_db_manager.open_user_database(username, password)
        assert engine is None, "Should return None for corrupted database"

    def test_nonexistent_user_returns_none(self, isolated_db_manager):
        """Test opening nonexistent user database returns None."""
        engine = isolated_db_manager.open_user_database(
            "nonexistent_user", "password"
        )
        assert engine is None, "Should return None for nonexistent user"

    def test_duplicate_user_rejected(self, isolated_db_manager):
        """Test creating duplicate user raises ValueError."""
        username = "testuser"
        password = "SecurePassword123!"

        # Create first user
        isolated_db_manager.create_user_database(username, password)

        # Attempt to create same user again
        with pytest.raises(ValueError, match="Database already exists"):
            isolated_db_manager.create_user_database(
                username, "DifferentPassword!"
            )

    # =========================================================================
    # HIGH PRIORITY: Security Critical Tests
    # =========================================================================

    def test_cipher_integrity_check_detects_tampering(
        self, isolated_db_manager
    ):
        """Test that cipher_integrity_check detects file tampering."""
        username = "testuser"
        password = "SecurePassword123!"

        # Create database with some data
        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]
        with engine.connect() as conn:
            conn.execute(
                text("CREATE TABLE test_data (id INTEGER, value TEXT)")
            )
            conn.execute(text("INSERT INTO test_data VALUES (1, 'secret')"))
            conn.commit()

        isolated_db_manager.close_user_database(username)

        # Tamper with the database file (modify bytes in the middle)
        db_path = isolated_db_manager._get_user_db_path(username)
        with open(db_path, "r+b") as f:
            f.seek(1024)  # Skip header, modify data pages
            original = f.read(100)
            f.seek(1024)
            # Flip some bytes
            tampered = bytes([b ^ 0xFF for b in original])
            f.write(tampered)

        # Try to open tampered database - should fail or return None
        engine = isolated_db_manager.open_user_database(username, password)
        # Tampered database should either fail to open or fail integrity check
        if engine is not None:
            # If it opens, integrity check should fail
            integrity_ok = isolated_db_manager.check_database_integrity(
                username
            )
            # Tampering may or may not be detected depending on which pages
            # were modified - this is expected behavior
            assert isinstance(integrity_ok, bool)
            logger.info(
                f"Integrity check after tampering returned: {integrity_ok}"
            )

    def test_plaintext_header_not_present(self, isolated_db_manager):
        """Verify encrypted DB doesn't have SQLite plaintext header."""
        username = "testuser"
        password = "SecurePassword123!"

        # Create database
        isolated_db_manager.create_user_database(username, password)

        # Write some data to ensure pages are written
        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]
        with engine.connect() as conn:
            conn.execute(
                text("CREATE TABLE test_data (id INTEGER, value TEXT)")
            )
            conn.execute(text("INSERT INTO test_data VALUES (1, 'secret')"))
            conn.commit()

        isolated_db_manager.close_user_database(username)

        # Read the database file header
        db_path = isolated_db_manager._get_user_db_path(username)
        with open(db_path, "rb") as f:
            header = f.read(16)

        # SQLite magic header is "SQLite format 3\0"
        sqlite_magic = b"SQLite format 3\x00"
        assert header != sqlite_magic, (
            "Encrypted database should NOT have plaintext SQLite header"
        )

    def test_cipher_status_returns_active(self, isolated_db_manager):
        """Verify PRAGMA cipher_status returns expected value for encrypted DB."""
        username = "testuser"
        password = "SecurePassword123!"

        # Create and open database
        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]
        with engine.connect() as conn:
            # cipher_status returns encryption status info
            try:
                result = conn.execute(text("PRAGMA cipher_status"))
                rows = result.fetchall()
                # cipher_status should return rows indicating encryption is active
                # The exact format varies by SQLCipher version
                assert len(rows) > 0, "cipher_status should return status info"
            except Exception:
                # Some versions may not support cipher_status
                # In that case, verify encryption via other means
                result = conn.execute(text("PRAGMA cipher_version"))
                version = result.fetchone()
                assert version is not None, "Should have cipher_version"

    def test_cipher_settings_match_configuration(self, isolated_db_manager):
        """Verify PRAGMA cipher_settings returns configured values."""
        username = "testuser"
        password = "SecurePassword123!"

        # Create database
        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]
        with engine.connect() as conn:
            # Get cipher settings
            try:
                result = conn.execute(text("PRAGMA cipher_settings"))
                rows = result.fetchall()
                # Should have settings configured
                # Convert to dict for easier inspection
                settings = {}
                for row in rows:
                    if len(row) >= 2:
                        settings[row[0]] = row[1]

                # Verify some expected settings exist
                # (exact values depend on configuration)
                assert len(rows) > 0, "cipher_settings should return settings"
            except Exception:
                # Older SQLCipher may not have cipher_settings
                # Verify cipher is active via version check
                result = conn.execute(text("PRAGMA cipher_version"))
                version = result.fetchone()
                assert version is not None

    # =========================================================================
    # MEDIUM PRIORITY: Compatibility & Edge Cases
    # =========================================================================

    def test_wal_files_are_encrypted(self, isolated_db_manager, tmp_path):
        """Test that WAL files are also encrypted (no plaintext leakage)."""
        username = "testuser"
        password = "SecurePassword123!"

        # Create database
        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]

        # Enable WAL mode
        with engine.connect() as conn:
            conn.execute(text("PRAGMA journal_mode=WAL"))
            conn.commit()

        # Create table and insert data with recognizable pattern
        secret_data = "TOP_SECRET_DATA_12345"
        with engine.connect() as conn:
            conn.execute(text("CREATE TABLE secrets (id INTEGER, value TEXT)"))
            conn.execute(
                text(f"INSERT INTO secrets VALUES (1, '{secret_data}')")
            )
            conn.commit()

        # Check for WAL file
        from pathlib import Path

        db_path = isolated_db_manager._get_user_db_path(username)
        wal_path = Path(str(db_path) + "-wal")
        shm_path = Path(str(db_path) + "-shm")

        # WAL file may or may not exist depending on checkpoint status
        if wal_path.exists():
            with open(wal_path, "rb") as f:
                wal_content = f.read()

            # Secret data should NOT appear in plaintext in WAL
            assert secret_data.encode() not in wal_content, (
                "Secret data should not appear in plaintext in WAL file"
            )

        # Cleanup: close database and remove WAL/SHM files
        isolated_db_manager.close_user_database(username)
        for path in [wal_path, shm_path]:
            if path.exists():
                path.unlink()

    def test_invalid_page_size_rejected(self, tmp_path):
        """Verify non-power-of-2 page sizes are rejected."""
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "test_pagesize.db"

        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()

        # Set key first
        cursor.execute("PRAGMA key = 'test_password'")

        # Try to set invalid page size (not power of 2)
        # SQLCipher should either reject this or use default
        cursor.execute("PRAGMA cipher_page_size = 5000")  # Not power of 2

        # Try to create a table to force the page size to be applied
        try:
            cursor.execute("CREATE TABLE test (id INTEGER)")
            conn.commit()

            # Check actual page size
            cursor.execute("PRAGMA page_size")
            actual_size = cursor.fetchone()[0]

            # Should be a power of 2 (SQLCipher enforces this)
            assert actual_size & (actual_size - 1) == 0, (
                f"Page size {actual_size} is not a power of 2"
            )
        except Exception as e:
            # SQLCipher properly rejected invalid page size - this is expected
            error_msg = str(e).lower()
            assert "error" in error_msg or "logic" in error_msg, (
                f"Unexpected error message: {e}"
            )
        finally:
            conn.close()

    def test_page_size_mismatch_fails(self, tmp_path):
        """Create DB with one page size, attempt to open with different size."""
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "test_pagesize_mismatch.db"

        # Create database with 4096 page size
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'test_password'")
        cursor.execute("PRAGMA cipher_page_size = 4096")
        cursor.execute("CREATE TABLE test (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO test VALUES (1, 'data')")
        conn.commit()
        conn.close()

        # Try to open with different page size (1024)
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'test_password'")
        cursor.execute("PRAGMA cipher_page_size = 1024")

        # Should fail to read data due to page size mismatch
        try:
            cursor.execute("SELECT * FROM test")
            cursor.fetchall()  # Result unused - testing if query fails
            # If we get here, the database somehow worked (unexpected)
            # This might happen if SQLCipher auto-detects page size
        except Exception as e:
            # Expected: should fail with decryption error
            assert (
                "file is not a database" in str(e).lower()
                or "decrypt" in str(e).lower()
                or "corrupt" in str(e).lower()
                or "error" in str(e).lower()
            ), f"Unexpected error: {e}"
        finally:
            conn.close()

    def test_key_after_operation_fails(self, tmp_path):
        """Verify PRAGMA key after SELECT fails with proper error."""
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "test_key_timing.db"

        # Create encrypted database first
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'test_password'")
        cursor.execute("CREATE TABLE test (id INTEGER)")
        conn.commit()
        conn.close()

        # Open and try to set key AFTER performing an operation
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()

        # Perform operation first (without key)
        try:
            cursor.execute("SELECT * FROM sqlite_master")
        except Exception as e:
            if isinstance(e, AssertionError):
                raise

        # Now try to set key - should have no effect or fail
        cursor.execute("PRAGMA key = 'test_password'")

        # Try to read data - should still fail because key was set too late
        try:
            cursor.execute("SELECT * FROM test")
            # If this succeeds, SQLCipher might handle late key differently
            cursor.fetchall()  # Result unused - testing if query fails
        except Exception as e:
            # Expected: database should be inaccessible
            assert (
                "file is not a database" in str(e).lower()
                or "no such table" in str(e).lower()
                or "error" in str(e).lower()
            )
        finally:
            conn.close()

    def test_cipher_pragmas_after_operation_ignored(self, tmp_path):
        """Verify cipher pragmas set after first operation have no effect."""
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "test_pragma_timing.db"

        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()

        # Set key first
        cursor.execute("PRAGMA key = 'test_password'")

        # Create table (this triggers first operation)
        cursor.execute("CREATE TABLE test (id INTEGER)")
        conn.commit()

        # Get current KDF iterations
        cursor.execute("PRAGMA kdf_iter")
        cursor.fetchone()  # Result unused - verifying pragma works

        # Try to change KDF iterations AFTER first operation
        cursor.execute("PRAGMA kdf_iter = 1")

        # Check if it changed (it shouldn't for an already-initialized DB)
        cursor.execute("PRAGMA kdf_iter")
        cursor.fetchone()  # Result unused - testing pragma behavior

        # The KDF iter shouldn't change for already-opened database
        # (changes only affect new databases or rekey operations)
        conn.close()

    # =========================================================================
    # LOWER PRIORITY: Robustness Tests
    # =========================================================================

    def test_large_blob_storage(self, isolated_db_manager):
        """Store and retrieve large binary data (1MB+)."""
        username = "testuser"
        password = "SecurePassword123!"

        # Create database
        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text
        import os

        engine = isolated_db_manager.connections[username]

        # Create table for blob storage
        with engine.connect() as conn:
            conn.execute(text("CREATE TABLE blobs (id INTEGER, data BLOB)"))
            conn.commit()

        # Generate 1MB of random data
        large_data = os.urandom(1024 * 1024)  # 1MB

        # Store the blob
        with engine.connect() as conn:
            # Use parameterized query for blob
            conn.execute(
                text("INSERT INTO blobs VALUES (1, :data)"),
                {"data": large_data},
            )
            conn.commit()

        # Close and reopen to ensure persistence
        isolated_db_manager.close_user_database(username)
        engine = isolated_db_manager.open_user_database(username, password)
        assert engine is not None

        # Retrieve and verify
        with engine.connect() as conn:
            result = conn.execute(text("SELECT data FROM blobs WHERE id = 1"))
            row = result.fetchone()
            assert row is not None
            retrieved_data = row[0]
            assert retrieved_data == large_data, "Large blob data mismatch"

    def test_many_records_performance(self, isolated_db_manager):
        """Insert 10,000+ records, verify retrieval works."""
        username = "testuser"
        password = "SecurePassword123!"

        # Create database
        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]

        # Create table
        with engine.connect() as conn:
            conn.execute(
                text(
                    "CREATE TABLE records (id INTEGER PRIMARY KEY, value TEXT)"
                )
            )
            conn.commit()

        # Insert 10,000 records in batches
        record_count = 10000
        batch_size = 1000

        with engine.connect() as conn:
            for batch_start in range(0, record_count, batch_size):
                values = ", ".join(
                    f"({i}, 'value_{i}')"
                    for i in range(
                        batch_start, min(batch_start + batch_size, record_count)
                    )
                )
                conn.execute(
                    text(f"INSERT INTO records (id, value) VALUES {values}")
                )
            conn.commit()

        # Verify count
        with engine.connect() as conn:
            result = conn.execute(text("SELECT COUNT(*) FROM records"))
            count = result.fetchone()[0]
            assert count == record_count, (
                f"Expected {record_count}, got {count}"
            )

        # Verify random access
        with engine.connect() as conn:
            result = conn.execute(
                text("SELECT value FROM records WHERE id = 5000")
            )
            row = result.fetchone()
            assert row[0] == "value_5000"

        # Close and reopen
        isolated_db_manager.close_user_database(username)
        engine = isolated_db_manager.open_user_database(username, password)
        assert engine is not None

        # Verify data persists
        with engine.connect() as conn:
            result = conn.execute(text("SELECT COUNT(*) FROM records"))
            count = result.fetchone()[0]
            assert count == record_count

    def test_rapid_open_close_cycles(self, isolated_db_manager):
        """Open/close database 100 times in rapid succession."""
        username = "testuser"
        password = "SecurePassword123!"

        # Create database with some data
        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]
        with engine.connect() as conn:
            conn.execute(text("CREATE TABLE test (id INTEGER, value TEXT)"))
            conn.execute(text("INSERT INTO test VALUES (1, 'persistent')"))
            conn.commit()

        # Rapid open/close cycles
        cycles = 100
        for i in range(cycles):
            isolated_db_manager.close_user_database(username)
            engine = isolated_db_manager.open_user_database(username, password)
            assert engine is not None, f"Failed to open on cycle {i}"

            # Quick verification
            with engine.connect() as conn:
                result = conn.execute(
                    text("SELECT value FROM test WHERE id = 1")
                )
                row = result.fetchone()
                assert row[0] == "persistent", f"Data corrupted on cycle {i}"

    def test_file_is_not_database_error_plaintext(self, tmp_path):
        """Open regular SQLite DB with SQLCipher, verify meaningful error."""
        import sqlite3

        # Create a regular (unencrypted) SQLite database
        plain_db_path = tmp_path / "plain.db"
        conn = sqlite3.connect(str(plain_db_path))
        cursor = conn.cursor()
        cursor.execute("CREATE TABLE test (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO test VALUES (1, 'plaintext_data')")
        conn.commit()
        conn.close()

        # Try to open with SQLCipher and a password
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        conn = pysqlcipher3.connect(str(plain_db_path))
        cursor = conn.cursor()

        # Set a key (which will try to decrypt)
        cursor.execute("PRAGMA key = 'some_password'")

        # Try to read - should fail with meaningful error
        try:
            cursor.execute("SELECT * FROM test")
            cursor.fetchall()  # Result unused - testing if query fails
            # If we get here with data, something is wrong
            # (SQLCipher shouldn't be able to read encrypted as plaintext)
            assert False, (
                "Should not be able to read plaintext DB with password"
            )
        except Exception as e:
            error_msg = str(e).lower()
            # Should get "file is not a database" or similar
            assert (
                "not a database" in error_msg
                or "file is encrypted" in error_msg
                or "error" in error_msg
            ), f"Expected meaningful error, got: {e}"
        finally:
            conn.close()

    def test_key_with_null_bytes_handled(self, tmp_path):
        """Test password containing null bytes (potential truncation issue)."""
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "test_null_key.db"

        # Password with embedded null bytes
        # Note: This tests if SQLCipher properly handles or rejects such passwords
        password_with_null = "pass\x00word\x00end"

        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()

        try:
            # Try to set key with null bytes
            # Using raw key format to ensure bytes are passed correctly
            cursor.execute(f"PRAGMA key = '{password_with_null}'")
            cursor.execute("CREATE TABLE test (id INTEGER)")
            cursor.execute("INSERT INTO test VALUES (1)")
            conn.commit()
            conn.close()

            # Try to reopen with same password
            conn = pysqlcipher3.connect(str(db_path))
            cursor = conn.cursor()
            cursor.execute(f"PRAGMA key = '{password_with_null}'")
            cursor.execute("SELECT * FROM test")
            result = cursor.fetchall()
            assert len(result) == 1, "Should be able to read with same password"

        except Exception as e:
            # If SQLCipher rejects null bytes, that's also acceptable
            # as long as it handles gracefully
            error_msg = str(e).lower()
            assert (
                "error" in error_msg
                or "invalid" in error_msg
                or "not a database" in error_msg
                or "null" in error_msg
            ), f"Unexpected error: {e}"
        finally:
            conn.close()

    def test_database_file_appears_random(self, isolated_db_manager):
        """Statistical test that encrypted file has high entropy."""
        import math

        username = "testuser"
        password = "SecurePassword123!"

        # Create database with predictable data
        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]

        # Insert lots of predictable data (low entropy content)
        with engine.connect() as conn:
            conn.execute(
                text("CREATE TABLE entropy_test (id INTEGER, value TEXT)")
            )
            # Insert repetitive data
            for i in range(100):
                conn.execute(
                    text(
                        f"INSERT INTO entropy_test VALUES ({i}, 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA')"
                    )
                )
            conn.commit()

        isolated_db_manager.close_user_database(username)

        # Read the database file
        db_path = isolated_db_manager._get_user_db_path(username)
        with open(db_path, "rb") as f:
            data = f.read()

        # Calculate Shannon entropy
        def calculate_entropy(data):
            if not data:
                return 0
            byte_counts = [0] * 256
            for byte in data:
                byte_counts[byte] += 1

            entropy = 0
            data_len = len(data)
            for count in byte_counts:
                if count > 0:
                    probability = count / data_len
                    entropy -= probability * math.log2(probability)
            return entropy

        entropy = calculate_entropy(data)

        # Well-encrypted data should have high entropy (close to 8 bits)
        # Unencrypted SQLite with repetitive data would have much lower entropy
        # We expect at least 7.5 bits of entropy for encrypted data
        assert entropy > 7.0, (
            f"Encrypted database entropy {entropy:.2f} is too low, "
            "suggesting weak or no encryption"
        )

    def test_cipher_version_available(self, isolated_db_manager):
        """Verify SQLCipher version is available and valid."""
        username = "testuser"
        password = "SecurePassword123!"

        # Create database
        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]
        with engine.connect() as conn:
            result = conn.execute(text("PRAGMA cipher_version"))
            version = result.fetchone()
            assert version is not None, "cipher_version should return a value"
            assert version[0] is not None, "cipher_version should not be None"
            # Version should be a string like "4.5.1" or similar
            version_str = str(version[0])
            assert len(version_str) > 0, "cipher_version should not be empty"

    def test_quick_check_passes(self, isolated_db_manager):
        """Verify PRAGMA quick_check passes for valid encrypted database."""
        username = "testuser"
        password = "SecurePassword123!"

        # Create database with data
        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]
        with engine.connect() as conn:
            conn.execute(text("CREATE TABLE test (id INTEGER, value TEXT)"))
            conn.execute(text("INSERT INTO test VALUES (1, 'data')"))
            conn.commit()

            # Run quick_check
            result = conn.execute(text("PRAGMA quick_check"))
            check_result = result.fetchone()
            assert check_result is not None
            assert check_result[0] == "ok", (
                f"quick_check failed: {check_result[0]}"
            )

    # =========================================================================
    # ADDITIONAL TESTS: Based on SQLCipher API Documentation
    # =========================================================================

    def test_cipher_provider_available(self, isolated_db_manager):
        """Verify cipher_provider returns the crypto library name."""
        username = "testuser"
        password = "SecurePassword123!"

        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]
        with engine.connect() as conn:
            result = conn.execute(text("PRAGMA cipher_provider"))
            provider = result.fetchone()
            assert provider is not None, "cipher_provider should return a value"
            provider_name = str(provider[0]).lower()
            # Should be one of: openssl, libtomcrypt, commoncrypto, nss
            valid_providers = ["openssl", "libtomcrypt", "commoncrypto", "nss"]
            assert any(p in provider_name for p in valid_providers), (
                f"Unknown cipher provider: {provider[0]}"
            )

    def test_cipher_provider_version_available(self, isolated_db_manager):
        """Verify cipher_provider_version returns version info."""
        username = "testuser"
        password = "SecurePassword123!"

        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]
        with engine.connect() as conn:
            result = conn.execute(text("PRAGMA cipher_provider_version"))
            version = result.fetchone()
            assert version is not None, (
                "cipher_provider_version should return a value"
            )
            assert len(str(version[0])) > 0, (
                "Provider version should not be empty"
            )

    def test_cipher_kdf_algorithm_queryable(self, isolated_db_manager):
        """Verify cipher_kdf_algorithm returns the KDF algorithm."""
        username = "testuser"
        password = "SecurePassword123!"

        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]
        with engine.connect() as conn:
            result = conn.execute(text("PRAGMA cipher_kdf_algorithm"))
            kdf = result.fetchone()
            assert kdf is not None, "cipher_kdf_algorithm should return a value"
            kdf_name = str(kdf[0]).upper()
            # Should be PBKDF2_HMAC_SHA512, PBKDF2_HMAC_SHA256, or PBKDF2_HMAC_SHA1
            # DevSkim: ignore DS126858 - Testing valid SQLCipher KDF algorithms including legacy SHA1
            valid_kdfs = [
                "PBKDF2_HMAC_SHA512",
                "PBKDF2_HMAC_SHA256",
                "PBKDF2_HMAC_SHA1",  # DevSkim: ignore DS126858
            ]
            assert any(k in kdf_name for k in valid_kdfs), (
                f"Unknown KDF algorithm: {kdf[0]}"
            )

    def test_cipher_hmac_algorithm_queryable(self, isolated_db_manager):
        """Verify cipher_hmac_algorithm returns the HMAC algorithm."""
        username = "testuser"
        password = "SecurePassword123!"

        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]
        with engine.connect() as conn:
            result = conn.execute(text("PRAGMA cipher_hmac_algorithm"))
            hmac = result.fetchone()
            assert hmac is not None, (
                "cipher_hmac_algorithm should return a value"
            )
            hmac_name = str(hmac[0]).upper()
            # Should be HMAC_SHA512, HMAC_SHA256, or HMAC_SHA1
            # DevSkim: ignore DS126858 - Testing valid SQLCipher HMAC algorithms including legacy SHA1
            valid_hmacs = [
                "HMAC_SHA512",
                "HMAC_SHA256",
                "HMAC_SHA1",  # DevSkim: ignore DS126858
            ]
            assert any(h in hmac_name for h in valid_hmacs), (
                f"Unknown HMAC algorithm: {hmac[0]}"
            )

    def test_raw_hex_key_bypasses_pbkdf2(self, tmp_path):
        """Test using raw hex key (x'...') which bypasses PBKDF2."""
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "test_raw_key.db"

        # 256-bit raw key in hex (64 hex chars = 32 bytes)
        raw_key = (
            "2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99"
        )

        # Create database with raw key
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = \"x'{raw_key}'\"")
        cursor.execute("CREATE TABLE test (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO test VALUES (1, 'raw_key_data')")
        conn.commit()
        conn.close()

        # Reopen with same raw key
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = \"x'{raw_key}'\"")
        cursor.execute("SELECT value FROM test WHERE id = 1")
        result = cursor.fetchone()
        assert result[0] == "raw_key_data", "Should read data with raw key"
        conn.close()

        # Different raw key should fail
        # Generate a clearly wrong key (all zeros) - not a real secret
        wrong_key = "0" * 64  # 256-bit key of all zeros for testing
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = \"x'{wrong_key}'\"")
        try:
            cursor.execute("SELECT * FROM test")
            result = cursor.fetchall()
            assert False, "Should fail with wrong raw key"
        except Exception as e:
            assert (
                "not a database" in str(e).lower()
                or "file is encrypted" in str(e).lower()
            )
        finally:
            conn.close()

    def test_attach_database_with_different_key(self, tmp_path):
        """Test ATTACH DATABASE with a different encryption key."""
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()

        # Create main database
        main_db_path = tmp_path / "main.db"
        conn = pysqlcipher3.connect(str(main_db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'main_password'")
        cursor.execute("CREATE TABLE main_data (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO main_data VALUES (1, 'main_value')")
        conn.commit()
        conn.close()

        # Create secondary database with different key
        second_db_path = tmp_path / "second.db"
        conn = pysqlcipher3.connect(str(second_db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'second_password'")
        cursor.execute("CREATE TABLE second_data (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO second_data VALUES (2, 'second_value')")
        conn.commit()
        conn.close()

        # Open main and attach secondary with its key
        conn = pysqlcipher3.connect(str(main_db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'main_password'")

        # Attach with KEY clause
        cursor.execute(
            f"ATTACH DATABASE '{second_db_path}' AS second KEY 'second_password'"
        )

        # Query from both databases
        cursor.execute("SELECT value FROM main_data WHERE id = 1")
        main_result = cursor.fetchone()
        assert main_result[0] == "main_value"

        cursor.execute("SELECT value FROM second.second_data WHERE id = 2")
        second_result = cursor.fetchone()
        assert second_result[0] == "second_value"

        conn.close()

    def test_sqlcipher_export_to_encrypted(self, tmp_path):
        """Test sqlcipher_export to migrate between databases."""
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()

        # Create source database
        source_path = tmp_path / "source.db"
        conn = pysqlcipher3.connect(str(source_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'source_pass'")
        cursor.execute("CREATE TABLE data (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO data VALUES (1, 'exported_value')")
        cursor.execute("INSERT INTO data VALUES (2, 'another_value')")
        conn.commit()
        conn.close()

        # Export to new database with different key
        dest_path = tmp_path / "dest.db"
        conn = pysqlcipher3.connect(str(source_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'source_pass'")

        # Attach destination with different key
        cursor.execute(f"ATTACH DATABASE '{dest_path}' AS dest KEY 'dest_pass'")

        # Export data
        cursor.execute("SELECT sqlcipher_export('dest')")
        cursor.execute("DETACH DATABASE dest")
        conn.close()

        # Verify destination with its own key
        conn = pysqlcipher3.connect(str(dest_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'dest_pass'")
        cursor.execute("SELECT COUNT(*) FROM data")
        count = cursor.fetchone()[0]
        assert count == 2, f"Expected 2 rows, got {count}"

        cursor.execute("SELECT value FROM data WHERE id = 1")
        result = cursor.fetchone()
        assert result[0] == "exported_value"
        conn.close()

    def test_very_long_password(self, isolated_db_manager):
        """Test with an extremely long password (10000+ characters)."""
        username = "testuser"
        # Generate a 10000 character password
        password = "A" * 10000 + "SecureEnd!"

        # Create database with very long password
        engine = isolated_db_manager.create_user_database(username, password)
        assert engine is not None

        from sqlalchemy import text

        with engine.connect() as conn:
            conn.execute(text("CREATE TABLE test (id INTEGER, value TEXT)"))
            conn.execute(text("INSERT INTO test VALUES (1, 'long_pass_data')"))
            conn.commit()

        # Close and reopen
        isolated_db_manager.close_user_database(username)
        engine = isolated_db_manager.open_user_database(username, password)
        assert engine is not None, "Should open with very long password"

        with engine.connect() as conn:
            result = conn.execute(text("SELECT value FROM test WHERE id = 1"))
            assert result.fetchone()[0] == "long_pass_data"

    def test_vacuum_preserves_encryption(self, isolated_db_manager):
        """Test that VACUUM command preserves encryption."""
        username = "testuser"
        password = "SecurePassword123!"

        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]

        # Create data
        with engine.connect() as conn:
            conn.execute(text("CREATE TABLE test (id INTEGER, value TEXT)"))
            for i in range(100):
                conn.execute(
                    text(f"INSERT INTO test VALUES ({i}, 'value_{i}')")
                )
            conn.commit()

        # Delete some data
        with engine.connect() as conn:
            conn.execute(text("DELETE FROM test WHERE id > 50"))
            conn.commit()

        # Run VACUUM
        with engine.connect() as conn:
            conn.execute(text("VACUUM"))
            conn.commit()

        # Close and reopen to verify encryption still works
        isolated_db_manager.close_user_database(username)
        engine = isolated_db_manager.open_user_database(username, password)
        assert engine is not None, "Should open after VACUUM"

        # Verify data integrity
        with engine.connect() as conn:
            result = conn.execute(text("SELECT COUNT(*) FROM test"))
            count = result.fetchone()[0]
            assert count == 51, f"Expected 51 rows after delete, got {count}"

        # Verify file is still encrypted (no plaintext header)
        db_path = isolated_db_manager._get_user_db_path(username)
        with open(db_path, "rb") as f:
            header = f.read(16)
        assert header != b"SQLite format 3\x00", (
            "Should still be encrypted after VACUUM"
        )

    def test_transaction_rollback(self, isolated_db_manager):
        """Test that transaction rollback works correctly with encryption."""
        username = "testuser"
        password = "SecurePassword123!"

        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]

        # Create table
        with engine.connect() as conn:
            conn.execute(text("CREATE TABLE test (id INTEGER, value TEXT)"))
            conn.execute(text("INSERT INTO test VALUES (1, 'original')"))
            conn.commit()

        # Start transaction, make changes, then rollback
        with engine.connect() as conn:
            conn.execute(
                text("UPDATE test SET value = 'modified' WHERE id = 1")
            )
            conn.execute(text("INSERT INTO test VALUES (2, 'new_row')"))
            # Rollback instead of commit
            conn.rollback()

        # Verify rollback worked
        with engine.connect() as conn:
            result = conn.execute(text("SELECT value FROM test WHERE id = 1"))
            value = result.fetchone()[0]
            assert value == "original", f"Rollback failed: got {value}"

            result = conn.execute(text("SELECT COUNT(*) FROM test"))
            count = result.fetchone()[0]
            assert count == 1, f"Rollback failed: got {count} rows"

    def test_cipher_memory_security_queryable(self, isolated_db_manager):
        """Test that cipher_memory_security can be queried."""
        username = "testuser"
        password = "SecurePassword123!"

        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]
        with engine.connect() as conn:
            result = conn.execute(text("PRAGMA cipher_memory_security"))
            mem_security = result.fetchone()
            assert mem_security is not None, (
                "cipher_memory_security should return a value"
            )
            # Value is 0 or 1 (may be returned as string)
            value = int(mem_security[0])
            assert value in (0, 1), (
                f"cipher_memory_security should be 0 or 1, got {value}"
            )

    def test_kdf_iter_queryable(self, isolated_db_manager):
        """Test that kdf_iter returns the iteration count."""
        username = "testuser"
        password = "SecurePassword123!"

        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]
        with engine.connect() as conn:
            result = conn.execute(text("PRAGMA kdf_iter"))
            kdf_iter = result.fetchone()
            assert kdf_iter is not None, "kdf_iter should return a value"
            # Should be a positive integer (default is 256000 for SQLCipher 4)
            # May be returned as string
            value = int(kdf_iter[0])
            assert value > 0, f"kdf_iter should be positive, got {value}"

    def test_empty_database_encrypted(self, isolated_db_manager):
        """Test that even an empty database is encrypted."""
        username = "testuser"
        password = "SecurePassword123!"

        # Create empty database (no tables)
        isolated_db_manager.create_user_database(username, password)
        isolated_db_manager.close_user_database(username)

        # Verify file is encrypted (no plaintext header)
        db_path = isolated_db_manager._get_user_db_path(username)
        with open(db_path, "rb") as f:
            header = f.read(16)

        assert header != b"SQLite format 3\x00", (
            "Empty database should still be encrypted"
        )

        # Should still be openable with correct password
        engine = isolated_db_manager.open_user_database(username, password)
        assert engine is not None

    def test_concurrent_writes(self, isolated_db_manager):
        """Test concurrent write operations to encrypted database."""
        import threading

        username = "testuser"
        password = "SecurePassword123!"

        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]

        # Create table
        with engine.connect() as conn:
            conn.execute(
                text(
                    "CREATE TABLE concurrent_test (id INTEGER PRIMARY KEY, value TEXT)"
                )
            )
            conn.commit()

        errors = []
        write_count = [0]
        lock = threading.Lock()

        def writer(thread_id):
            try:
                session = (
                    isolated_db_manager.create_thread_safe_session_for_metrics(
                        username, password
                    )
                )
                for i in range(10):
                    unique_id = thread_id * 100 + i
                    session.execute(
                        text(
                            f"INSERT INTO concurrent_test VALUES ({unique_id}, 'thread_{thread_id}')"
                        )
                    )
                    session.commit()
                    with lock:
                        write_count[0] += 1
                session.close()
            except Exception as e:
                errors.append(f"Thread {thread_id}: {e}")

        # Run multiple writers
        threads = [threading.Thread(target=writer, args=(i,)) for i in range(5)]
        for t in threads:
            t.start()
        for t in threads:
            t.join(timeout=30)

        assert len(errors) == 0, f"Concurrent write errors: {errors}"
        assert write_count[0] == 50, f"Expected 50 writes, got {write_count[0]}"

        # Verify all data was written
        with engine.connect() as conn:
            result = conn.execute(text("SELECT COUNT(*) FROM concurrent_test"))
            count = result.fetchone()[0]
            assert count == 50, f"Expected 50 rows, got {count}"

    def test_reader_not_blocked_by_concurrent_writer(self, isolated_db_manager):
        """Under DEFERRED isolation + WAL mode, a reader must not block
        behind an active writer. Previously IMMEDIATE isolation forced
        every transaction — including pure SELECTs — to take a RESERVED
        write lock, serialising readers behind writers and causing the
        login-hang symptom. After the IMMEDIATE → DEFERRED change, a
        reader should complete while a writer is still in its
        transaction.
        """
        import threading
        import time

        username = "reader_writer_user"
        password = "SecurePassword123!"

        isolated_db_manager.create_user_database(username, password)
        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]

        with engine.connect() as conn:
            conn.execute(
                text("CREATE TABLE rw_test (id INTEGER PRIMARY KEY, v INT)")
            )
            conn.execute(text("INSERT INTO rw_test VALUES (1, 100)"))
            conn.commit()

        writer_started = threading.Event()
        writer_release = threading.Event()
        reader_done = threading.Event()
        reader_elapsed_ms = [None]
        errors = []

        def writer():
            try:
                sess = (
                    isolated_db_manager.create_thread_safe_session_for_metrics(
                        username, password
                    )
                )
                # Open a write transaction and hold it until we say so.
                sess.execute(text("UPDATE rw_test SET v = 200 WHERE id = 1"))
                writer_started.set()
                writer_release.wait(timeout=5)
                sess.commit()
                sess.close()
            except Exception as e:
                errors.append(f"writer: {e}")

        def reader():
            try:
                writer_started.wait(timeout=5)
                start = time.perf_counter()
                sess = (
                    isolated_db_manager.create_thread_safe_session_for_metrics(
                        username, password
                    )
                )
                result = sess.execute(
                    text("SELECT v FROM rw_test WHERE id = 1")
                ).fetchone()
                sess.rollback()
                sess.close()
                reader_elapsed_ms[0] = (time.perf_counter() - start) * 1000
                # Must see the pre-commit snapshot value (100), not 200.
                assert result[0] == 100, (
                    f"Reader saw uncommitted write (got {result[0]})"
                )
                reader_done.set()
            except Exception as e:
                errors.append(f"reader: {e}")
                reader_done.set()

        wt = threading.Thread(target=writer, daemon=True)
        rt = threading.Thread(target=reader, daemon=True)
        wt.start()
        rt.start()

        # The reader should finish quickly (well under 1s). If it takes
        # anywhere near busy_timeout (10s), we regressed into the
        # IMMEDIATE behaviour.
        assert reader_done.wait(timeout=3), (
            "Reader was blocked by writer — IMMEDIATE-style serialisation"
        )
        writer_release.set()
        wt.join(timeout=5)
        rt.join(timeout=5)

        assert not errors, f"errors: {errors}"
        assert reader_elapsed_ms[0] < 1000, (
            f"Reader took {reader_elapsed_ms[0]:.0f}ms — "
            "too long for a concurrent read under WAL + DEFERRED"
        )

    def test_cipher_use_hmac_queryable(self, isolated_db_manager):
        """Test that cipher_use_hmac returns expected value."""
        username = "testuser"
        password = "SecurePassword123!"

        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]
        with engine.connect() as conn:
            result = conn.execute(text("PRAGMA cipher_use_hmac"))
            use_hmac = result.fetchone()
            assert use_hmac is not None, "cipher_use_hmac should return a value"
            # Default should be 1 (enabled) for SQLCipher 4
            # May be returned as string
            value = int(use_hmac[0])
            assert value in (0, 1), (
                f"cipher_use_hmac should be 0 or 1, got {value}"
            )

    def test_cipher_page_size_queryable(self, isolated_db_manager):
        """Test that cipher_page_size returns the configured page size."""
        username = "testuser"
        password = "SecurePassword123!"

        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]
        with engine.connect() as conn:
            result = conn.execute(text("PRAGMA cipher_page_size"))
            page_size = result.fetchone()
            assert page_size is not None, (
                "cipher_page_size should return a value"
            )
            # Should be a power of 2 between 512 and 65536
            # May be returned as string
            size = int(page_size[0])
            assert 512 <= size <= 65536, f"Page size {size} out of range"
            assert size & (size - 1) == 0, f"Page size {size} not power of 2"

    def test_fts_with_encryption(self, isolated_db_manager):
        """Test Full-Text Search works with encryption."""
        username = "testuser"
        password = "SecurePassword123!"

        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]

        # Create FTS table
        with engine.connect() as conn:
            try:
                conn.execute(
                    text("CREATE VIRTUAL TABLE docs USING fts5(title, content)")
                )
                conn.execute(
                    text(
                        "INSERT INTO docs VALUES ('First Doc', 'This is searchable content')"
                    )
                )
                conn.execute(
                    text(
                        "INSERT INTO docs VALUES ('Second Doc', 'More text to search through')"
                    )
                )
                conn.commit()

                # Search using FTS
                result = conn.execute(
                    text("SELECT title FROM docs WHERE docs MATCH 'searchable'")
                )
                rows = result.fetchall()
                assert len(rows) == 1, f"Expected 1 match, got {len(rows)}"
                assert rows[0][0] == "First Doc"
            except Exception as e:
                # FTS5 may not be compiled in
                if "no such module" in str(e).lower():
                    pytest.skip("FTS5 not available")
                raise

    def test_savepoint_rollback(self, isolated_db_manager):
        """Test SAVEPOINT and ROLLBACK TO work with encryption."""
        username = "testuser"
        password = "SecurePassword123!"

        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]

        with engine.connect() as conn:
            conn.execute(text("CREATE TABLE test (id INTEGER, value TEXT)"))
            conn.execute(text("INSERT INTO test VALUES (1, 'original')"))

            # Create savepoint
            conn.execute(text("SAVEPOINT sp1"))
            conn.execute(text("INSERT INTO test VALUES (2, 'after_savepoint')"))

            # Verify insert
            result = conn.execute(text("SELECT COUNT(*) FROM test"))
            assert result.fetchone()[0] == 2

            # Rollback to savepoint
            conn.execute(text("ROLLBACK TO sp1"))

            # Verify rollback
            result = conn.execute(text("SELECT COUNT(*) FROM test"))
            assert result.fetchone()[0] == 1

            conn.commit()

    # =========================================================================
    # ADDITIONAL TESTS: Version Compatibility & Migration
    # =========================================================================

    def test_cipher_compatibility_mode(self, tmp_path):
        """Test PRAGMA cipher_compatibility to open SQLCipher 3.x formatted databases."""
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "compat_test.db"

        # Create database with SQLCipher 3 compatibility settings
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'test_password'")
        # Set compatibility mode to SQLCipher 3
        cursor.execute("PRAGMA cipher_compatibility = 3")
        cursor.execute("CREATE TABLE test (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO test VALUES (1, 'compat_data')")
        conn.commit()
        conn.close()

        # Reopen with same compatibility mode
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'test_password'")
        cursor.execute("PRAGMA cipher_compatibility = 3")
        cursor.execute("SELECT value FROM test WHERE id = 1")
        result = cursor.fetchone()
        assert result is not None, "Should read data with compatibility mode"
        assert result[0] == "compat_data"
        conn.close()

    def test_cipher_migrate_upgrades_database(self, tmp_path):
        """Test cipher_migrate to upgrade database from legacy settings."""
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "migrate_test.db"

        # Create database with legacy SQLCipher 3 settings
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'test_password'")
        cursor.execute("PRAGMA cipher_compatibility = 3")
        cursor.execute("CREATE TABLE legacy_data (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO legacy_data VALUES (1, 'old_format_data')")
        cursor.execute("INSERT INTO legacy_data VALUES (2, 'more_data')")
        conn.commit()
        conn.close()

        # Open with legacy settings and migrate to current format
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'test_password'")
        cursor.execute("PRAGMA cipher_compatibility = 3")

        # Verify data is accessible before migration
        cursor.execute("SELECT COUNT(*) FROM legacy_data")
        count_before = cursor.fetchone()[0]
        assert count_before == 2, "Should have 2 rows before migration"

        # Run cipher_migrate to upgrade to current format
        try:
            cursor.execute("PRAGMA cipher_migrate")
            result = cursor.fetchone()
            # cipher_migrate returns 0 on success, 1 on failure
            if result is not None:
                assert result[0] == 0, f"cipher_migrate failed: {result[0]}"
        except Exception as e:
            # Some versions may not support cipher_migrate
            if "not an error" not in str(e).lower():
                pytest.skip(f"cipher_migrate not supported: {e}")

        conn.close()

        # Now open without compatibility mode (using current defaults)
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'test_password'")
        # Don't set compatibility mode - use defaults

        try:
            cursor.execute("SELECT COUNT(*) FROM legacy_data")
            count_after = cursor.fetchone()[0]
            assert count_after == 2, "Data should persist after migration"
        except Exception as e:
            if isinstance(e, AssertionError):
                raise
            # If migration didn't work, database may still require compat mode
            # This is acceptable - we're testing the migration API exists
        finally:
            conn.close()

    def test_cipher_salt_retrievable(self, tmp_path):
        """Test that cipher_salt can retrieve the 16-byte salt."""
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "salt_test.db"

        # Create database
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'test_password'")
        cursor.execute("CREATE TABLE test (id INTEGER)")
        conn.commit()

        # Retrieve the salt
        cursor.execute("PRAGMA cipher_salt")
        salt = cursor.fetchone()
        conn.close()

        assert salt is not None, "cipher_salt should return a value"
        salt_value = str(salt[0])
        # Salt should be 32 hex characters (16 bytes)
        # Remove any 'x' prefix if present
        salt_hex = (
            salt_value.replace("x'", "").replace("'", "").replace("X'", "")
        )
        assert len(salt_hex) == 32, (
            f"Salt should be 32 hex chars (16 bytes), got {len(salt_hex)}: {salt_hex}"
        )
        # Verify it's valid hex
        try:
            int(salt_hex, 16)
        except ValueError:
            pytest.fail(f"Salt is not valid hex: {salt_hex}")

    def test_plaintext_header_with_salt(self, tmp_path):
        """Test plaintext header mode for iOS WAL compatibility."""
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "plaintext_header_test.db"

        # Create database with plaintext header
        # Note: plaintext_header_size must be set BEFORE the first operation
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'test_password'")
        # Set plaintext header size (first 32 bytes unencrypted for iOS)
        cursor.execute("PRAGMA cipher_plaintext_header_size = 32")
        cursor.execute("CREATE TABLE test (id INTEGER, secret TEXT)")
        cursor.execute("INSERT INTO test VALUES (1, 'sensitive_data')")
        conn.commit()

        # Get the salt for external management (must be retrieved while still open)
        cursor.execute("PRAGMA cipher_salt")
        salt = cursor.fetchone()
        salt_value = salt[0] if salt else None
        conn.close()

        # Verify file header contains SQLite magic bytes
        with open(db_path, "rb") as f:
            header = f.read(32)

        # First 16 bytes should be SQLite magic
        sqlite_magic = b"SQLite format 3\x00"
        assert header[:16] == sqlite_magic, (
            "Plaintext header should contain SQLite magic bytes"
        )

        # But the rest of the file should be encrypted
        with open(db_path, "rb") as f:
            f.seek(32)  # Skip plaintext header
            encrypted_portion = f.read(100)

        # Check that sensitive data is NOT in plaintext
        assert b"sensitive_data" not in encrypted_portion, (
            "Data should be encrypted after plaintext header"
        )

        # Reopen database - with plaintext header, salt is stored in
        # the unencrypted portion, so we just need the key and header size
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'test_password'")
        cursor.execute("PRAGMA cipher_plaintext_header_size = 32")

        # When using plaintext header, we need to set cipher_salt if the salt
        # was moved to the plaintext portion
        if salt_value:
            # Set the salt - format depends on how it was retrieved
            try:
                cursor.execute(f'PRAGMA cipher_salt = "{salt_value}"')
            except Exception as e:
                if isinstance(e, AssertionError):
                    raise
                # If this fails, the salt may already be readable from header

        try:
            cursor.execute("SELECT secret FROM test WHERE id = 1")
            result = cursor.fetchone()
            assert result[0] == "sensitive_data", "Should read encrypted data"
        except Exception as e:
            # Plaintext header mode has specific requirements that vary by version
            # If we can't reopen, verify the core feature works: header is plaintext
            # and data is encrypted
            error_lower = str(e).lower()
            if (
                "hmac check failed" in error_lower
                or "not a database" in error_lower
                or "logic error" in error_lower
                or "sql error" in error_lower
            ):
                # This is acceptable - we verified the header is plaintext
                # and data is encrypted. The reopen may require additional config
                # that varies by SQLCipher version
                pass
            else:
                raise
        finally:
            conn.close()

    # =========================================================================
    # ADDITIONAL TESTS: Security Edge Cases
    # =========================================================================

    def test_cipher_add_random_entropy(self, tmp_path):
        """Test adding external entropy to the random number generator."""
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "entropy_test.db"

        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'test_password'")

        # Add external entropy (hex string)
        # This mixes additional randomness into SQLCipher's RNG
        entropy = "DEADBEEF" * 8  # 32 bytes of entropy
        try:
            cursor.execute(f"PRAGMA cipher_add_random = \"x'{entropy}'\"")
        except Exception as e:
            if isinstance(e, AssertionError):
                raise
            # Some versions may not support this or have different syntax

        # Verify database still functions
        cursor.execute("CREATE TABLE test (id INTEGER)")
        cursor.execute("INSERT INTO test VALUES (1)")
        conn.commit()

        cursor.execute("SELECT * FROM test")
        result = cursor.fetchall()
        assert len(result) == 1, "Database should function after adding entropy"
        conn.close()

    def test_hmac_disabled_mode(self, tmp_path):
        """Test database with HMAC disabled (less secure, smaller pages)."""
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "no_hmac_test.db"

        # Create database with HMAC disabled
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'test_password'")
        cursor.execute("PRAGMA cipher_use_hmac = 0")
        cursor.execute("CREATE TABLE test (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO test VALUES (1, 'no_hmac_data')")
        conn.commit()
        conn.close()

        # Reopen with same settings
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'test_password'")
        cursor.execute("PRAGMA cipher_use_hmac = 0")
        cursor.execute("SELECT value FROM test WHERE id = 1")
        result = cursor.fetchone()
        assert result[0] == "no_hmac_data", (
            "Should read data with HMAC disabled"
        )

        # cipher_integrity_check requires HMAC - should fail or return error
        try:
            cursor.execute("PRAGMA cipher_integrity_check")
            cursor.fetchone()  # Result unused - testing integrity check behavior
            # With HMAC disabled, integrity check may fail or return empty
            # This is expected behavior
        except Exception as e:
            if isinstance(e, AssertionError):
                raise
            # Expected - integrity check not available without HMAC
        conn.close()

    def test_different_kdf_algorithms(self, tmp_path):
        """Test database creation with different KDF algorithms."""
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()

        # DevSkim: ignore DS126858 - Testing different KDF algorithms including legacy SHA1
        kdf_algorithms = [
            "PBKDF2_HMAC_SHA256",
            "PBKDF2_HMAC_SHA512",
            "PBKDF2_HMAC_SHA1",  # DevSkim: ignore DS126858
        ]

        for kdf in kdf_algorithms:
            db_path = tmp_path / f"kdf_{kdf}.db"

            try:
                conn = pysqlcipher3.connect(str(db_path))
                cursor = conn.cursor()
                cursor.execute("PRAGMA key = 'test_password'")
                cursor.execute(f"PRAGMA cipher_kdf_algorithm = {kdf}")
                cursor.execute("CREATE TABLE test (id INTEGER)")
                cursor.execute("INSERT INTO test VALUES (1)")
                conn.commit()
                conn.close()

                # Reopen with same KDF
                conn = pysqlcipher3.connect(str(db_path))
                cursor = conn.cursor()
                cursor.execute("PRAGMA key = 'test_password'")
                cursor.execute(f"PRAGMA cipher_kdf_algorithm = {kdf}")
                cursor.execute("SELECT * FROM test")
                result = cursor.fetchall()
                assert len(result) == 1, f"Should read data with KDF {kdf}"
                conn.close()
            except Exception as e:
                # Some KDF algorithms may not be supported
                if (
                    "not supported" in str(e).lower()
                    or "invalid" in str(e).lower()
                ):
                    continue
                raise

    def test_different_hmac_algorithms(self, tmp_path):
        """Test database creation with different HMAC algorithms."""
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()

        # DevSkim: ignore DS126858 - Testing different HMAC algorithms including legacy SHA1
        hmac_algorithms = [
            "HMAC_SHA256",
            "HMAC_SHA512",
            "HMAC_SHA1",  # DevSkim: ignore DS126858
        ]

        for hmac_algo in hmac_algorithms:
            db_path = tmp_path / f"hmac_{hmac_algo}.db"

            try:
                conn = pysqlcipher3.connect(str(db_path))
                cursor = conn.cursor()
                cursor.execute("PRAGMA key = 'test_password'")
                cursor.execute(f"PRAGMA cipher_hmac_algorithm = {hmac_algo}")
                cursor.execute("CREATE TABLE test (id INTEGER)")
                cursor.execute("INSERT INTO test VALUES (1)")
                conn.commit()
                conn.close()

                # Reopen with same HMAC algorithm
                conn = pysqlcipher3.connect(str(db_path))
                cursor = conn.cursor()
                cursor.execute("PRAGMA key = 'test_password'")
                cursor.execute(f"PRAGMA cipher_hmac_algorithm = {hmac_algo}")
                cursor.execute("SELECT * FROM test")
                result = cursor.fetchall()
                assert len(result) == 1, (
                    f"Should read data with HMAC {hmac_algo}"
                )
                conn.close()
            except Exception as e:
                # Some HMAC algorithms may not be supported
                if (
                    "not supported" in str(e).lower()
                    or "invalid" in str(e).lower()
                ):
                    continue
                raise

    # =========================================================================
    # ADDITIONAL TESTS: Database Operations
    # =========================================================================

    def test_delete_journal_mode(self, isolated_db_manager):
        """Test encryption works with DELETE journal mode (non-WAL)."""
        username = "testuser"
        password = "SecurePassword123!"

        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]

        # Set journal mode to DELETE
        with engine.connect() as conn:
            result = conn.execute(text("PRAGMA journal_mode = DELETE"))
            mode = result.fetchone()[0]
            assert mode.lower() == "delete", f"Expected DELETE mode, got {mode}"
            conn.commit()

        # Create and manipulate data
        with engine.connect() as conn:
            conn.execute(text("CREATE TABLE test (id INTEGER, value TEXT)"))
            conn.execute(
                text("INSERT INTO test VALUES (1, 'delete_mode_data')")
            )
            conn.commit()

        # Close and reopen
        isolated_db_manager.close_user_database(username)
        engine = isolated_db_manager.open_user_database(username, password)
        assert engine is not None

        # Verify data persists
        with engine.connect() as conn:
            result = conn.execute(text("SELECT value FROM test WHERE id = 1"))
            assert result.fetchone()[0] == "delete_mode_data"

    def test_memory_database_with_key(self, tmp_path):
        """Test :memory: database behavior with encryption key."""
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()

        # Create in-memory database with key
        conn = pysqlcipher3.connect(":memory:")
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'test_password'")

        # Memory databases may or may not support encryption
        # We're testing that the API doesn't crash
        try:
            cursor.execute("CREATE TABLE test (id INTEGER, value TEXT)")
            cursor.execute("INSERT INTO test VALUES (1, 'memory_data')")
            conn.commit()

            cursor.execute("SELECT value FROM test WHERE id = 1")
            result = cursor.fetchone()
            assert result[0] == "memory_data"

            # Check if encryption is actually applied
            cursor.execute("PRAGMA cipher_version")
            version = cursor.fetchone()
            # Memory DB should still report cipher version
            assert version is not None

        except Exception as e:
            # If memory DB doesn't support encryption, that's acceptable
            # We're documenting the behavior
            if "not a database" in str(e).lower():
                pytest.skip("Memory databases may not support encryption")
            raise
        finally:
            conn.close()

    def test_backup_preserves_encryption(self, tmp_path):
        """Test that file copy preserves encryption."""
        import shutil

        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        original_path = tmp_path / "original.db"
        backup_path = tmp_path / "backup.db"

        # Create original encrypted database
        conn = pysqlcipher3.connect(str(original_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'backup_password'")
        cursor.execute("CREATE TABLE secrets (id INTEGER, data TEXT)")
        cursor.execute("INSERT INTO secrets VALUES (1, 'confidential')")
        conn.commit()
        conn.close()

        # Copy file
        shutil.copy(original_path, backup_path)

        # Verify backup requires same password
        conn = pysqlcipher3.connect(str(backup_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'backup_password'")
        cursor.execute("SELECT data FROM secrets WHERE id = 1")
        result = cursor.fetchone()
        assert result[0] == "confidential", "Backup should have same data"
        conn.close()

        # Wrong password should fail on backup
        conn = pysqlcipher3.connect(str(backup_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'wrong_password'")
        try:
            cursor.execute("SELECT * FROM secrets")
            pytest.fail("Wrong password should fail on backup")
        except Exception as e:
            assert "not a database" in str(e).lower()
        finally:
            conn.close()

    def test_database_shrinks_after_vacuum(self, tmp_path):
        """Test that VACUUM reduces file size and encryption remains intact."""
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "vacuum_size_test.db"

        # Create database with lots of data
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'test_password'")
        cursor.execute("CREATE TABLE big_data (id INTEGER, data TEXT)")

        # Insert lots of data
        big_string = "X" * 1000
        for i in range(1000):
            cursor.execute(f"INSERT INTO big_data VALUES ({i}, '{big_string}')")
        conn.commit()

        # Delete most data
        cursor.execute("DELETE FROM big_data WHERE id > 100")
        conn.commit()

        # Size shouldn't change much without VACUUM
        size_after_delete = db_path.stat().st_size

        # Run VACUUM
        cursor.execute("VACUUM")
        conn.close()

        # Size should be significantly smaller
        size_after_vacuum = db_path.stat().st_size

        assert size_after_vacuum < size_after_delete, (
            f"VACUUM should reduce size: {size_after_delete} -> {size_after_vacuum}"
        )

        # Verify encryption still works
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'test_password'")
        cursor.execute("SELECT COUNT(*) FROM big_data")
        count = cursor.fetchone()[0]
        assert count == 101, f"Expected 101 rows, got {count}"
        conn.close()

        # Verify file is still encrypted
        with open(db_path, "rb") as f:
            header = f.read(16)
        assert header != b"SQLite format 3\x00", (
            "Should still be encrypted after VACUUM"
        )

    # =========================================================================
    # ADDITIONAL TESTS: Error Handling
    # =========================================================================

    def test_rekey_with_wrong_current_password(self, isolated_db_manager):
        """Test that rekey fails gracefully with wrong current password."""
        username = "testuser"
        password = "CorrectPassword123!"

        # Create database
        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]
        with engine.connect() as conn:
            conn.execute(text("CREATE TABLE test (id INTEGER)"))
            conn.execute(text("INSERT INTO test VALUES (1)"))
            conn.commit()

        isolated_db_manager.close_user_database(username)

        # Attempt to change password with wrong current password
        result = isolated_db_manager.change_password(
            username, "WrongPassword!", "NewPassword123!"
        )

        # Should fail
        assert result is False, "Rekey should fail with wrong current password"

        # Original password should still work
        engine = isolated_db_manager.open_user_database(username, password)
        assert engine is not None, "Original password should still work"

        with engine.connect() as conn:
            result = conn.execute(text("SELECT * FROM test"))
            assert len(result.fetchall()) == 1

    def test_attach_with_wrong_key_fails(self, tmp_path):
        """Test ATTACH DATABASE with wrong key provides meaningful error."""
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()

        # Create main database
        main_db_path = tmp_path / "main_attach.db"
        conn = pysqlcipher3.connect(str(main_db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'main_key'")
        cursor.execute("CREATE TABLE main_data (id INTEGER)")
        conn.commit()
        conn.close()

        # Create secondary database with different key
        second_db_path = tmp_path / "second_attach.db"
        conn = pysqlcipher3.connect(str(second_db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'second_key'")
        cursor.execute("CREATE TABLE second_data (id INTEGER)")
        conn.commit()
        conn.close()

        # Open main and try to attach secondary with WRONG key
        conn = pysqlcipher3.connect(str(main_db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'main_key'")

        # Attach with wrong key - may fail at ATTACH or at query time
        # depending on SQLCipher version
        try:
            cursor.execute(
                f"ATTACH DATABASE '{second_db_path}' AS second KEY 'wrong_key'"
            )
            # If ATTACH succeeded, try to access the attached database
            cursor.execute("SELECT * FROM second.second_data")
            cursor.fetchall()  # Result unused - testing if query fails
            pytest.fail(
                "Should fail when accessing database attached with wrong key"
            )
        except Exception as e:
            error_msg = str(e).lower()
            # Should get a meaningful error about the database
            assert (
                "not a database" in error_msg
                or "no such table" in error_msg
                or "unable to open" in error_msg
                or "encrypted" in error_msg
                or "file is encrypted" in error_msg
                or "error" in error_msg
            ), f"Expected meaningful error, got: {e}"
        finally:
            conn.close()

    # =========================================================================
    # ADDITIONAL TESTS: Gap Coverage (Official SQLCipher Test Suite Alignment)
    # =========================================================================

    def test_multiple_key_pragma_calls_safe(self, tmp_path):
        """Verify setting wrong key then correct key works (official test: multiple-key-calls-safe).

        This test ensures that if a user attempts to open with the wrong key and then
        provides the correct key, the database will authenticate correctly without
        corruption. This is important for applications that may retry key entry.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "multi_key_test.db"
        correct_password = "correct_password_123"
        wrong_password = "wrong_password_456"

        # Create encrypted database
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{correct_password}'")
        cursor.execute("CREATE TABLE test (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO test VALUES (1, 'secret_data')")
        conn.commit()
        conn.close()

        # Reopen - first with wrong key (should fail silently until first op)
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{wrong_password}'")

        # Try an operation - this should fail
        try:
            cursor.execute("SELECT * FROM test")
            cursor.fetchall()
            wrong_key_succeeded = True
        except Exception:
            wrong_key_succeeded = False

        assert not wrong_key_succeeded, "Operation with wrong key should fail"
        conn.close()

        # Now set the correct key on a fresh connection
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{correct_password}'")

        # This should now work
        cursor.execute("SELECT value FROM test WHERE id = 1")
        result = cursor.fetchone()
        assert result is not None, "Should be able to read with correct key"
        assert result[0] == "secret_data", "Data should be intact"
        conn.close()

    def test_rekey_in_wal_mode(self, tmp_path):
        """Verify rekey works correctly with WAL journal mode.

        WAL mode + rekey is a known problem area in SQLCipher. This test ensures
        that rekeying a database operating in WAL mode works correctly and that
        WAL files (if present) are properly re-encrypted.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "wal_rekey_test.db"
        original_password = "original_pass_123"
        new_password = "new_pass_456"

        # Create database with WAL mode
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{original_password}'")
        cursor.execute("PRAGMA journal_mode = WAL")

        # Verify WAL mode is active
        cursor.execute("PRAGMA journal_mode")
        mode = cursor.fetchone()[0]
        if mode.lower() != "wal":
            conn.close()
            pytest.skip("WAL mode not available in this configuration")

        # Create table and insert data
        cursor.execute("CREATE TABLE wal_test (id INTEGER, value TEXT)")
        cursor.execute(
            "INSERT INTO wal_test VALUES (1, 'wal_data_before_rekey')"
        )
        cursor.execute("INSERT INTO wal_test VALUES (2, 'more_wal_data')")
        conn.commit()

        # Perform rekey while in WAL mode
        cursor.execute(f"PRAGMA rekey = '{new_password}'")
        conn.commit()
        conn.close()

        # Verify WAL file is properly handled (may or may not exist)
        wal_path = tmp_path / "wal_rekey_test.db-wal"

        # If WAL file exists, verify it doesn't contain plaintext data
        if wal_path.exists():
            with open(wal_path, "rb") as f:
                wal_content = f.read()
            assert b"wal_data_before_rekey" not in wal_content, (
                "WAL file should not contain plaintext data"
            )

        # Reopen with new password and verify data
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{new_password}'")

        cursor.execute("SELECT COUNT(*) FROM wal_test")
        count = cursor.fetchone()[0]
        assert count == 2, f"Expected 2 rows, got {count}"

        cursor.execute("SELECT value FROM wal_test WHERE id = 1")
        result = cursor.fetchone()
        assert result[0] == "wal_data_before_rekey", (
            "Data should persist after rekey"
        )
        conn.close()

        # Verify old password no longer works
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{original_password}'")
        try:
            cursor.execute("SELECT * FROM wal_test")
            pytest.fail("Old password should no longer work")
        except Exception as e:
            assert "not a database" in str(e).lower()
        finally:
            conn.close()

    def test_kdf_iterations_mismatch_fails(self, tmp_path):
        """Verify mismatched KDF iterations causes 'file is not a database'.

        This is a common migration failure cause. When a database is created with
        specific KDF iteration count, it must be opened with the same count.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "kdf_mismatch_test.db"
        password = "test_password_123"

        # Create database with high iteration count (SQLCipher 4 default: 256000)
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")
        # Uses the default high iteration count (256000 for SQLCipher 4)
        cursor.execute("CREATE TABLE test (id INTEGER)")
        cursor.execute("INSERT INTO test VALUES (1)")
        conn.commit()
        conn.close()

        # Try to open with different (lower) iteration count
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        # Set a different KDF iteration count before key
        cursor.execute("PRAGMA cipher_default_kdf_iter = 1000")
        cursor.execute(f"PRAGMA key = '{password}'")

        # This should fail because KDF iterations don't match
        try:
            cursor.execute("SELECT * FROM test")
            cursor.fetchall()
            # If it succeeds, check if kdf_iter was ignored
            cursor.execute("PRAGMA kdf_iter")
            actual_kdf = cursor.fetchone()[0]
            if int(actual_kdf) != 1000:
                # cipher_default_kdf_iter was likely ignored, which is acceptable
                conn.close()
                pytest.skip(
                    "cipher_default_kdf_iter not supported in this version"
                )
            pytest.fail("Should fail with mismatched KDF iterations")
        except Exception as e:
            error_msg = str(e).lower()
            assert "not a database" in error_msg or "error" in error_msg, (
                f"Expected 'file is not a database' error, got: {e}"
            )
        finally:
            conn.close()

    def test_rekey_on_empty_database(self, tmp_path):
        """Verify rekey behavior on brand new empty database.

        Official test case: rekey-as-first-op-on-empty. Tests what happens when
        rekey is called on a database that has been keyed but has no tables.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "empty_rekey_test.db"
        original_password = "original_pass"
        new_password = "new_pass"

        # Create new database, set key but don't create any tables
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{original_password}'")

        # Immediately try to rekey (no tables created yet)
        # SQLCipher should handle this gracefully
        try:
            cursor.execute(f"PRAGMA rekey = '{new_password}'")
            conn.commit()
            rekey_succeeded = True
        except Exception:
            rekey_succeeded = False

        conn.close()

        if rekey_succeeded:
            # If rekey succeeded, verify new password works
            conn = pysqlcipher3.connect(str(db_path))
            cursor = conn.cursor()
            cursor.execute(f"PRAGMA key = '{new_password}'")

            # Should be able to create tables with new key
            cursor.execute("CREATE TABLE test (id INTEGER)")
            cursor.execute("INSERT INTO test VALUES (1)")
            conn.commit()

            cursor.execute("SELECT * FROM test")
            result = cursor.fetchall()
            assert len(result) == 1, (
                "Should be able to use rekeyed empty database"
            )
            conn.close()
        else:
            # If rekey failed, verify original password still works
            conn = pysqlcipher3.connect(str(db_path))
            cursor = conn.cursor()
            cursor.execute(f"PRAGMA key = '{original_password}'")

            # Should still be usable
            cursor.execute("CREATE TABLE test (id INTEGER)")
            cursor.execute("INSERT INTO test VALUES (1)")
            conn.commit()
            conn.close()

    def test_rekey_with_same_password(self, tmp_path):
        """Verify rekeying with identical password doesn't corrupt database.

        Edge case: what happens when you rekey a database with the same password?
        This should not corrupt the database.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "same_pass_rekey_test.db"
        password = "same_password_123"

        # Create database with data
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")
        cursor.execute("CREATE TABLE test (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO test VALUES (1, 'original_data')")
        cursor.execute("INSERT INTO test VALUES (2, 'more_data')")
        conn.commit()

        # Rekey with the SAME password
        cursor.execute(f"PRAGMA rekey = '{password}'")
        conn.commit()
        conn.close()

        # Verify database is not corrupted
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")

        cursor.execute("SELECT COUNT(*) FROM test")
        count = cursor.fetchone()[0]
        assert count == 2, (
            f"Expected 2 rows after same-password rekey, got {count}"
        )

        cursor.execute("SELECT value FROM test WHERE id = 1")
        result = cursor.fetchone()
        assert result[0] == "original_data", "Data should be intact"

        # Run integrity check
        cursor.execute("PRAGMA integrity_check")
        integrity = cursor.fetchone()[0]
        assert integrity.lower() == "ok", f"Integrity check failed: {integrity}"
        conn.close()

    def test_attach_database_inherits_default_key(self, tmp_path):
        """Verify ATTACH without KEY uses main database key.

        Official test: attach-database-with-default-key. When attaching a database
        without specifying a KEY parameter, it should use the main database's key.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        shared_password = "shared_key_123"

        # Create main database
        main_db_path = tmp_path / "main_default_key.db"
        conn = pysqlcipher3.connect(str(main_db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{shared_password}'")
        cursor.execute("CREATE TABLE main_data (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO main_data VALUES (1, 'main_value')")
        conn.commit()
        conn.close()

        # Create second database with the SAME password
        second_db_path = tmp_path / "second_default_key.db"
        conn = pysqlcipher3.connect(str(second_db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{shared_password}'")
        cursor.execute("CREATE TABLE second_data (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO second_data VALUES (1, 'second_value')")
        conn.commit()
        conn.close()

        # Open main and attach second WITHOUT specifying KEY
        conn = pysqlcipher3.connect(str(main_db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{shared_password}'")

        # Attach without KEY parameter - should use main database key
        # Note: This behavior may vary by SQLCipher version
        try:
            cursor.execute(f"ATTACH DATABASE '{second_db_path}' AS second")

            # Try to access the attached database
            cursor.execute("SELECT value FROM second.second_data WHERE id = 1")
            result = cursor.fetchone()
            assert result[0] == "second_value", (
                "Should access attached DB with inherited key"
            )

            # Verify main database still works
            cursor.execute("SELECT value FROM main_data WHERE id = 1")
            result = cursor.fetchone()
            assert result[0] == "main_value", "Main DB should still work"

        except Exception as e:
            error_msg = str(e).lower()
            # If ATTACH without key doesn't inherit key, it may fail
            # This is acceptable - we're documenting the behavior
            if "not a database" in error_msg or "unable to open" in error_msg:
                pytest.skip(
                    "This SQLCipher version requires explicit KEY for ATTACH"
                )
            raise
        finally:
            conn.close()

    def test_schema_alteration_persists(self, isolated_db_manager):
        """Verify ALTER TABLE changes persist through database reopen.

        Official test: alter-schema. Tests that schema modifications like
        ALTER TABLE ADD COLUMN are properly persisted in encrypted databases.
        """
        username = "testuser"
        password = "SecurePassword123!"

        isolated_db_manager.create_user_database(username, password)

        from sqlalchemy import text

        engine = isolated_db_manager.connections[username]

        # Create initial table
        with engine.connect() as conn:
            conn.execute(
                text("CREATE TABLE alter_test (id INTEGER PRIMARY KEY)")
            )
            conn.execute(text("INSERT INTO alter_test VALUES (1)"))
            conn.commit()

        # Alter table - add new column
        with engine.connect() as conn:
            conn.execute(text("ALTER TABLE alter_test ADD COLUMN new_col TEXT"))
            conn.execute(
                text(
                    "UPDATE alter_test SET new_col = 'added_value' WHERE id = 1"
                )
            )
            conn.commit()

        # Verify change before close
        with engine.connect() as conn:
            result = conn.execute(
                text("SELECT new_col FROM alter_test WHERE id = 1")
            )
            assert result.fetchone()[0] == "added_value"

        # Close and reopen database
        isolated_db_manager.close_user_database(username)
        engine = isolated_db_manager.open_user_database(username, password)
        assert engine is not None, "Should reopen database"

        # Verify schema change persisted
        with engine.connect() as conn:
            # Check column exists
            result = conn.execute(text("PRAGMA table_info(alter_test)"))
            columns = [row[1] for row in result.fetchall()]
            assert "new_col" in columns, "Added column should persist"

            # Check data persisted
            result = conn.execute(
                text("SELECT new_col FROM alter_test WHERE id = 1")
            )
            assert result.fetchone()[0] == "added_value", (
                "Data in new column should persist"
            )

    def test_cipher_default_compatibility_pragma(self, tmp_path):
        """Verify cipher_default_compatibility sets process-wide default.

        Tests that cipher_default_compatibility pragma works for setting
        a process-wide default compatibility mode for all new connections.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "default_compat_test.db"

        # Create in-memory connection to set default compatibility
        setup_conn = pysqlcipher3.connect(":memory:")
        setup_cursor = setup_conn.cursor()

        try:
            # Set process-wide default compatibility to SQLCipher 3
            setup_cursor.execute("PRAGMA cipher_default_compatibility = 3")
        except Exception as e:
            setup_conn.close()
            if "error" in str(e).lower() or "not an error" in str(e).lower():
                pytest.skip("cipher_default_compatibility not supported")
            raise

        setup_conn.close()

        # Now create a new database - it should use SQLCipher 3 settings
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'test_password'")

        # Verify compatibility settings are applied (query should not error)
        # Note: cipher_default_compatibility may not persist to individual DBs
        # The important thing is the pragma doesn't error
        try:
            cursor.execute("PRAGMA cipher_compatibility")
            cursor.fetchone()  # Just verify the query succeeds
        except Exception as e:
            if isinstance(e, AssertionError):
                raise
            # Some versions may not support querying compatibility

        # Create and verify database works
        cursor.execute("CREATE TABLE test (id INTEGER)")
        cursor.execute("INSERT INTO test VALUES (1)")
        conn.commit()

        cursor.execute("SELECT * FROM test")
        result = cursor.fetchall()
        assert len(result) == 1, (
            "Database should work after setting default compat"
        )
        conn.close()

        # Reset to default (SQLCipher 4 compatibility)
        reset_conn = pysqlcipher3.connect(":memory:")
        reset_cursor = reset_conn.cursor()
        try:
            reset_cursor.execute("PRAGMA cipher_default_compatibility = 4")
        except Exception as e:
            if isinstance(e, AssertionError):
                raise
            # May not be supported
        reset_conn.close()

    def test_uri_key_parameter(self, tmp_path):
        """Verify encryption key can be provided via URI parameter.

        Tests opening a database with the key specified in the URI
        connection string rather than via PRAGMA key.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "uri_key_test.db"
        password = "uri_test_password"

        # First, create the database traditionally
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")
        cursor.execute("CREATE TABLE test (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO test VALUES (1, 'uri_data')")
        conn.commit()
        conn.close()

        # Now try to open using URI with key parameter
        # URI format: file:path?key=password
        uri = f"file:{db_path}?key={password}"

        try:
            # Try connecting with URI - requires uri=True parameter
            conn = pysqlcipher3.connect(uri, uri=True)
            cursor = conn.cursor()

            # Key should already be set from URI
            cursor.execute("SELECT value FROM test WHERE id = 1")
            result = cursor.fetchone()
            assert result[0] == "uri_data", "Should read data via URI key"
            conn.close()

        except TypeError:
            # If uri parameter not supported, try without it
            try:
                conn = pysqlcipher3.connect(uri)
                cursor = conn.cursor()
                cursor.execute("SELECT value FROM test WHERE id = 1")
                result = cursor.fetchone()
                assert result[0] == "uri_data", "Should read data via URI key"
                conn.close()
            except Exception as e:
                if (
                    "uri" in str(e).lower()
                    or "not a database" in str(e).lower()
                ):
                    pytest.skip(
                        "URI key parameter not supported in this version"
                    )
                raise

        except Exception as e:
            error_msg = str(e).lower()
            if "not a database" in error_msg or "unable to open" in error_msg:
                pytest.skip("URI key parameter not supported in this version")
            raise

    def test_memory_database_integrity_check_behavior(self, tmp_path):
        """Verify cipher_integrity_check behavior on :memory: database.

        Official test: memory-integrity-check-should-fail. Tests what happens
        when cipher_integrity_check is called on an in-memory database
        (which has no file to verify).
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()

        # Open :memory: database with key
        conn = pysqlcipher3.connect(":memory:")
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'test_password'")

        # Create some data
        cursor.execute("CREATE TABLE test (id INTEGER)")
        cursor.execute("INSERT INTO test VALUES (1)")
        conn.commit()

        # Call cipher_integrity_check on memory database
        try:
            cursor.execute("PRAGMA cipher_integrity_check")
            cursor.fetchone()
            # Memory DB integrity check behavior varies by version
            # It may return an error message, empty result, or skip
            # Memory databases don't have files, so integrity check
            # should either fail, return error, or return empty
            # All of these behaviors are acceptable

        except Exception as e:
            # It's acceptable for cipher_integrity_check to raise an error
            # on memory databases since there's no file to check
            error_msg = str(e).lower()
            # Verify it's a reasonable error about the memory database
            assert (
                "memory" in error_msg
                or "undefined" in error_msg
                or "error" in error_msg
                or "not" in error_msg
            ), f"Unexpected error for memory DB integrity check: {e}"

        finally:
            conn.close()

    def test_rekey_attached_database_by_name(self, tmp_path):
        """Verify rekey can target attached database by schema name.

        Official test: rekey-database-by-name. Tests that PRAGMA schema.rekey
        can be used to rekey an attached database without affecting the main database.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()

        main_password = "main_pass_123"
        attached_original_password = "attached_pass_123"
        attached_new_password = "attached_new_pass_456"

        # Create main database
        main_db_path = tmp_path / "main_rekey_by_name.db"
        conn = pysqlcipher3.connect(str(main_db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{main_password}'")
        cursor.execute("CREATE TABLE main_data (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO main_data VALUES (1, 'main_value')")
        conn.commit()
        conn.close()

        # Create attached database
        attached_db_path = tmp_path / "attached_rekey_by_name.db"
        conn = pysqlcipher3.connect(str(attached_db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{attached_original_password}'")
        cursor.execute("CREATE TABLE attached_data (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO attached_data VALUES (1, 'attached_value')")
        conn.commit()
        conn.close()

        # Open main and attach second database
        conn = pysqlcipher3.connect(str(main_db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{main_password}'")
        cursor.execute(
            f"ATTACH DATABASE '{attached_db_path}' AS other KEY '{attached_original_password}'"
        )

        # Verify both databases accessible
        cursor.execute("SELECT value FROM main_data WHERE id = 1")
        assert cursor.fetchone()[0] == "main_value"
        cursor.execute("SELECT value FROM other.attached_data WHERE id = 1")
        assert cursor.fetchone()[0] == "attached_value"

        # Rekey ONLY the attached database using schema qualifier
        try:
            cursor.execute(f"PRAGMA other.rekey = '{attached_new_password}'")
            rekey_worked = True
        except Exception as e:
            rekey_worked = False
            rekey_error = str(e)
            # Schema-qualified rekey may not be supported in all versions
            if "error" in rekey_error.lower():
                conn.close()
                pytest.skip("Schema-qualified PRAGMA rekey not supported")
            raise

        conn.commit()
        conn.close()

        if rekey_worked:
            # Verify main database still uses original password
            conn = pysqlcipher3.connect(str(main_db_path))
            cursor = conn.cursor()
            cursor.execute(f"PRAGMA key = '{main_password}'")
            cursor.execute("SELECT value FROM main_data WHERE id = 1")
            assert cursor.fetchone()[0] == "main_value", (
                "Main DB should still use original password"
            )
            conn.close()

            # Verify attached database now uses new password
            conn = pysqlcipher3.connect(str(attached_db_path))
            cursor = conn.cursor()
            cursor.execute(f"PRAGMA key = '{attached_new_password}'")
            cursor.execute("SELECT value FROM attached_data WHERE id = 1")
            assert cursor.fetchone()[0] == "attached_value", (
                "Attached DB should use new password"
            )
            conn.close()

            # Verify attached database's original password no longer works
            conn = pysqlcipher3.connect(str(attached_db_path))
            cursor = conn.cursor()
            cursor.execute(f"PRAGMA key = '{attached_original_password}'")
            try:
                cursor.execute("SELECT * FROM attached_data")
                pytest.fail(
                    "Original password should no longer work on attached DB"
                )
            except Exception as e:
                assert "not a database" in str(e).lower()
            finally:
                conn.close()

    # =========================================================================
    # ADDITIONAL TESTS: Codec Error Recovery & Backup API (Gap Coverage)
    # Based on official SQLCipher test suite: sqlcipher-codecerror.test,
    # sqlcipher-backup.test, sqlcipher-integrity.test
    # =========================================================================

    def test_codec_error_recovery_delete_mode(self, tmp_path):
        """Test database recovery after simulated write failure in DELETE journal mode.

        Based on official test: codec-error-journal-delete.
        Verifies that transaction rollback preserves data integrity when
        encryption encounters issues during write operations.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "codec_error_delete_mode.db"
        password = "test_password_123"

        # Create database with DELETE journal mode
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")
        cursor.execute("PRAGMA journal_mode = DELETE")

        # Verify DELETE mode
        cursor.execute("PRAGMA journal_mode")
        mode = cursor.fetchone()[0]
        assert mode.lower() == "delete", f"Expected DELETE mode, got {mode}"

        # Create table and insert initial data
        cursor.execute("CREATE TABLE recovery_test (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO recovery_test VALUES (1, 'original_data')")
        cursor.execute(
            "INSERT INTO recovery_test VALUES (2, 'more_original_data')"
        )
        conn.commit()

        # Start a transaction, make changes, then rollback
        # This simulates what happens when an error occurs during write
        cursor.execute("BEGIN TRANSACTION")
        cursor.execute(
            "UPDATE recovery_test SET value = 'modified' WHERE id = 1"
        )
        cursor.execute("INSERT INTO recovery_test VALUES (3, 'new_row')")

        # Rollback the transaction (simulates recovery from error)
        conn.rollback()

        # Verify original data is intact
        cursor.execute("SELECT value FROM recovery_test WHERE id = 1")
        result = cursor.fetchone()
        assert result[0] == "original_data", (
            "Original data should be preserved after rollback"
        )

        cursor.execute("SELECT COUNT(*) FROM recovery_test")
        count = cursor.fetchone()[0]
        assert count == 2, f"Expected 2 rows after rollback, got {count}"

        conn.close()

        # Verify database is not corrupted - reopen and check
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")

        cursor.execute("PRAGMA integrity_check")
        integrity = cursor.fetchone()[0]
        assert integrity.lower() == "ok", (
            f"Integrity check failed after recovery: {integrity}"
        )

        cursor.execute("SELECT COUNT(*) FROM recovery_test")
        count = cursor.fetchone()[0]
        assert count == 2, "Data should persist correctly after reopen"
        conn.close()

    def test_codec_error_recovery_wal_mode(self, tmp_path):
        """Test database recovery after simulated write failure in WAL mode.

        Based on official test: codec-error-journal-wal.
        WAL mode has different recovery characteristics than DELETE mode.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "codec_error_wal_mode.db"
        password = "test_password_123"

        # Create database with WAL mode
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")
        cursor.execute("PRAGMA journal_mode = WAL")

        # Verify WAL mode
        cursor.execute("PRAGMA journal_mode")
        mode = cursor.fetchone()[0]
        if mode.lower() != "wal":
            conn.close()
            pytest.skip("WAL mode not available in this configuration")

        # Create table and insert initial data
        cursor.execute(
            "CREATE TABLE wal_recovery_test (id INTEGER, value TEXT)"
        )
        cursor.execute(
            "INSERT INTO wal_recovery_test VALUES (1, 'wal_original')"
        )
        cursor.execute(
            "INSERT INTO wal_recovery_test VALUES (2, 'wal_more_data')"
        )
        conn.commit()

        # Start a transaction, make changes, then rollback
        cursor.execute("BEGIN TRANSACTION")
        cursor.execute(
            "UPDATE wal_recovery_test SET value = 'wal_modified' WHERE id = 1"
        )
        cursor.execute(
            "INSERT INTO wal_recovery_test VALUES (3, 'wal_new_row')"
        )

        # Rollback (simulates error recovery in WAL mode)
        conn.rollback()

        # Verify original data is intact
        cursor.execute("SELECT value FROM wal_recovery_test WHERE id = 1")
        result = cursor.fetchone()
        assert result[0] == "wal_original", (
            "Original data should be preserved after WAL rollback"
        )

        cursor.execute("SELECT COUNT(*) FROM wal_recovery_test")
        count = cursor.fetchone()[0]
        assert count == 2, f"Expected 2 rows after WAL rollback, got {count}"

        conn.close()

        # Check WAL file doesn't contain plaintext data
        wal_path = tmp_path / "codec_error_wal_mode.db-wal"
        if wal_path.exists():
            with open(wal_path, "rb") as f:
                wal_content = f.read()
            assert b"wal_original" not in wal_content, (
                "WAL file should not contain plaintext data"
            )

        # Reopen and verify integrity
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")

        cursor.execute("PRAGMA integrity_check")
        integrity = cursor.fetchone()[0]
        assert integrity.lower() == "ok", (
            f"Integrity check failed after WAL recovery: {integrity}"
        )
        conn.close()

    def test_decryption_failure_during_read(self, tmp_path):
        """Test behavior when decryption fails during read operation.

        Based on official test: codec-error-journal-wal-read.
        When a page is corrupted, decryption should fail with HMAC check failure.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "decryption_failure_test.db"
        password = "test_password_123"

        # Create database with multiple pages of data
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")

        # Create table and insert enough data to span multiple pages
        cursor.execute("CREATE TABLE multi_page_test (id INTEGER, data TEXT)")
        # Insert many rows to ensure multiple pages
        large_string = "X" * 500
        for i in range(100):
            cursor.execute(
                f"INSERT INTO multi_page_test VALUES ({i}, '{large_string}')"
            )
        conn.commit()
        conn.close()

        # Get file size to determine where to corrupt
        file_size = db_path.stat().st_size

        # Corrupt a page in the middle of the file (not the first page)
        # The first page contains the header, so we corrupt a data page
        with open(db_path, "r+b") as f:
            # Seek to middle of file
            corrupt_offset = file_size // 2
            f.seek(corrupt_offset)
            original_bytes = f.read(32)
            f.seek(corrupt_offset)
            # Flip all bits to ensure corruption
            corrupted = bytes([b ^ 0xFF for b in original_bytes])
            f.write(corrupted)

        # Try to read from corrupted database
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")

        # Attempt to read data - should eventually hit the corrupted page
        try:
            cursor.execute("SELECT COUNT(*) FROM multi_page_test")
            cursor.fetchone()
            cursor.execute("SELECT * FROM multi_page_test")
            cursor.fetchall()
            # If we get here, corruption might not have affected readable pages
            # Run integrity check to detect corruption
            cursor.execute("PRAGMA integrity_check")
            integrity = cursor.fetchone()[0]
            # Integrity check should detect the corruption
            if integrity.lower() != "ok":
                # Corruption detected - expected behavior
                pass
        except Exception as e:
            error_msg = str(e).lower()
            # Expected errors for corrupted encrypted database
            assert (
                "corrupt" in error_msg
                or "hmac" in error_msg
                or "not a database" in error_msg
                or "decrypt" in error_msg
                or "disk" in error_msg
                or "i/o" in error_msg
                or "malformed" in error_msg
            ), f"Expected decryption/corruption error, got: {e}"
        finally:
            conn.close()

    def test_backup_api_encrypted_to_encrypted(self, tmp_path):
        """Test SQLite backup API between two encrypted databases.

        Based on official test: backup-encrypted-encrypted.
        Uses Python's connection.backup() method to copy data between
        encrypted databases while preserving encryption.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        source_path = tmp_path / "backup_source.db"
        dest_path = tmp_path / "backup_dest.db"
        source_password = "source_pass_123"
        dest_password = "dest_pass_456"

        # Create source encrypted database with data
        source_conn = pysqlcipher3.connect(str(source_path))
        source_cursor = source_conn.cursor()
        source_cursor.execute(f"PRAGMA key = '{source_password}'")
        source_cursor.execute(
            "CREATE TABLE backup_test (id INTEGER, value TEXT)"
        )
        source_cursor.execute(
            "INSERT INTO backup_test VALUES (1, 'backup_value_1')"
        )
        source_cursor.execute(
            "INSERT INTO backup_test VALUES (2, 'backup_value_2')"
        )
        source_cursor.execute(
            "INSERT INTO backup_test VALUES (3, 'backup_value_3')"
        )
        source_conn.commit()

        # Create destination encrypted database
        dest_conn = pysqlcipher3.connect(str(dest_path))
        dest_cursor = dest_conn.cursor()
        dest_cursor.execute(f"PRAGMA key = '{dest_password}'")
        # Need to initialize the database with at least one operation
        dest_cursor.execute("SELECT 1")
        dest_conn.commit()

        # Perform backup using Python's backup API
        try:
            source_conn.backup(dest_conn)
            backup_succeeded = True
        except AttributeError:
            # backup() method not available in this Python/SQLite version
            backup_succeeded = False
            pytest.skip("backup() method not available")
        except Exception as e:
            # Backup between differently-keyed databases may fail
            # This is expected - documenting the behavior
            error_msg = str(e).lower()
            if (
                "readonly" in error_msg
                or "not a database" in error_msg
                or "encrypted" in error_msg
            ):
                backup_succeeded = False
            else:
                raise

        source_conn.close()
        dest_conn.close()

        if backup_succeeded:
            # Verify destination has the source's data
            # Note: After backup, dest uses source's encryption
            dest_conn = pysqlcipher3.connect(str(dest_path))
            dest_cursor = dest_conn.cursor()
            dest_cursor.execute(f"PRAGMA key = '{source_password}'")

            try:
                dest_cursor.execute("SELECT COUNT(*) FROM backup_test")
                count = dest_cursor.fetchone()[0]
                assert count == 3, f"Expected 3 rows in backup, got {count}"

                dest_cursor.execute(
                    "SELECT value FROM backup_test WHERE id = 1"
                )
                result = dest_cursor.fetchone()
                assert result[0] == "backup_value_1", (
                    "Backup data should match source"
                )
            except Exception as e:
                if isinstance(e, AssertionError):
                    raise
                # If source key doesn't work, backup may have failed silently
            finally:
                dest_conn.close()

    def test_backup_api_blocks_plaintext_to_encrypted(self, tmp_path):
        """Test backup from plaintext to encrypted fails appropriately.

        Based on official test: backup-plain-encrypted.
        Verifies that attempting to backup a plaintext database to an
        encrypted database produces an appropriate error.
        """
        import sqlite3

        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        plain_path = tmp_path / "plain_source.db"
        encrypted_path = tmp_path / "encrypted_dest.db"
        password = "encrypted_pass_123"

        # Create plaintext source database
        plain_conn = sqlite3.connect(str(plain_path))
        plain_cursor = plain_conn.cursor()
        plain_cursor.execute("CREATE TABLE plain_data (id INTEGER, value TEXT)")
        plain_cursor.execute("INSERT INTO plain_data VALUES (1, 'plain_value')")
        plain_conn.commit()

        # Create encrypted destination database
        encrypted_conn = pysqlcipher3.connect(str(encrypted_path))
        encrypted_cursor = encrypted_conn.cursor()
        encrypted_cursor.execute(f"PRAGMA key = '{password}'")
        encrypted_cursor.execute("SELECT 1")  # Initialize
        encrypted_conn.commit()

        # Attempt backup from plaintext to encrypted
        try:
            plain_conn.backup(encrypted_conn)
            backup_succeeded = True
        except AttributeError:
            pytest.skip("backup() method not available")
        except TypeError:
            # Cross-module backup (sqlite3 -> sqlcipher) not supported
            # This is expected - documents that plain sqlite3.Connection
            # cannot backup to sqlcipher3.Connection
            backup_succeeded = False
        except Exception as e:
            backup_succeeded = False
            error_msg = str(e).lower()
            # Expect some kind of error about incompatible databases
            assert (
                "error" in error_msg
                or "readonly" in error_msg
                or "database" in error_msg
                or "connection" in error_msg
            ), f"Expected database error, got: {e}"

        plain_conn.close()
        encrypted_conn.close()

        if backup_succeeded:
            # If backup succeeded, verify the dest is now plaintext (overwrote encryption)
            # or verify it still has the plaintext data accessible
            # This behavior documents what actually happens
            verify_conn = pysqlcipher3.connect(str(encrypted_path))
            verify_cursor = verify_conn.cursor()
            verify_cursor.execute(f"PRAGMA key = '{password}'")

            try:
                verify_cursor.execute("SELECT * FROM plain_data")
                # If this works, backup overwrote the encrypted DB with plaintext
            except Exception as e:
                if isinstance(e, AssertionError):
                    raise
                # If this fails, the backup may have been blocked or corrupted the DB
            finally:
                verify_conn.close()

    def test_integrity_check_detects_last_page_tampering(self, tmp_path):
        """Test cipher_integrity_check detects tampering on last page.

        Based on official test: version-4-integrity-check-invalid-last-page.
        The last page of an encrypted database is important for integrity
        because it contains crucial metadata.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "last_page_tampering_test.db"
        password = "test_password_123"

        # Create database with multiple pages
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")

        # Get page size for calculations
        cursor.execute("PRAGMA page_size")
        page_size = int(cursor.fetchone()[0])

        # Create enough data to span multiple pages
        cursor.execute("CREATE TABLE tampering_test (id INTEGER, data TEXT)")
        large_string = "Y" * 500
        for i in range(200):  # Create lots of data
            cursor.execute(
                f"INSERT INTO tampering_test VALUES ({i}, '{large_string}')"
            )
        conn.commit()
        conn.close()

        # Get file size and tamper with the last page
        file_size = db_path.stat().st_size
        last_page_start = file_size - page_size

        # Ensure we're tampering with actual data, not before the file
        if last_page_start > 0:
            with open(db_path, "r+b") as f:
                f.seek(last_page_start + 100)  # 100 bytes into last page
                original = f.read(64)
                f.seek(last_page_start + 100)
                # Flip bits to tamper
                tampered = bytes([b ^ 0xFF for b in original])
                f.write(tampered)

        # Try to open and run integrity check
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")

        tampering_detected = False
        try:
            # cipher_integrity_check should detect HMAC failures
            cursor.execute("PRAGMA cipher_integrity_check")
            result = cursor.fetchall()
            # If cipher_integrity_check returns errors, tampering was detected
            if result and len(result) > 0:
                for row in result:
                    if row[0] and "ok" not in str(row[0]).lower():
                        tampering_detected = True
                        break
            # Also try regular integrity_check
            cursor.execute("PRAGMA integrity_check")
            integrity = cursor.fetchone()[0]
            if integrity.lower() != "ok":
                tampering_detected = True
        except Exception as e:
            # Exception during integrity check also indicates tampering detected
            error_msg = str(e).lower()
            if (
                "corrupt" in error_msg
                or "hmac" in error_msg
                or "disk" in error_msg
                or "malformed" in error_msg
            ):
                tampering_detected = True
        finally:
            conn.close()

        # Tampering should be detected (either via integrity check or exception).
        # The 64-byte XOR-0xFF flip above invalidates the per-page HMAC, which
        # cipher_integrity_check / integrity_check are required to report.
        assert tampering_detected, "Last page tampering should be detectable"

    def test_integrity_check_with_plaintext_header(self, tmp_path):
        """Test cipher_integrity_check works with plaintext header enabled.

        Based on official test: integrity-check-plaintext-header.
        Verifies that integrity checks function correctly when the database
        uses a plaintext header (for iOS compatibility).
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "plaintext_header_integrity_test.db"
        password = "test_password_123"

        # Create database with plaintext header
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")
        cursor.execute("PRAGMA cipher_plaintext_header_size = 32")

        # Create table and insert data
        cursor.execute("CREATE TABLE header_test (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO header_test VALUES (1, 'header_data_1')")
        cursor.execute("INSERT INTO header_test VALUES (2, 'header_data_2')")
        conn.commit()

        # Run cipher_integrity_check
        try:
            cursor.execute("PRAGMA cipher_integrity_check")
            result = cursor.fetchall()
            # Should return 'ok' or empty result for valid database
            integrity_ok = True
            if result:
                for row in result:
                    if row[0] and "ok" not in str(row[0]).lower():
                        if str(row[0]).strip():  # Non-empty error
                            integrity_ok = False
                            break
            assert integrity_ok, (
                f"Integrity check failed with plaintext header: {result}"
            )
        except Exception as e:
            error_msg = str(e).lower()
            # Some versions may not support cipher_integrity_check
            if "undefined" in error_msg or "no such" in error_msg:
                pytest.skip("cipher_integrity_check not supported")
            raise

        # Also verify standard integrity_check
        cursor.execute("PRAGMA integrity_check")
        std_integrity = cursor.fetchone()[0]
        assert std_integrity.lower() == "ok", (
            f"Standard integrity check failed: {std_integrity}"
        )

        conn.close()

        # Verify file has plaintext header (SQLite magic bytes)
        with open(db_path, "rb") as f:
            header = f.read(16)
        assert header == b"SQLite format 3\x00", (
            "File should have plaintext SQLite header"
        )

    def test_key_change_mid_session(self, tmp_path):
        """Test behavior when attempting to change key mid-session.

        Based on official test: change-key-middle.
        Tests what happens when PRAGMA rekey is called after performing
        database operations (not just after opening).
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "mid_session_key_change.db"
        original_password = "original_pass_123"
        new_password = "new_pass_456"

        # Create database and perform operations
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{original_password}'")

        # Create table and insert data
        cursor.execute("CREATE TABLE mid_session_test (id INTEGER, value TEXT)")
        cursor.execute(
            "INSERT INTO mid_session_test VALUES (1, 'initial_data')"
        )
        conn.commit()

        # Perform more operations (we're mid-session now)
        cursor.execute("SELECT * FROM mid_session_test")
        cursor.fetchall()

        cursor.execute("INSERT INTO mid_session_test VALUES (2, 'more_data')")
        conn.commit()

        # Now attempt to rekey mid-session
        try:
            cursor.execute(f"PRAGMA rekey = '{new_password}'")
            conn.commit()
            rekey_succeeded = True
        except Exception:
            rekey_succeeded = False

        conn.close()

        if rekey_succeeded:
            # Verify new password works
            conn = pysqlcipher3.connect(str(db_path))
            cursor = conn.cursor()
            cursor.execute(f"PRAGMA key = '{new_password}'")

            cursor.execute("SELECT COUNT(*) FROM mid_session_test")
            count = cursor.fetchone()[0]
            assert count == 2, f"Expected 2 rows, got {count}"

            cursor.execute("SELECT value FROM mid_session_test WHERE id = 1")
            result = cursor.fetchone()
            assert result[0] == "initial_data", (
                "Data should persist after mid-session rekey"
            )
            conn.close()

            # Verify old password no longer works
            conn = pysqlcipher3.connect(str(db_path))
            cursor = conn.cursor()
            cursor.execute(f"PRAGMA key = '{original_password}'")
            try:
                cursor.execute("SELECT * FROM mid_session_test")
                pytest.fail("Old password should not work after rekey")
            except Exception as e:
                assert "not a database" in str(e).lower()
            finally:
                conn.close()
        else:
            # If rekey failed mid-session, verify database is not corrupted
            # and original password still works
            conn = pysqlcipher3.connect(str(db_path))
            cursor = conn.cursor()
            cursor.execute(f"PRAGMA key = '{original_password}'")

            cursor.execute("SELECT COUNT(*) FROM mid_session_test")
            count = cursor.fetchone()[0]
            assert count == 2, "Data should be intact even if rekey failed"
            conn.close()

    def test_sqlcipher_export_unencrypted_to_encrypted(self, tmp_path):
        """Test sqlcipher_export to encrypt a plaintext database.

        Based on official test: unencrypted-to-encrypted-export.
        Uses sqlcipher_export() to create an encrypted copy of a
        plaintext SQLite database.
        """
        import sqlite3

        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        plain_path = tmp_path / "plaintext_source.db"
        encrypted_path = tmp_path / "encrypted_export.db"
        password = "export_password_123"

        # Create plaintext database with standard sqlite3
        plain_conn = sqlite3.connect(str(plain_path))
        plain_cursor = plain_conn.cursor()
        plain_cursor.execute(
            "CREATE TABLE export_test (id INTEGER, value TEXT)"
        )
        plain_cursor.execute(
            "INSERT INTO export_test VALUES (1, 'export_value_1')"
        )
        plain_cursor.execute(
            "INSERT INTO export_test VALUES (2, 'export_value_2')"
        )
        plain_cursor.execute(
            "INSERT INTO export_test VALUES (3, 'export_value_3')"
        )
        plain_conn.commit()
        plain_conn.close()

        # Open plaintext database with SQLCipher (no key)
        conn = pysqlcipher3.connect(str(plain_path))
        cursor = conn.cursor()
        # Don't set key - it's plaintext

        # Verify we can read plaintext data
        cursor.execute("SELECT COUNT(*) FROM export_test")
        count = cursor.fetchone()[0]
        assert count == 3, "Should read plaintext data"

        # Attach encrypted destination with key
        cursor.execute(
            f"ATTACH DATABASE '{encrypted_path}' AS encrypted KEY '{password}'"
        )

        # Export to encrypted database
        try:
            cursor.execute("SELECT sqlcipher_export('encrypted')")
            cursor.execute("DETACH DATABASE encrypted")
            export_succeeded = True
        except Exception as e:
            export_succeeded = False
            export_error = str(e)

        conn.close()

        if export_succeeded:
            # Verify encrypted database has the data
            conn = pysqlcipher3.connect(str(encrypted_path))
            cursor = conn.cursor()
            cursor.execute(f"PRAGMA key = '{password}'")

            cursor.execute("SELECT COUNT(*) FROM export_test")
            count = cursor.fetchone()[0]
            assert count == 3, f"Expected 3 rows in export, got {count}"

            cursor.execute("SELECT value FROM export_test WHERE id = 1")
            result = cursor.fetchone()
            assert result[0] == "export_value_1", "Exported data should match"
            conn.close()

            # Verify file is actually encrypted (no plaintext header)
            with open(encrypted_path, "rb") as f:
                header = f.read(16)
            assert header != b"SQLite format 3\x00", (
                "Exported database should be encrypted"
            )
        else:
            pytest.skip(f"sqlcipher_export not supported: {export_error}")

    def test_migration_failure_handling(self, tmp_path):
        """Test cipher_migrate behavior on incompatible database.

        Based on official test: migrate-failure.
        Verifies that cipher_migrate handles errors gracefully when
        attempting to migrate a database with incompatible settings.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "migrate_failure_test.db"
        password = "test_password_123"

        # Create database with SQLCipher 4 default settings
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")
        cursor.execute("CREATE TABLE migrate_test (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO migrate_test VALUES (1, 'original_data')")
        conn.commit()
        conn.close()

        # Try to open with wrong settings and then migrate
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        # Set incompatible settings before key
        cursor.execute("PRAGMA cipher_compatibility = 3")
        cursor.execute(f"PRAGMA key = '{password}'")

        # This should fail because database was created with SQLCipher 4 settings
        try:
            cursor.execute("SELECT * FROM migrate_test")
            cursor.fetchall()
            # If this succeeded, cipher_compatibility may have been ignored
            conn.close()
            pytest.skip(
                "cipher_compatibility pragma may not affect existing databases"
            )
        except Exception as e:
            if isinstance(e, AssertionError):
                raise
            # Expected - can't read with wrong settings

        # Now try to migrate - this should fail because settings don't match
        try:
            cursor.execute("PRAGMA cipher_migrate")
            result = cursor.fetchone()
            # cipher_migrate returns 0 on success, non-zero on failure
            if result and result[0] != 0:
                # Migration failed as expected
                pass
        except Exception as e:
            # Migration failure is expected behavior
            error_msg = str(e).lower()
            assert (
                "error" in error_msg
                or "not" in error_msg
                or "fail" in error_msg
            ), f"Expected migration error, got: {e}"
        finally:
            conn.close()

        # Verify original database is not corrupted
        # (should still open with original settings)
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")
        # Don't set cipher_compatibility - use defaults

        try:
            cursor.execute("SELECT value FROM migrate_test WHERE id = 1")
            result = cursor.fetchone()
            assert result[0] == "original_data", (
                "Original data should be intact after failed migration"
            )
        except Exception as e:
            if isinstance(e, AssertionError):
                raise
            # If this fails, database may have been affected
            # This documents the actual behavior
        finally:
            conn.close()

    def test_plaintext_header_migration_delete_mode(self, tmp_path):
        """Test migrating database to use plaintext header offset.

        Based on official test: plaintext-header-migrate-journal-delete.
        Tests converting a standard encrypted database to use a plaintext
        header for iOS compatibility.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "plaintext_header_migration.db"
        password = "test_password_123"

        # Create standard encrypted database (no plaintext header)
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")
        cursor.execute("PRAGMA journal_mode = DELETE")

        cursor.execute(
            "CREATE TABLE header_migrate_test (id INTEGER, value TEXT)"
        )
        cursor.execute(
            "INSERT INTO header_migrate_test VALUES (1, 'migrate_data_1')"
        )
        cursor.execute(
            "INSERT INTO header_migrate_test VALUES (2, 'migrate_data_2')"
        )
        conn.commit()
        conn.close()

        # Verify file does NOT have plaintext header
        with open(db_path, "rb") as f:
            header = f.read(16)
        assert header != b"SQLite format 3\x00", (
            "Original database should be fully encrypted"
        )

        # Now export to a new database with plaintext header
        export_path = tmp_path / "plaintext_header_export.db"

        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")

        # Attach destination with plaintext header
        cursor.execute(
            f"ATTACH DATABASE '{export_path}' AS ptheader KEY '{password}'"
        )
        # Set plaintext header on the attached database
        cursor.execute("PRAGMA ptheader.cipher_plaintext_header_size = 32")

        try:
            cursor.execute("SELECT sqlcipher_export('ptheader')")
            cursor.execute("DETACH DATABASE ptheader")
            export_succeeded = True
        except Exception as e:
            export_succeeded = False
            export_error = str(e)

        conn.close()

        if export_succeeded:
            # Verify exported database has plaintext header
            with open(export_path, "rb") as f:
                header = f.read(16)

            if header == b"SQLite format 3\x00":
                # Plaintext header successfully applied
                # Verify data is accessible
                conn = pysqlcipher3.connect(str(export_path))
                cursor = conn.cursor()
                cursor.execute(f"PRAGMA key = '{password}'")
                cursor.execute("PRAGMA cipher_plaintext_header_size = 32")

                try:
                    cursor.execute("SELECT COUNT(*) FROM header_migrate_test")
                    count = cursor.fetchone()[0]
                    assert count == 2, (
                        f"Expected 2 rows after migration, got {count}"
                    )
                except Exception as e:
                    if isinstance(e, AssertionError):
                        raise
                    # May need additional configuration
                finally:
                    conn.close()
            else:
                # Plaintext header not applied - may need different approach
                pass
        else:
            pytest.skip(
                f"Plaintext header migration not supported: {export_error}"
            )

    def test_invalid_salt_specification_fails(self, tmp_path):
        """Test that invalid salt specification causes HMAC failure.

        Based on official test: raw-key-with-invalid-salt-spec.
        When using a raw key with an incorrect salt, the HMAC check
        should fail.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "invalid_salt_test.db"

        # Create database with a raw key (bypasses PBKDF2)
        # Raw key format: x'<64 hex chars>'
        # DevSkim: ignore DS173237 - Test dummy key, not a real secret
        raw_key = (
            "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"
        )

        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = \"x'{raw_key}'\"")
        cursor.execute("CREATE TABLE salt_test (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO salt_test VALUES (1, 'salt_data')")
        conn.commit()
        conn.close()

        # Try to open with different (wrong) salt
        # DevSkim: ignore DS173237 - Test dummy salt, not a real secret
        wrong_salt = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"

        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()

        # Set wrong salt before key
        try:
            cursor.execute(f"PRAGMA cipher_salt = \"x'{wrong_salt}'\"")
            cursor.execute(f"PRAGMA key = \"x'{raw_key}'\"")

            # Attempt to read - should fail with HMAC error
            cursor.execute("SELECT * FROM salt_test")
            cursor.fetchall()

            # If we get here, wrong salt was either ignored or not checked
            # Check if salt was actually applied
            cursor.execute("PRAGMA cipher_salt")
            used_salt = cursor.fetchone()
            conn.close()

            # Document the behavior - some versions may ignore explicit salt
            # when the key format doesn't include salt specification
            if used_salt and used_salt[0] != f"x'{wrong_salt}'":
                pytest.skip("cipher_salt pragma may be ignored with raw keys")

        except Exception as e:
            conn.close()
            error_msg = str(e).lower()
            # Expected: HMAC check failure or "not a database" error
            assert (
                "hmac" in error_msg
                or "not a database" in error_msg
                or "file is encrypted" in error_msg
                or "error" in error_msg
                or "decrypt" in error_msg
            ), f"Expected HMAC/decryption error with wrong salt, got: {e}"

    # =========================================================================
    # SQLCipher 4.7+ Behavior & 2025 Features
    # =========================================================================

    def test_select_before_key_fails_4_7(self, tmp_path):
        """Verify SELECT 1 fails before PRAGMA key is set (SQLCipher 4.7+ behavior).

        Breaking change in 4.7.0: Schema-less statements no longer work
        before keying. Previously, SELECT 1 would succeed before PRAGMA key;
        now it fails.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "select_before_key_test.db"
        password = "test_password_123"

        # Create an encrypted database first
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")
        cursor.execute("CREATE TABLE pre_key_test (id INTEGER)")
        cursor.execute("INSERT INTO pre_key_test VALUES (1)")
        conn.commit()
        conn.close()

        # Reopen without setting key first
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()

        # Try SELECT 1 WITHOUT setting key first
        # In SQLCipher 4.7+, this should fail
        pre_key_select_failed = False
        try:
            cursor.execute("SELECT 1")
            cursor.fetchone()
            # If we get here, either:
            # 1. SQLCipher version < 4.7.0 (older behavior)
            # 2. The database is somehow readable without key
        except Exception as e:
            pre_key_select_failed = True
            error_msg = str(e).lower()
            # Should get an error about the database being encrypted
            assert (
                "not a database" in error_msg
                or "file is encrypted" in error_msg
                or "encrypted" in error_msg
                or "error" in error_msg
            ), f"Unexpected error type: {e}"

        # Now set key and verify SELECT 1 works
        cursor.execute(f"PRAGMA key = '{password}'")
        cursor.execute("SELECT 1")
        result = cursor.fetchone()
        assert result[0] == 1, "SELECT 1 should work after key is set"

        conn.close()

        # Document the observed behavior (may vary by SQLCipher version)
        if not pre_key_select_failed:
            # Older SQLCipher version - document this
            pytest.skip(
                "SELECT before key succeeded - SQLCipher version may be < 4.7.0"
            )

    def test_select_sqlite_master_before_key_fails(self, tmp_path):
        """Verify schema query fails before PRAGMA key is set.

        Tests that SELECT * FROM sqlite_master fails without key.
        This is a fundamental security requirement for encrypted databases.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "schema_before_key_test.db"
        password = "test_password_123"

        # Create an encrypted database with a table
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")
        cursor.execute("CREATE TABLE schema_test (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO schema_test VALUES (1, 'secret_data')")
        conn.commit()
        conn.close()

        # Reopen without setting key
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()

        # Try to query schema WITHOUT key
        schema_query_failed = False
        try:
            cursor.execute("SELECT * FROM sqlite_master")
            result = cursor.fetchall()
            # If we can read schema without key, that's a security concern
            if result:
                # We got schema data without key - this shouldn't happen
                pass
        except Exception as e:
            schema_query_failed = True
            error_msg = str(e).lower()
            # Expected errors for encrypted databases
            assert (
                "not a database" in error_msg
                or "file is encrypted" in error_msg
                or "encrypted" in error_msg
                or "error" in error_msg
            ), f"Unexpected error: {e}"

        # Now set key and verify query works
        cursor.execute(f"PRAGMA key = '{password}'")
        cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
        tables = [row[0] for row in cursor.fetchall()]
        assert "schema_test" in tables, (
            "Should find schema_test table after key is set"
        )

        conn.close()

        # Schema should not be readable without key
        assert schema_query_failed, (
            "Schema query should fail without key on encrypted database"
        )

    def test_cipher_log_level_settable(self, tmp_path):
        """Test PRAGMA cipher_log_level can be set and queried.

        New in SQLCipher 4.6.0 - default log output to stderr/logcat.
        Log levels: NONE=0, ERROR=1, WARN=2, INFO=3, DEBUG=4, TRACE=5
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "cipher_log_level_test.db"
        password = "test_password_123"

        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")

        # Test setting different log levels
        log_levels = {
            "NONE": 0,
            "ERROR": 1,
            "WARN": 2,
            "INFO": 3,
            "DEBUG": 4,
            "TRACE": 5,
        }

        pragma_supported = True
        for level_name, level_value in log_levels.items():
            try:
                cursor.execute(f"PRAGMA cipher_log_level = {level_value}")
                cursor.execute("PRAGMA cipher_log_level")
                result = cursor.fetchone()

                if result is not None:
                    # Verify the level was set (value may be returned differently)
                    current_level = result[0]
                    # Accept either numeric or string representation
                    assert (
                        current_level == level_value
                        or str(current_level).upper() == level_name
                        or (
                            isinstance(current_level, int)
                            and 0 <= current_level <= 5
                        )
                    ), f"Log level setting failed for {level_name}"
            except Exception as e:
                error_msg = str(e).lower()
                if "undefined" in error_msg or "no such" in error_msg:
                    pragma_supported = False
                    break
                # Other errors might be version-specific

        # Verify database still functions after log level changes
        cursor.execute("CREATE TABLE log_test (id INTEGER)")
        cursor.execute("INSERT INTO log_test VALUES (1)")
        cursor.execute("SELECT * FROM log_test")
        assert cursor.fetchone()[0] == 1, "Database should function normally"

        conn.close()

        if not pragma_supported:
            pytest.skip(
                "cipher_log_level pragma not supported (version < 4.6.0)"
            )

    def test_cipher_status_returns_keyed_state(self, tmp_path):
        """Test PRAGMA cipher_status returns correct keyed state.

        cipher_status returns 1 if database is keyed and not in error state.
        Useful for verifying encryption is properly configured.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "cipher_status_test.db"
        password = "test_password_123"

        # Create encrypted database
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")
        cursor.execute("CREATE TABLE status_test (id INTEGER)")
        conn.commit()
        conn.close()

        # Reopen and check cipher_status
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()

        # Check status BEFORE key - should indicate not ready
        try:
            cursor.execute("PRAGMA cipher_status")
            _before_key_status = cursor.fetchone()
            # Status before key may be 0 or None
        except Exception:
            _before_key_status = None  # noqa: F841

        # Set key
        cursor.execute(f"PRAGMA key = '{password}'")

        # Check status AFTER key - should be 1 (ready)
        try:
            cursor.execute("PRAGMA cipher_status")
            after_key_status = cursor.fetchone()

            if after_key_status is not None:
                # After keying successfully, status should be 1
                # (or non-zero indicating ready state)
                status_value = after_key_status[0]
                assert status_value in (1, "1", "ok", True), (
                    f"Expected keyed status, got: {status_value}"
                )
        except Exception as e:
            error_msg = str(e).lower()
            if "undefined" in error_msg or "no such" in error_msg:
                pytest.skip("cipher_status pragma not supported")
            raise

        # Verify database operations work
        cursor.execute("SELECT COUNT(*) FROM status_test")
        conn.close()

    def test_cipher_migrate_version_4_database_returns_error(self, tmp_path):
        """Test cipher_migrate on already-v4 database returns appropriate error.

        Bug fix in SQLCipher 4.8.0 improved error handling for this case.
        cipher_migrate should fail gracefully when database is already v4.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "cipher_migrate_v4_test.db"
        password = "test_password_123"

        # Create database with SQLCipher 4 defaults
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")

        # Verify we're using SQLCipher 4 settings
        cursor.execute("PRAGMA cipher_version")
        _version = cursor.fetchone()[0]  # noqa: F841

        cursor.execute("CREATE TABLE migrate_v4_test (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO migrate_v4_test VALUES (1, 'v4_data')")
        conn.commit()
        conn.close()

        # Reopen and attempt cipher_migrate
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")

        # cipher_migrate on already-v4 database should return failure (non-zero)
        # or indicate that no migration is needed
        try:
            cursor.execute("PRAGMA cipher_migrate")
            result = cursor.fetchone()

            if result is not None:
                migrate_result = result[0]
                # cipher_migrate returns:
                # 0 = success (migration performed)
                # 1 = failure (no migration needed or error)
                # Already on v4 should return non-zero (no migration needed)
                assert migrate_result != 0, (
                    f"cipher_migrate on v4 DB should return non-zero, "
                    f"got: {migrate_result}"
                )
        except Exception as e:
            error_msg = str(e).lower()
            if "undefined" in error_msg or "no such" in error_msg:
                pytest.skip("cipher_migrate pragma not supported")
            # Other errors are acceptable - migration not needed
            pass

        # Verify database is not corrupted after failed migration attempt
        cursor.execute("SELECT value FROM migrate_v4_test WHERE id = 1")
        result = cursor.fetchone()
        assert result[0] == "v4_data", (
            "Data should be intact after failed cipher_migrate"
        )

        conn.close()

    def test_hmac_check_failure_clear_error(self, tmp_path):
        """Test HMAC check failure provides clear error message.

        Common issue: users get confusing "file is not a database" errors.
        This test documents expected error format and verifies error messages
        contain useful diagnostic information.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "hmac_error_test.db"
        correct_password = "correct_password_123"
        wrong_password = "wrong_password_456"

        # Create encrypted database
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{correct_password}'")
        cursor.execute("CREATE TABLE hmac_test (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO hmac_test VALUES (1, 'secret_data')")
        conn.commit()
        conn.close()

        # Reopen with WRONG key
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{wrong_password}'")

        # Attempt query - should fail with HMAC error
        hmac_failure_detected = False
        error_message = ""
        try:
            cursor.execute("SELECT * FROM hmac_test")
            cursor.fetchall()
            # If we get here, wrong key somehow worked (shouldn't happen)
            pytest.fail("Query with wrong password should fail")
        except Exception as e:
            hmac_failure_detected = True
            error_message = str(e).lower()

        conn.close()

        # Verify error message contains useful diagnostic info
        # Common error messages include:
        # - "file is not a database"
        # - "file is encrypted or is not a database"
        # - "HMAC validation failed"
        # - "decrypt failed"
        assert hmac_failure_detected, "Wrong password should cause failure"

        # Check for common error patterns
        error_patterns = [
            "not a database",
            "encrypted",
            "hmac",
            "decrypt",
            "sqlite_master",
            "error",
        ]
        has_useful_error = any(
            pattern in error_message for pattern in error_patterns
        )
        assert has_useful_error, (
            f"Error should contain useful diagnostic info. Got: {error_message}"
        )

    def test_concurrent_open_close_no_deadlock(self, tmp_path):
        """Test multiple threads can open/close same database without deadlock.

        Thread safety improvements in SQLCipher 4.9.0 address potential
        deadlocks during concurrent access.
        """
        import threading
        import time

        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "concurrent_test.db"
        password = "test_password_123"

        # Create encrypted database first
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")
        cursor.execute("CREATE TABLE concurrent_test (id INTEGER, value TEXT)")
        for i in range(10):
            cursor.execute(
                f"INSERT INTO concurrent_test VALUES ({i}, 'data_{i}')"
            )
        conn.commit()
        conn.close()

        # Track results from threads
        results = []
        errors = []
        lock = threading.Lock()

        def thread_operation(thread_id):
            """Open database, execute query, close."""
            try:
                conn = pysqlcipher3.connect(str(db_path))
                cursor = conn.cursor()
                cursor.execute(f"PRAGMA key = '{password}'")
                cursor.execute("SELECT COUNT(*) FROM concurrent_test")
                count = cursor.fetchone()[0]
                conn.close()

                with lock:
                    results.append((thread_id, count))
            except Exception as e:
                with lock:
                    errors.append((thread_id, str(e)))

        # Spawn 5 threads to do concurrent open/close
        threads = []
        for i in range(5):
            t = threading.Thread(target=thread_operation, args=(i,))
            threads.append(t)

        # Start all threads
        for t in threads:
            t.start()

        # Wait for completion with timeout (deadlock detection)
        timeout = 30  # seconds
        start_time = time.time()
        for t in threads:
            remaining = timeout - (time.time() - start_time)
            if remaining > 0:
                t.join(timeout=remaining)
            if t.is_alive():
                pytest.fail(
                    "Thread deadlock detected - operation did not complete"
                )

        # Check results
        if errors:
            # Some errors may be acceptable (e.g., database locked briefly)
            acceptable_errors = ["locked", "busy"]
            for thread_id, error in errors:
                error_lower = error.lower()
                if not any(ae in error_lower for ae in acceptable_errors):
                    pytest.fail(f"Thread {thread_id} failed with: {error}")

        # At least some threads should have succeeded
        assert len(results) >= 1, (
            "At least one thread should complete successfully"
        )

        # All successful threads should see correct count
        for thread_id, count in results:
            assert count == 10, f"Thread {thread_id} got wrong count: {count}"

    def test_library_shutdown_thread_safety(self, tmp_path):
        """Test library handles connection close while operations may be active.

        Thread safety improvements in SQLCipher 4.9.0 for shutdown sequences.
        Tests graceful handling when closing connection from another thread.
        """
        import threading
        import time

        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "shutdown_safety_test.db"
        password = "test_password_123"

        # Create database with substantial data
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")
        cursor.execute("CREATE TABLE shutdown_test (id INTEGER, value TEXT)")
        for i in range(100):
            cursor.execute(
                f"INSERT INTO shutdown_test VALUES ({i}, 'data_{i}')"
            )
        conn.commit()
        conn.close()

        # Use separate connections for reader and closer
        read_errors = []
        read_count = [0]
        stop_reading = threading.Event()

        def continuous_reader():
            """Continuously read from database until stopped."""
            try:
                reader_conn = pysqlcipher3.connect(str(db_path))
                reader_cursor = reader_conn.cursor()
                reader_cursor.execute(f"PRAGMA key = '{password}'")

                while not stop_reading.is_set():
                    try:
                        reader_cursor.execute(
                            "SELECT COUNT(*) FROM shutdown_test"
                        )
                        reader_cursor.fetchone()
                        read_count[0] += 1
                    except Exception as e:
                        error_msg = str(e).lower()
                        # Database closed/locked errors are expected
                        if not any(
                            x in error_msg
                            for x in ["closed", "locked", "cannot operate"]
                        ):
                            read_errors.append(str(e))
                        break
                reader_conn.close()
            except Exception as e:
                error_msg = str(e).lower()
                if not any(x in error_msg for x in ["closed", "locked"]):
                    read_errors.append(str(e))

        # Start reader thread
        reader_thread = threading.Thread(target=continuous_reader)
        reader_thread.start()

        # Let reader run briefly
        time.sleep(0.1)

        # Signal stop and wait for reader
        stop_reading.set()
        reader_thread.join(timeout=5)

        if reader_thread.is_alive():
            pytest.fail("Reader thread did not stop gracefully")

        # Check that some reads completed
        assert read_count[0] >= 1, (
            "Reader should have completed at least one read"
        )

        # Check for unexpected errors (not related to normal shutdown)
        critical_errors = [
            e
            for e in read_errors
            if "crash" in e.lower()
            or "segfault" in e.lower()
            or "abort" in e.lower()
        ]
        assert not critical_errors, (
            f"Critical errors during shutdown: {critical_errors}"
        )

    # ========================================================================
    # SQLCipher 4.x Compatibility Tests (January 2026)
    # Based on GitHub issues #98, #4025, #16482 and iOS compatibility requirements
    # ========================================================================

    def test_rekey_preserves_row_count(self, tmp_path):
        """Verify rekey doesn't delete data (addresses GitHub issue #98).

        Issue #98 reported that rekey operation could result in empty database.
        This test verifies exact row count before and after rekey matches.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "rekey_row_count_test.db"
        original_password = "original_password_123"  # DevSkim: ignore DS117838
        new_password = "new_password_456"  # DevSkim: ignore DS117838
        num_rows = 1000

        # Create database with substantial data
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{original_password}'")
        cursor.execute(
            "CREATE TABLE rekey_test (id INTEGER PRIMARY KEY, value TEXT)"
        )

        # Insert 1000 rows
        for i in range(num_rows):
            cursor.execute(
                f"INSERT INTO rekey_test (value) VALUES ('data_{i}')"
            )
        conn.commit()

        # Get row count before rekey
        cursor.execute("SELECT COUNT(*) FROM rekey_test")
        count_before = cursor.fetchone()[0]
        assert count_before == num_rows, (
            f"Setup failed: expected {num_rows}, got {count_before}"
        )

        # Perform rekey
        cursor.execute(f"PRAGMA rekey = '{new_password}'")
        conn.commit()

        # Get row count after rekey (same connection)
        cursor.execute("SELECT COUNT(*) FROM rekey_test")
        count_after_same_conn = cursor.fetchone()[0]
        conn.close()

        # Reopen with new password and verify count
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{new_password}'")
        cursor.execute("SELECT COUNT(*) FROM rekey_test")
        count_after_reopen = cursor.fetchone()[0]
        conn.close()

        # Verify row counts match
        assert count_after_same_conn == num_rows, (
            f"Row count changed after rekey (same conn): "
            f"expected {num_rows}, got {count_after_same_conn}"
        )
        assert count_after_reopen == num_rows, (
            f"Row count changed after rekey (reopen): "
            f"expected {num_rows}, got {count_after_reopen}"
        )

    def test_rekey_preserves_data_content(self, tmp_path):
        """Verify specific data values survive rekey operation.

        Addresses GitHub issue #98 - ensures not just row count but actual
        data content is preserved including Unicode and various data types.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "rekey_data_content_test.db"
        original_password = "original_pass"  # DevSkim: ignore DS117838
        new_password = "new_pass"  # DevSkim: ignore DS117838

        # Test data with various types
        test_data = [
            (1, "simple text", 42, 3.14159),
            (2, "Unicode: こんにちは 🎉 émojis", 100, 2.71828),
            (3, "Special chars: <>&\"'", -999, 0.0),
            (4, "Long text" * 100, 2147483647, 1e308),
            (5, "", 0, -1e308),
        ]

        # Create database with test data
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{original_password}'")
        cursor.execute("""
            CREATE TABLE content_test (
                id INTEGER PRIMARY KEY,
                text_col TEXT,
                int_col INTEGER,
                real_col REAL
            )
        """)

        for row in test_data:
            cursor.execute("INSERT INTO content_test VALUES (?, ?, ?, ?)", row)
        conn.commit()

        # Perform rekey
        cursor.execute(f"PRAGMA rekey = '{new_password}'")
        conn.commit()
        conn.close()

        # Reopen with new password and verify data
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{new_password}'")
        cursor.execute(
            "SELECT id, text_col, int_col, real_col FROM content_test "
            "ORDER BY id"
        )
        results = cursor.fetchall()
        conn.close()

        # Verify each row matches original data
        assert len(results) == len(test_data), (
            f"Row count mismatch: expected {len(test_data)}, got {len(results)}"
        )

        for original, retrieved in zip(test_data, results):
            assert original[0] == retrieved[0], (
                f"ID mismatch: expected {original[0]}, got {retrieved[0]}"
            )
            assert original[1] == retrieved[1], (
                f"Text mismatch for id {original[0]}"
            )
            assert original[2] == retrieved[2], (
                f"Integer mismatch for id {original[0]}"
            )
            # Float comparison with tolerance
            assert abs(original[3] - retrieved[3]) < 1e-10 or (
                original[3] == retrieved[3]
            ), f"Float mismatch for id {original[0]}"

    def test_rekey_on_plaintext_db_fails(self, tmp_path):
        """PRAGMA rekey cannot encrypt a plaintext database.

        Addresses DBeaver issue #16482 - rekey on plaintext DB should fail
        gracefully, not corrupt the database. SQLCipher requires the DB to
        already be encrypted before rekey can be used.
        """
        import sqlite3

        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "plaintext_rekey_test.db"
        new_password = "new_password"  # DevSkim: ignore DS117838

        # Create plaintext SQLite database using standard sqlite3
        conn = sqlite3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute("CREATE TABLE test (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO test VALUES (1, 'test_data')")
        conn.commit()
        conn.close()

        # Verify file is plaintext (has SQLite magic)
        with open(db_path, "rb") as f:
            header = f.read(16)
        assert header[:6] == b"SQLite", (
            "Test setup failed: database should be plaintext"
        )

        # Try to rekey plaintext database using sqlcipher
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()

        # Don't set key (it's plaintext) - just try rekey
        # We expect this to either fail or be a no-op (not actually encrypt)
        try:
            cursor.execute(f"PRAGMA rekey = '{new_password}'")
            conn.commit()
        except Exception as e:
            if isinstance(e, AssertionError):
                raise
            # Expected - rekey on plaintext may fail
        conn.close()

        # Verify database is still accessible (not corrupted)
        conn = sqlite3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute("SELECT * FROM test")
        result = cursor.fetchall()
        conn.close()

        assert len(result) == 1, (
            "Database should still be accessible after failed rekey"
        )
        assert result[0] == (1, "test_data"), (
            "Data should be preserved after failed rekey"
        )

        # Verify file is still plaintext
        with open(db_path, "rb") as f:
            header = f.read(16)
        assert header[:6] == b"SQLite", (
            "Database should remain plaintext (rekey should not encrypt it)"
        )

    def test_hmac_disabled_database_opens(self, tmp_path):
        """Test cipher_use_hmac=OFF mode works correctly.

        Addresses SQLiteBrowser issue #4025 - databases created with
        cipher_use_hmac=OFF should still be openable and functional.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "hmac_off_test.db"
        password = "test_password"  # DevSkim: ignore DS117838

        # Create database with HMAC disabled
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")
        cursor.execute("PRAGMA cipher_use_hmac = OFF")

        # Insert test data
        cursor.execute("CREATE TABLE hmac_test (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO hmac_test VALUES (1, 'hmac_off_data')")
        cursor.execute("INSERT INTO hmac_test VALUES (2, 'more_data')")
        conn.commit()
        conn.close()

        # Reopen with same settings
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")
        cursor.execute("PRAGMA cipher_use_hmac = OFF")

        # Verify data is accessible
        cursor.execute("SELECT * FROM hmac_test ORDER BY id")
        results = cursor.fetchall()
        conn.close()

        assert len(results) == 2, "Should retrieve both rows"
        assert results[0] == (1, "hmac_off_data")
        assert results[1] == (2, "more_data")

        # Verify wrong password still fails (encryption still works)
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute("PRAGMA key = 'wrong_password'")
        cursor.execute("PRAGMA cipher_use_hmac = OFF")

        wrong_password_error = None
        try:
            cursor.execute("SELECT * FROM hmac_test")
            cursor.fetchall()
        except Exception as e:
            wrong_password_error = str(e)
        conn.close()

        assert wrong_password_error is not None, (
            "Wrong password should still fail even with HMAC disabled"
        )

    def test_16kb_page_size(self, tmp_path):
        """Test with 16KB page size (Google Play Store requirement).

        Addresses android-database-sqlcipher issue #664 - Google Play
        requires 16KB page alignment for Android 15+ apps.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "page_16kb_test.db"
        password = "test_password"  # DevSkim: ignore DS117838
        page_size = 16384  # 16KB

        # Create database with 16KB page size
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")
        cursor.execute(f"PRAGMA cipher_page_size = {page_size}")

        # Create table and insert data
        cursor.execute(
            "CREATE TABLE large_page_test (id INTEGER PRIMARY KEY, data TEXT)"
        )

        # Insert enough data to span multiple pages
        for i in range(100):
            cursor.execute(
                f"INSERT INTO large_page_test (data) VALUES "
                f"('{('x' * 1000)}_{i}')"
            )
        conn.commit()

        # Verify page size was applied
        cursor.execute("PRAGMA cipher_page_size")
        actual_page_size = int(cursor.fetchone()[0])
        conn.close()

        # Reopen and verify data persistence
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")
        cursor.execute(f"PRAGMA cipher_page_size = {page_size}")

        cursor.execute("SELECT COUNT(*) FROM large_page_test")
        count = cursor.fetchone()[0]
        conn.close()

        assert count == 100, f"All rows should persist, got {count}"
        assert actual_page_size == page_size, (
            f"Page size should be {page_size}, got {actual_page_size}"
        )

        # Verify file size is aligned to page size (rough check)
        file_size = db_path.stat().st_size
        assert file_size > page_size, (
            "Database file should be larger than one page"
        )

    def test_plaintext_header_with_external_salt(self, tmp_path):
        """Create DB with plaintext header and external salt management.

        For iOS compatibility (Mozilla issue #2100, SQLCipher #352).
        iOS requires plaintext header to prevent 0xdead10cc termination.
        """
        import secrets

        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "external_salt_test.db"
        password = "test_password"  # DevSkim: ignore DS117838

        # Generate random 16-byte salt (32 hex chars)
        external_salt = secrets.token_hex(16)

        # Create database with plaintext header and external salt
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()

        # IMPORTANT: cipher_plaintext_header_size and cipher_salt must be
        # set BEFORE PRAGMA key
        cursor.execute("PRAGMA cipher_plaintext_header_size = 32")
        cursor.execute(f"PRAGMA cipher_salt = \"x'{external_salt}'\"")
        cursor.execute(f"PRAGMA key = '{password}'")

        # Create table and insert data
        cursor.execute("CREATE TABLE salt_test (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO salt_test VALUES (1, 'external_salt_data')")
        conn.commit()
        conn.close()

        # Verify file has SQLite magic bytes (plaintext header)
        with open(db_path, "rb") as f:
            header = f.read(32)

        # First 16 bytes should contain SQLite header string for plaintext
        # header mode
        has_sqlite_magic = b"SQLite" in header

        if has_sqlite_magic:
            # Plaintext header applied - verify we can reopen
            conn = pysqlcipher3.connect(str(db_path))
            cursor = conn.cursor()
            cursor.execute("PRAGMA cipher_plaintext_header_size = 32")
            cursor.execute(f"PRAGMA cipher_salt = \"x'{external_salt}'\"")
            cursor.execute(f"PRAGMA key = '{password}'")

            cursor.execute("SELECT * FROM salt_test")
            result = cursor.fetchall()
            conn.close()

            assert len(result) == 1
            assert result[0] == (1, "external_salt_data")
        else:
            # Some SQLCipher versions may not support this feature
            pytest.skip(
                "Plaintext header with external salt not supported in "
                "this SQLCipher version"
            )

    def test_extract_salt_for_external_storage(self, tmp_path):
        """Extract salt via PRAGMA cipher_salt for external storage.

        Tests the ability to extract the salt from an encrypted database
        for external storage (needed for iOS plaintext header mode).
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "extract_salt_test.db"
        password = "test_password"  # DevSkim: ignore DS117838

        # Create standard encrypted database
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")
        cursor.execute("CREATE TABLE salt_extract_test (id INTEGER)")
        cursor.execute("INSERT INTO salt_extract_test VALUES (1)")
        conn.commit()

        # Extract salt
        cursor.execute("PRAGMA cipher_salt")
        salt_result = cursor.fetchone()
        conn.close()

        if salt_result and salt_result[0]:
            salt = salt_result[0]

            # Salt should be a hex string (with or without x'' wrapper)
            salt_str = str(salt)

            # Remove x'' wrapper if present
            if salt_str.startswith("x'") and salt_str.endswith("'"):
                salt_hex = salt_str[2:-1]
            else:
                salt_hex = salt_str

            # Salt should be 32 hex characters (16 bytes)
            # Note: some versions may return different formats
            assert len(salt_hex) >= 16, (
                f"Salt should be at least 16 chars, got {len(salt_hex)}"
            )

            # Verify salt contains only hex characters
            try:
                int(salt_hex[:32], 16)
                is_valid_hex = True
            except ValueError:
                is_valid_hex = False

            assert is_valid_hex, f"Salt should be hex string, got: {salt_hex}"
        else:
            pytest.skip("PRAGMA cipher_salt not supported in this version")

    def test_reopen_with_extracted_salt(self, tmp_path):
        """Reopen plaintext header DB using stored salt.

        Tests the full workflow of creating a plaintext header database,
        extracting the salt, and reopening with that salt.
        """
        import secrets

        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "reopen_salt_test.db"
        password = "test_password"  # DevSkim: ignore DS117838

        # Generate external salt
        external_salt = secrets.token_hex(16)

        # Create database with plaintext header
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()

        try:
            cursor.execute("PRAGMA cipher_plaintext_header_size = 32")
            cursor.execute(f"PRAGMA cipher_salt = \"x'{external_salt}'\"")
            cursor.execute(f"PRAGMA key = '{password}'")

            cursor.execute("CREATE TABLE reopen_test (id INTEGER, data TEXT)")
            cursor.execute("INSERT INTO reopen_test VALUES (1, 'test_value')")
            cursor.execute(
                "INSERT INTO reopen_test VALUES (2, 'another_value')"
            )
            conn.commit()
            conn.close()

            # Simulate storing salt externally (like iOS Keychain)
            stored_salt = external_salt

            # Reopen using stored salt
            conn = pysqlcipher3.connect(str(db_path))
            cursor = conn.cursor()
            cursor.execute("PRAGMA cipher_plaintext_header_size = 32")
            cursor.execute(f"PRAGMA cipher_salt = \"x'{stored_salt}'\"")
            cursor.execute(f"PRAGMA key = '{password}'")

            # Verify data accessible
            cursor.execute("SELECT * FROM reopen_test ORDER BY id")
            results = cursor.fetchall()
            conn.close()

            assert len(results) == 2, f"Expected 2 rows, got {len(results)}"
            assert results[0] == (1, "test_value")
            assert results[1] == (2, "another_value")

        except Exception as e:
            conn.close()
            error_msg = str(e).lower()
            if (
                "cipher_plaintext_header" in error_msg
                or "not supported" in error_msg
            ):
                pytest.skip(
                    "Plaintext header mode not supported in this version"
                )
            raise

    def test_cipher_compatibility_3_after_key(self, tmp_path):
        """cipher_compatibility must be set AFTER key pragma.

        Based on pysqlcipher3 documentation - when opening v3 databases,
        cipher_compatibility = 3 must be set AFTER PRAGMA key.
        """
        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        db_path = tmp_path / "compat_order_test.db"
        password = "test_password"  # DevSkim: ignore DS117838

        # Create database with v3 compatibility settings
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")
        cursor.execute("PRAGMA cipher_compatibility = 3")

        cursor.execute("CREATE TABLE compat_test (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO compat_test VALUES (1, 'v3_compat_data')")
        conn.commit()
        conn.close()

        # Reopen with CORRECT order: key THEN compatibility
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")
        cursor.execute("PRAGMA cipher_compatibility = 3")

        cursor.execute("SELECT * FROM compat_test")
        result = cursor.fetchall()
        conn.close()

        assert len(result) == 1, "Data should be accessible"
        assert result[0] == (1, "v3_compat_data")

        # Test that setting compatibility BEFORE key may cause issues
        # (This documents the correct order requirement)
        conn = pysqlcipher3.connect(str(db_path))
        cursor = conn.cursor()

        # Wrong order: compatibility before key
        cursor.execute("PRAGMA cipher_compatibility = 3")
        cursor.execute(f"PRAGMA key = '{password}'")

        # This might work or fail depending on SQLCipher version
        try:
            cursor.execute("SELECT * FROM compat_test")
            result = cursor.fetchall()
            # If it works, that's fine - we're just documenting behavior
        except Exception as e:
            if isinstance(e, AssertionError):
                raise
            # If it fails, that demonstrates why order matters
        conn.close()

    def test_add_encryption_to_plaintext_db(self, tmp_path):
        """Use sqlcipher_export to add encryption to plaintext DB.

        Based on pysqlcipher3 test coverage - tests the recommended method
        for encrypting an existing plaintext database using ATTACH and
        sqlcipher_export().
        """
        import sqlite3

        from local_deep_research.database.sqlcipher_compat import (
            get_sqlcipher_module,
        )

        pysqlcipher3 = get_sqlcipher_module()
        plaintext_path = tmp_path / "plaintext_source.db"
        encrypted_path = tmp_path / "encrypted_dest.db"
        password = "encryption_password"  # DevSkim: ignore DS117838

        # Create plaintext database with standard sqlite3
        conn = sqlite3.connect(str(plaintext_path))
        cursor = conn.cursor()
        cursor.execute(
            "CREATE TABLE export_test (id INTEGER PRIMARY KEY, name TEXT)"
        )
        cursor.execute("INSERT INTO export_test VALUES (1, 'Alice')")
        cursor.execute("INSERT INTO export_test VALUES (2, 'Bob')")
        cursor.execute("INSERT INTO export_test VALUES (3, 'Charlie')")
        conn.commit()
        conn.close()

        # Verify source is plaintext
        with open(plaintext_path, "rb") as f:
            header = f.read(16)
        assert header[:6] == b"SQLite", "Source should be plaintext"

        # Open plaintext database with sqlcipher (no key)
        conn = pysqlcipher3.connect(str(plaintext_path))
        cursor = conn.cursor()

        # Attach encrypted destination
        cursor.execute(
            f"ATTACH DATABASE '{encrypted_path}' AS encrypted KEY '{password}'"
        )

        # Export to encrypted database
        cursor.execute("SELECT sqlcipher_export('encrypted')")

        # Detach
        cursor.execute("DETACH DATABASE encrypted")
        conn.close()

        # Verify encrypted database is actually encrypted
        with open(encrypted_path, "rb") as f:
            header = f.read(16)
        assert header[:6] != b"SQLite", (
            "Encrypted database should not have plaintext header"
        )

        # Open encrypted database and verify data
        conn = pysqlcipher3.connect(str(encrypted_path))
        cursor = conn.cursor()
        cursor.execute(f"PRAGMA key = '{password}'")

        cursor.execute("SELECT * FROM export_test ORDER BY id")
        results = cursor.fetchall()
        conn.close()

        assert len(results) == 3, f"Expected 3 rows, got {len(results)}"
        assert results[0] == (1, "Alice")
        assert results[1] == (2, "Bob")
        assert results[2] == (3, "Charlie")
