"""
Tests for research_library.utils module.

Tests path traversal protection and utility functions.
"""

from pathlib import Path
from unittest.mock import patch, Mock

from local_deep_research.constants import (
    FILE_PATH_METADATA_ONLY,
    FILE_PATH_TEXT_ONLY,
    FILE_PATH_BLOB_DELETED,
)


class TestGetAbsoluteLibraryPath:
    """Tests for get_absolute_library_path function."""

    def test_valid_relative_path_returns_absolute_path(self, tmp_path):
        """Valid relative paths should return absolute paths."""
        from local_deep_research.research_library.utils import (
            get_absolute_library_path,
        )

        with patch(
            "local_deep_research.research_library.utils.get_library_storage_path"
        ) as mock_storage:
            mock_storage.return_value = tmp_path

            result = get_absolute_library_path("pdfs/test.pdf", "testuser")

            assert result is not None
            assert result == tmp_path / "pdfs/test.pdf"

    def test_path_traversal_with_dotdot_returns_none(self, tmp_path):
        """Path traversal attempts with .. should return None."""
        from local_deep_research.research_library.utils import (
            get_absolute_library_path,
        )

        with patch(
            "local_deep_research.research_library.utils.get_library_storage_path"
        ) as mock_storage:
            mock_storage.return_value = tmp_path

            result = get_absolute_library_path(
                "../../../etc/passwd", "testuser"
            )

            assert result is None

    def test_path_traversal_with_nested_dotdot_returns_none(self, tmp_path):
        """Path traversal with nested .. should return None."""
        from local_deep_research.research_library.utils import (
            get_absolute_library_path,
        )

        with patch(
            "local_deep_research.research_library.utils.get_library_storage_path"
        ) as mock_storage:
            mock_storage.return_value = tmp_path

            # Various traversal attempts using actual .. patterns
            traversal_paths = [
                "pdfs/../../secret.txt",
                "./../../etc/passwd",
                "subdir/../../../etc/passwd",
            ]

            for path in traversal_paths:
                result = get_absolute_library_path(path, "testuser")
                assert result is None, f"Path '{path}' should have been blocked"

    def test_absolute_path_outside_root_returns_none(self, tmp_path):
        """Absolute paths outside library root should return None."""
        from local_deep_research.research_library.utils import (
            get_absolute_library_path,
        )

        with patch(
            "local_deep_research.research_library.utils.get_library_storage_path"
        ) as mock_storage:
            mock_storage.return_value = tmp_path

            result = get_absolute_library_path("/etc/passwd", "testuser")

            assert result is None

    def test_empty_path_returns_none(self, tmp_path):
        """Empty path should return None."""
        from local_deep_research.research_library.utils import (
            get_absolute_library_path,
        )

        with patch(
            "local_deep_research.research_library.utils.get_library_storage_path"
        ) as mock_storage:
            mock_storage.return_value = tmp_path

            result = get_absolute_library_path("", "testuser")

            assert result is None

    def test_nested_valid_path_returns_correct_path(self, tmp_path):
        """Deeply nested valid paths should work correctly."""
        from local_deep_research.research_library.utils import (
            get_absolute_library_path,
        )

        with patch(
            "local_deep_research.research_library.utils.get_library_storage_path"
        ) as mock_storage:
            mock_storage.return_value = tmp_path

            result = get_absolute_library_path(
                "pdfs/2024/01/document.pdf", "testuser"
            )

            assert result is not None
            assert result == tmp_path / "pdfs/2024/01/document.pdf"

    def test_symlink_blocked(self, tmp_path):
        """Symlinks should be blocked in get_absolute_library_path."""
        import pytest

        from local_deep_research.research_library.utils import (
            get_absolute_library_path,
        )

        # Create a symlink inside library root pointing outside
        link_path = tmp_path / "evil_link"
        try:
            link_path.symlink_to("/etc/passwd")
        except OSError:
            pytest.skip("Cannot create symlinks in this environment")

        with patch(
            "local_deep_research.research_library.utils.get_library_storage_path"
        ) as mock_storage:
            mock_storage.return_value = tmp_path

            result = get_absolute_library_path("evil_link", "testuser")
            assert result is None


