"""
Tests targeting specific uncovered lines in path_validator.py.

Covers:
- Line 140: Malformed Windows path on win32
- Line 153: safe_join returns None for unix absolute path
- Lines 157-162: Windows absolute path handling (drive letter parsing)
- Lines 185-186: Windows system directory restrictions
- Line 274: validate_model_path when validate_safe_path returns None
- Line 308: validate_data_path when validate_safe_path returns None
- Lines 375-385: Absolute config path validation (suffix + existence)
- Line 400: Relative config path when validate_safe_path returns None
"""

from pathlib import Path
from unittest.mock import patch

import pytest

from local_deep_research.security.path_validator import PathValidator


# ---------------------------------------------------------------------------
# Line 140: Malformed Windows path on win32
# The function does `import sys` locally (line 112), so we patch `sys.platform`
# on the actual sys module.
# ---------------------------------------------------------------------------
class TestMalformedWindowsPath:
    """Line 140: raise ValueError('Malformed Windows path input')"""

    @patch("sys.platform", "win32")
    def test_malformed_windows_path_slash_colon(self):
        """'/C:/Windows' on win32 triggers the malformed path guard."""
        with pytest.raises(ValueError, match="Malformed Windows path input"):
            PathValidator.validate_local_filesystem_path("/C:/Windows")

    @patch("sys.platform", "win32")
    def test_malformed_windows_path_backslash_colon(self):
        """'\\C:\\Windows' on win32 triggers the malformed path guard."""
        with pytest.raises(ValueError, match="Malformed Windows path input"):
            PathValidator.validate_local_filesystem_path("\\C:\\Windows")


# ---------------------------------------------------------------------------
# Line 153: safe_join returns None for unix absolute path
# ---------------------------------------------------------------------------
class TestSafeJoinReturnsNoneUnix:
    """Line 153: raise ValueError('Invalid path - failed security validation')"""

    @patch(
        "local_deep_research.security.path_validator.safe_join",
        return_value=None,
    )
    def test_safe_join_none_for_absolute_path(self, mock_safe_join):
        with pytest.raises(ValueError, match="failed security validation"):
            PathValidator.validate_local_filesystem_path("/home/user/docs")


# ---------------------------------------------------------------------------
# Lines 157-162: Windows absolute path handling (drive letter parsing)
# On Linux, a path like 'C:\Users\test' is not absolute (no leading /),
# so it enters the drive-letter branch at line 155.
# ---------------------------------------------------------------------------
class TestWindowsDriveLetterParsing:
    """Lines 157-162: Windows drive-letter absolute path branch."""

    @patch("local_deep_research.security.path_validator.safe_join")
    def test_windows_drive_letter_path_success(self, mock_safe_join):
        """Drive-letter path where safe_join succeeds (lines 157-162)."""
        mock_safe_join.return_value = "/tmp/fake_resolved"
        result = PathValidator.validate_local_filesystem_path("C:\\Users\\test")
        assert result is not None

    @patch(
        "local_deep_research.security.path_validator.safe_join",
        return_value=None,
    )
    def test_windows_drive_letter_safe_join_none(self, mock_safe_join):
        """Drive-letter path where safe_join returns None (line 160-161)."""
        with pytest.raises(ValueError, match="failed security validation"):
            PathValidator.validate_local_filesystem_path("C:\\Users\\test")