class TestGetAbsolutePathFromSettings:
    """Tests for get_absolute_path_from_settings function."""

    def test_valid_relative_path_returns_absolute_path(self, tmp_path):
        """Valid relative paths should return absolute paths."""
        from local_deep_research.research_library.utils import (
            get_absolute_path_from_settings,
        )

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            result = get_absolute_path_from_settings("pdfs/test.pdf")

            assert result is not None
            assert result == tmp_path / "pdfs/test.pdf"

    def test_empty_path_returns_library_root(self, tmp_path):
        """Empty path should return the library root."""
        from local_deep_research.research_library.utils import (
            get_absolute_path_from_settings,
        )

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            result = get_absolute_path_from_settings("")

            assert result is not None
            assert result == tmp_path

    def test_path_traversal_returns_none(self, tmp_path):
        """Path traversal attempts should return None."""
        from local_deep_research.research_library.utils import (
            get_absolute_path_from_settings,
        )

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            traversal_paths = [
                "../../../etc/passwd",
                "pdfs/../../secret.txt",
            ]

            for path in traversal_paths:
                result = get_absolute_path_from_settings(path)
                assert result is None, f"Path '{path}' should have been blocked"

    def test_symlink_blocked(self, tmp_path):
        """Symlinks should be blocked in get_absolute_path_from_settings."""
        import pytest

        from local_deep_research.research_library.utils import (
            get_absolute_path_from_settings,
        )

        # Create a symlink inside library root pointing outside
        link_path = tmp_path / "evil_link"
        try:
            link_path.symlink_to("/etc/passwd")
        except OSError:
            pytest.skip("Cannot create symlinks in this environment")

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            result = get_absolute_path_from_settings("evil_link")
            assert result is None


class TestLibraryServiceSafeAbsolutePath:
    """Tests for LibraryService._get_safe_absolute_path method."""

    def test_metadata_only_returns_none(self):
        """metadata_only marker should return None."""
        from local_deep_research.research_library.services.library_service import (
            LibraryService,
        )

        service = LibraryService("testuser")

        result = service._get_safe_absolute_path(FILE_PATH_METADATA_ONLY)

        assert result is None

    def test_text_only_not_stored_returns_none(self):
        """text_only_not_stored marker should return None."""
        from local_deep_research.research_library.services.library_service import (
            LibraryService,
        )

        service = LibraryService("testuser")

        result = service._get_safe_absolute_path(FILE_PATH_TEXT_ONLY)

        assert result is None

    def test_empty_path_returns_none(self):
        """Empty path should return None."""
        from local_deep_research.research_library.services.library_service import (
            LibraryService,
        )

        service = LibraryService("testuser")

        result = service._get_safe_absolute_path("")

        assert result is None

    def test_none_path_returns_none(self):
        """None path should return None."""
        from local_deep_research.research_library.services.library_service import (
            LibraryService,
        )

        service = LibraryService("testuser")

        result = service._get_safe_absolute_path(None)

        assert result is None

    def test_valid_path_returns_string(self, tmp_path):
        """Valid paths should return a string."""
        from local_deep_research.research_library.services.library_service import (
            LibraryService,
        )

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            service = LibraryService("testuser")

            result = service._get_safe_absolute_path("pdfs/test.pdf")

            assert result is not None
            assert isinstance(result, str)
            assert result == str(tmp_path / "pdfs/test.pdf")

    def test_path_traversal_returns_none(self, tmp_path):
        """Path traversal attempts should return None."""
        from local_deep_research.research_library.services.library_service import (
            LibraryService,
        )

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            service = LibraryService("testuser")

            result = service._get_safe_absolute_path("../../../etc/passwd")

            assert result is None


class TestPDFStorageManagerSafeFilePath:
    """Tests for PDFStorageManager._get_safe_file_path method."""

    def test_valid_path_returns_path_object(self, tmp_path):
        """Valid relative paths should return Path objects."""
        from local_deep_research.research_library.services.pdf_storage_manager import (
            PDFStorageManager,
        )

        manager = PDFStorageManager(
            library_root=tmp_path,
            storage_mode="filesystem",
        )

        result = manager._get_safe_file_path("pdfs/test.pdf")

        assert result is not None
        assert isinstance(result, Path)
        assert result == tmp_path / "pdfs/test.pdf"

    def test_metadata_only_returns_none(self, tmp_path):
        """metadata_only marker should return None."""
        from local_deep_research.research_library.services.pdf_storage_manager import (
            PDFStorageManager,
        )

        manager = PDFStorageManager(
            library_root=tmp_path,
            storage_mode="filesystem",
        )

        result = manager._get_safe_file_path(FILE_PATH_METADATA_ONLY)

        assert result is None

    def test_text_only_not_stored_returns_none(self, tmp_path):
        """text_only_not_stored marker should return None."""
        from local_deep_research.research_library.services.pdf_storage_manager import (
            PDFStorageManager,
        )

        manager = PDFStorageManager(
            library_root=tmp_path,
            storage_mode="filesystem",
        )

        result = manager._get_safe_file_path(FILE_PATH_TEXT_ONLY)

        assert result is None

    def test_empty_path_returns_none(self, tmp_path):
        """Empty path should return None."""
        from local_deep_research.research_library.services.pdf_storage_manager import (
            PDFStorageManager,
        )

        manager = PDFStorageManager(
            library_root=tmp_path,
            storage_mode="filesystem",
        )

        result = manager._get_safe_file_path("")

        assert result is None

    def test_path_traversal_returns_none(self, tmp_path):
        """Path traversal attempts should return None."""
        from local_deep_research.research_library.services.pdf_storage_manager import (
            PDFStorageManager,
        )

        manager = PDFStorageManager(
            library_root=tmp_path,
            storage_mode="filesystem",
        )

        traversal_paths = [
            "../../../etc/passwd",
            "pdfs/../../secret.txt",
        ]

        for path in traversal_paths:
            result = manager._get_safe_file_path(path)
            assert result is None, f"Path '{path}' should have been blocked"

    def test_none_path_returns_none(self, tmp_path):
        """None path should return None."""
        from local_deep_research.research_library.services.pdf_storage_manager import (
            PDFStorageManager,
        )

        manager = PDFStorageManager(
            library_root=tmp_path,
            storage_mode="filesystem",
        )

        result = manager._get_safe_file_path(None)

        assert result is None


class TestPathTraversalIntegration:
    """Integration tests for path traversal protection across components."""

    def test_malicious_document_file_path_blocked_in_has_pdf(self, tmp_path):
        """PDFStorageManager.has_pdf should handle malicious paths safely."""
        from unittest.mock import patch

        from local_deep_research.research_library.services.pdf_storage_manager import (
            PDFStorageManager,
        )

        manager = PDFStorageManager(
            library_root=tmp_path,
            storage_mode="filesystem",
        )

        # Create a mock document with malicious file_path
        mock_doc = Mock()
        mock_doc.file_type = "pdf"
        mock_doc.file_path = "../../../etc/passwd"

        mock_session = Mock()
        mock_session.query.return_value.filter_by.return_value.first.return_value = None

        # Patch DocumentBlob so its .id attribute is accessible outside SQLAlchemy
        mock_blob_class = Mock()
        with patch(
            "local_deep_research.database.models.library.DocumentBlob",
            mock_blob_class,
        ):
            # Should not raise an error, just return False
            result = manager.has_pdf(mock_doc, mock_session)

        assert result is False

    def test_malicious_document_file_path_blocked_in_load(self, tmp_path):
        """PDFStorageManager._load_from_filesystem should handle malicious paths."""
        from local_deep_research.research_library.services.pdf_storage_manager import (
            PDFStorageManager,
        )

        manager = PDFStorageManager(
            library_root=tmp_path,
            storage_mode="filesystem",
        )

        # Create a mock document with malicious file_path
        mock_doc = Mock()
        mock_doc.file_path = "../../../etc/passwd"

        # Should return None, not raise or attempt to read /etc/passwd
        result = manager._load_from_filesystem(mock_doc)

        assert result is None

    def test_malicious_document_file_path_blocked_in_delete(self, tmp_path):
        """PDFStorageManager.delete_pdf should handle malicious paths safely."""
        from local_deep_research.research_library.services.pdf_storage_manager import (
            PDFStorageManager,
        )

        manager = PDFStorageManager(
            library_root=tmp_path,
            storage_mode="filesystem",
        )

        # Create a mock document with malicious file_path
        mock_doc = Mock()
        mock_doc.file_path = "../../../etc/passwd"
        mock_doc.storage_mode = "filesystem"

        mock_session = Mock()
        mock_session.query.return_value.filter_by.return_value.first.return_value = None

        # Should not raise an error or delete /etc/passwd
        result = manager.delete_pdf(mock_doc, mock_session)

        # Should return True (successful "deletion" since there was nothing to delete)
        # The important thing is it doesn't try to delete outside the library
        assert result is True

    def test_is_already_downloaded_handles_malicious_path(self, tmp_path):
        """DownloadService.is_already_downloaded should not crash on malicious paths."""
        from local_deep_research.research_library.services.download_service import (
            DownloadService,
        )
        from local_deep_research.database.models.download_tracker import (
            DownloadTracker,
        )

        # Create a mock tracker with malicious file_path
        mock_tracker = Mock(spec=DownloadTracker)
        mock_tracker.file_path = "../../../etc/passwd"
        mock_tracker.is_downloaded = True

        mock_session = Mock()
        mock_session.query.return_value.filter_by.return_value.first.return_value = mock_tracker

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with (
            patch(
                "local_deep_research.utilities.db_utils.get_settings_manager",
                return_value=mock_settings,
            ),
            patch(
                "local_deep_research.research_library.services.download_service.get_user_db_session"
            ) as mock_db_session,
        ):
            # Make the context manager return our mock session
            mock_db_session.return_value.__enter__ = Mock(
                return_value=mock_session
            )
            mock_db_session.return_value.__exit__ = Mock(return_value=False)

            service = DownloadService.__new__(DownloadService)
            service.username = "testuser"
            service.password = None

            # Should not raise AttributeError on None.exists()
            # Should return (False, None) because path traversal is blocked
            result = service.is_already_downloaded(
                "https://example.com/doc.pdf"
            )

            assert result == (False, None)