# ---------------------------------------------------------------------------
# Lines 185-186: Windows system directory restrictions
# When sys.platform == "win32" and restricted_dirs is None, extra Windows
# system dirs are added. We mock safe_join and Path.resolve so that the
# validated path falls inside one of those restricted dirs.
# ---------------------------------------------------------------------------
class TestWindowsSystemDirRestrictions:
    """Lines 184-193: Windows restricted directories added on win32."""

    @patch("sys.platform", "win32")
    @patch("local_deep_research.security.path_validator.safe_join")
    def test_windows_restricted_dirs_block_access(self, mock_safe_join):
        """On win32, C:\\Windows is restricted by default."""
        # The path 'C:\Windows\System32\cmd.exe' has len>2, [1]==':',
        # so it enters the drive-letter branch.
        # It does NOT start with / or \, so the malformed guard (line 135-140)
        # is skipped.
        fake = Path("C:\\Windows\\System32\\cmd.exe")
        mock_safe_join.return_value = str(fake)

        with patch.object(Path, "resolve", return_value=fake):
            with patch.object(Path, "is_relative_to", return_value=True):
                with pytest.raises(
                    ValueError, match="Cannot access system directories"
                ):
                    PathValidator.validate_local_filesystem_path(
                        "C:\\Windows\\System32\\cmd.exe"
                    )


# ---------------------------------------------------------------------------
# Line 274: validate_model_path - validate_safe_path returns None
# ---------------------------------------------------------------------------
class TestValidateModelPathNone:
    """Line 274: raise ValueError('Invalid model path')"""

    @patch.object(PathValidator, "validate_safe_path", return_value=None)
    def test_model_path_returns_none(self, mock_vsp):
        with pytest.raises(ValueError, match="Invalid model path"):
            PathValidator.validate_model_path(
                "bad_model.bin", model_root="/tmp"
            )


# ---------------------------------------------------------------------------
# Line 308: validate_data_path - validate_safe_path returns None
# ---------------------------------------------------------------------------
class TestValidateDataPathNone:
    """Line 308: raise ValueError('Invalid data path')"""

    @patch.object(PathValidator, "validate_safe_path", return_value=None)
    def test_data_path_returns_none(self, mock_vsp):
        with pytest.raises(ValueError, match="Invalid data path"):
            PathValidator.validate_data_path("bad_file.dat", data_root="/tmp")


# ---------------------------------------------------------------------------
# Lines 375-385: Absolute config path validation
# safe_join("/", config_path) returns None when config_path starts with "/",
# so on Unix these lines are unreachable through normal flow.
# We mock safe_join to return a value so we can exercise lines 375-385.
# ---------------------------------------------------------------------------
class TestAbsoluteConfigPath:
    """Lines 375-385: absolute config path suffix and existence checks."""

    @patch("local_deep_research.security.path_validator.safe_join")
    def test_absolute_config_bad_extension(self, mock_safe_join):
        """Line 378-379: suffix not in CONFIG_EXTENSIONS."""
        mock_safe_join.return_value = "/tmp/config.exe"
        with pytest.raises(ValueError, match="Invalid config file type"):
            PathValidator.validate_config_path("/tmp/config.exe")

    @patch("local_deep_research.security.path_validator.safe_join")
    def test_absolute_config_does_not_exist(self, mock_safe_join, tmp_path):
        """Line 382-383: valid extension but file does not exist."""
        nonexistent = str(tmp_path / "missing.json")
        mock_safe_join.return_value = nonexistent
        with pytest.raises(ValueError, match="Config file not found"):
            PathValidator.validate_config_path(nonexistent)

    @patch("local_deep_research.security.path_validator.safe_join")
    def test_absolute_config_valid(self, mock_safe_join, tmp_path):
        """Lines 375-385: happy path - valid extension, file exists."""
        cfg = tmp_path / "settings.yaml"
        cfg.write_text("key: value")
        cfg_str = str(cfg)
        mock_safe_join.return_value = cfg_str
        result = PathValidator.validate_config_path(cfg_str)
        assert result == Path(cfg_str)


# ---------------------------------------------------------------------------
# Line 400: Relative config path - validate_safe_path returns None
# ---------------------------------------------------------------------------
class TestRelativeConfigPathNone:
    """Line 400: raise ValueError('Invalid config path: ...')"""

    @patch.object(PathValidator, "validate_safe_path", return_value=None)
    def test_relative_config_path_none(self, mock_vsp):
        with pytest.raises(ValueError, match="Invalid config path"):
            PathValidator.validate_config_path(
                "my_config.yaml", config_root="/tmp"
            )