class TestPathTraversalAttackVectors:
    """Test various path traversal attack vectors are blocked."""

    def test_simple_dotdot_traversal(self, tmp_path):
        """Basic ../ traversal should be blocked."""
        from local_deep_research.research_library.utils import (
            get_absolute_path_from_settings,
        )

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            attacks = [
                "../secret.txt",
                "../../secret.txt",
                "../../../etc/passwd",
                "../../../../etc/shadow",
            ]
            for attack in attacks:
                result = get_absolute_path_from_settings(attack)
                assert result is None, f"Attack '{attack}' should be blocked"

    def test_nested_traversal_escaping_root(self, tmp_path):
        """Traversal that escapes library root should be blocked."""
        from local_deep_research.research_library.utils import (
            get_absolute_path_from_settings,
        )

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            # These paths escape the library root
            attacks = [
                "pdfs/../../../etc/passwd",
                "documents/subfolder/../../../../../../tmp/evil",
                "a/b/c/d/../../../../../../../../etc/passwd",
            ]
            for attack in attacks:
                result = get_absolute_path_from_settings(attack)
                assert result is None, f"Attack '{attack}' should be blocked"

    def test_traversal_staying_in_root_is_allowed(self, tmp_path):
        """Traversal that stays within library root should be allowed."""
        from local_deep_research.research_library.utils import (
            get_absolute_path_from_settings,
        )

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            # This path uses .. but stays within library root
            # valid/path/to/../../../outside resolves to "outside" which is in root
            result = get_absolute_path_from_settings(
                "valid/path/to/../../../outside"
            )
            # This is actually safe - resolves to tmp_path/outside
            assert result is not None
            assert result == tmp_path / "outside"

    def test_absolute_path_injection(self, tmp_path):
        """Absolute paths should be blocked."""
        from local_deep_research.research_library.utils import (
            get_absolute_path_from_settings,
        )

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            attacks = [
                "/etc/passwd",
                "/etc/shadow",
                "/root/.ssh/id_rsa",
                "/var/log/auth.log",
            ]
            for attack in attacks:
                result = get_absolute_path_from_settings(attack)
                assert result is None, f"Attack '{attack}' should be blocked"

    def test_dot_current_dir_traversal(self, tmp_path):
        """Traversal using ./ patterns should be blocked when escaping."""
        from local_deep_research.research_library.utils import (
            get_absolute_path_from_settings,
        )

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            attacks = [
                "./../../../etc/passwd",
                "./././../../../etc/passwd",
                "././../secret.txt",
            ]
            for attack in attacks:
                result = get_absolute_path_from_settings(attack)
                assert result is None, f"Attack '{attack}' should be blocked"

    def test_double_slash_traversal(self, tmp_path):
        """Double slashes with traversal should be handled."""
        from local_deep_research.research_library.utils import (
            get_absolute_path_from_settings,
        )

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            # These should be blocked due to .. patterns
            attacks = [
                "..//..//etc/passwd",
                "pdfs//..//..//secret.txt",
            ]
            for attack in attacks:
                result = get_absolute_path_from_settings(attack)
                assert result is None, f"Attack '{attack}' should be blocked"

    def test_backslash_traversal_windows_style(self, tmp_path):
        """Windows-style backslash traversal - safe on Linux, blocked on Windows.

        On Linux, backslashes are valid filename characters, not path separators.
        So '..\\..\\etc\\passwd' is treated as a literal filename, which is safe.
        On Windows, werkzeug's safe_join would block these.
        """
        import sys

        from local_deep_research.research_library.utils import (
            get_absolute_path_from_settings,
        )

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            attacks = [
                "..\\..\\..\\etc\\passwd",
                "..\\..\\windows\\win.ini",
            ]
            for attack in attacks:
                result = get_absolute_path_from_settings(attack)
                if sys.platform == "win32":
                    # On Windows, these should be blocked
                    assert result is None, (
                        f"Attack '{attack}' should be blocked"
                    )
                else:
                    # On Linux, backslashes are literal - path stays in library
                    if result is not None:
                        assert str(tmp_path) in str(result)

    def test_mixed_slash_with_real_traversal(self, tmp_path):
        """Mixed slash patterns with real .. traversal should be blocked."""
        from local_deep_research.research_library.utils import (
            get_absolute_path_from_settings,
        )

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            # These contain real forward-slash traversal that escapes root
            attacks = [
                "pdfs/../../../etc/passwd",
                "subdir/../../../../../../etc/passwd",
            ]
            for attack in attacks:
                result = get_absolute_path_from_settings(attack)
                assert result is None, f"Attack '{attack}' should be blocked"

    def test_mangled_patterns_stay_in_library(self, tmp_path):
        """Mangled patterns like '..../' don't escape library.

        Pattern '....' is treated as a literal directory name, not traversal.
        These patterns are safe because they stay within the library root.
        """
        from local_deep_research.research_library.utils import (
            get_absolute_path_from_settings,
        )

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            # These don't actually traverse - '....' is a literal dirname
            patterns = [
                "....//....//file.pdf",
                "..../file.pdf",
            ]
            for pattern in patterns:
                result = get_absolute_path_from_settings(pattern)
                # These are allowed because they stay in library
                if result is not None:
                    assert str(tmp_path) in str(result)

    def test_null_byte_injection(self, tmp_path):
        """Null byte injection should be blocked or handled safely.

        Null bytes (%00) can truncate paths in some languages (PHP < 5.3.4).
        While Python handles this better, we should still test it.
        """
        from local_deep_research.research_library.utils import (
            get_absolute_path_from_settings,
        )

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            # Null byte injection patterns
            attacks = [
                "../../../etc/passwd\x00.pdf",
                "pdfs/test.pdf\x00/../../../etc/passwd",
                "../../../etc/passwd%00.pdf",  # URL-encoded null (if decoded)
            ]
            for attack in attacks:
                result = get_absolute_path_from_settings(attack)
                assert result is None, (
                    f"Null byte attack '{attack!r}' should be blocked"
                )

    def test_unc_path_injection_windows(self, tmp_path):
        """UNC paths (Windows network shares) should be blocked.

        UNC paths like \\\\server\\share can be used for SSRF or credential theft.
        """
        from local_deep_research.research_library.utils import (
            get_absolute_path_from_settings,
        )

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            # UNC path patterns
            attacks = [
                "\\\\attacker.com\\share\\malicious.pdf",
                "\\\\localhost\\c$\\windows\\win.ini",
                "//attacker.com/share/file.pdf",
            ]
            for attack in attacks:
                result = get_absolute_path_from_settings(attack)
                # Should be blocked or stay within library
                if result is not None:
                    assert str(tmp_path) in str(result), (
                        f"Attack '{attack}' resulted in path outside library"
                    )

    def test_dot_segment_variations(self, tmp_path):
        """Various dot segment patterns should be handled.

        Tests patterns like '...' and '....' which some systems mishandle.
        """
        from local_deep_research.research_library.utils import (
            get_absolute_path_from_settings,
        )

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            # Various dot patterns
            attacks = [
                ".../.../.../etc/passwd",
                "..../..../etc/passwd",
                "..;/..;/..;/etc/passwd",  # Tomcat bypass
            ]
            for attack in attacks:
                result = get_absolute_path_from_settings(attack)
                # These are unusual patterns - should either be blocked or safe
                if result is not None:
                    assert str(tmp_path) in str(result), (
                        f"Attack '{attack}' resulted in path outside library"
                    )


class TestPathTraversalEdgeCases:
    """Test edge cases and boundary conditions."""

    def test_whitespace_paths(self, tmp_path):
        """Paths with only whitespace should be handled."""
        from local_deep_research.research_library.utils import (
            get_absolute_path_from_settings,
        )

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            # Whitespace-only paths should return None or be sanitized
            whitespace_paths = ["   ", "\t", "\n", "  \t\n  "]
            for path in whitespace_paths:
                result = get_absolute_path_from_settings(path)
                # After strip(), these become empty, which should return library_root
                # or None depending on implementation
                assert result is None or result == tmp_path

    def test_path_with_spaces(self, tmp_path):
        """Valid paths with spaces should work."""
        from local_deep_research.research_library.utils import (
            get_absolute_path_from_settings,
        )

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            result = get_absolute_path_from_settings("pdfs/my document.pdf")
            assert result is not None
            assert result == tmp_path / "pdfs/my document.pdf"

    def test_path_with_special_characters(self, tmp_path):
        """Paths with allowed special characters should work."""
        from local_deep_research.research_library.utils import (
            get_absolute_path_from_settings,
        )

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            # Valid special characters
            valid_paths = [
                "pdfs/test-file.pdf",
                "pdfs/test_file.pdf",
                "pdfs/test.file.pdf",
            ]
            for path in valid_paths:
                result = get_absolute_path_from_settings(path)
                assert result is not None, f"Path '{path}' should be allowed"
                assert result == tmp_path / path

    def test_very_long_path(self, tmp_path):
        """Very long paths should be handled without crashing."""
        from local_deep_research.research_library.utils import (
            get_absolute_path_from_settings,
        )

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            # Create a very long but valid path
            long_path = "/".join(["subdir"] * 50) + "/file.pdf"
            result = get_absolute_path_from_settings(long_path)
            # Should either work or fail gracefully (no crash)
            assert result is None or isinstance(result, Path)

    def test_unicode_path(self, tmp_path):
        """Unicode paths should be handled."""
        from local_deep_research.research_library.utils import (
            get_absolute_path_from_settings,
        )

        mock_settings = Mock()
        mock_settings.get_setting.return_value = str(tmp_path)

        with patch(
            "local_deep_research.utilities.db_utils.get_settings_manager",
            return_value=mock_settings,
        ):
            # Unicode filenames are common
            unicode_paths = [
                "pdfs/документ.pdf",  # Russian
                "pdfs/文档.pdf",  # Chinese
                "pdfs/ドキュメント.pdf",  # Japanese
            ]
            for path in unicode_paths:
                # Should not crash - just verify no exception is raised
                _ = get_absolute_path_from_settings(path)
                # Result depends on filesystem support


class TestCascadeHelperPathSafety:
    """Test CascadeHelper path handling."""

    def test_delete_filesystem_file_rejects_special_markers(self):
        """CascadeHelper should reject special path markers."""
        from local_deep_research.research_library.deletion.utils.cascade_helper import (
            CascadeHelper,
        )

        markers = [
            FILE_PATH_METADATA_ONLY,
            FILE_PATH_TEXT_ONLY,
            FILE_PATH_BLOB_DELETED,
        ]
        for marker in markers:
            result = CascadeHelper.delete_filesystem_file(marker)
            assert result is False, f"Marker '{marker}' should be rejected"

    def test_delete_filesystem_file_handles_none(self):
        """CascadeHelper should handle None path."""
        from local_deep_research.research_library.deletion.utils.cascade_helper import (
            CascadeHelper,
        )

        result = CascadeHelper.delete_filesystem_file(None)
        assert result is False

    def test_delete_filesystem_file_handles_empty_string(self):
        """CascadeHelper should handle empty string path."""
        from local_deep_research.research_library.deletion.utils.cascade_helper import (
            CascadeHelper,
        )

        result = CascadeHelper.delete_filesystem_file("")
        assert result is False

    def test_delete_filesystem_file_nonexistent_returns_false(self, tmp_path):
        """CascadeHelper should return False for nonexistent files."""
        from local_deep_research.research_library.deletion.utils.cascade_helper import (
            CascadeHelper,
        )

        result = CascadeHelper.delete_filesystem_file(
            str(tmp_path / "nonexistent.pdf")
        )
        assert result is False

    def test_delete_filesystem_file_deletes_valid_file(self, tmp_path):
        """CascadeHelper should delete valid files."""
        from local_deep_research.research_library.deletion.utils.cascade_helper import (
            CascadeHelper,
        )

        # Create a test file
        test_file = tmp_path / "test.pdf"
        test_file.write_text("test content")
        assert test_file.exists()

        result = CascadeHelper.delete_filesystem_file(str(test_file))

        assert result is True
        assert not test_file.exists()
