"""Unit tests for path validator."""

from pathlib import Path

import pytest

from webdrop_bridge.core.validator import PathValidator, ValidationError


class TestPathValidator:
    """Test path validation."""

    def test_validator_initialization(self, tmp_path):
        """Test creating a validator with valid roots."""
        dir1 = tmp_path / "dir1"
        dir2 = tmp_path / "dir2"
        dir1.mkdir()
        dir2.mkdir()

        validator = PathValidator([dir1, dir2])

        assert len(validator.allowed_roots) == 2

    def test_validator_nonexistent_root(self, tmp_path):
        """Test that nonexistent root is logged as warning but doesn't raise error."""
        nonexistent = tmp_path / "nonexistent"

        # Should not raise - just logs warning and skips the root
        validator = PathValidator([nonexistent])
        assert len(validator.allowed_roots) == 0  # Non-existent roots are skipped

    def test_validator_non_directory_root(self, tmp_path):
        """Test that non-directory root raises ValidationError."""
        file_path = tmp_path / "file.txt"
        file_path.write_text("test")

        with pytest.raises(ValidationError, match="not a directory"):
            PathValidator([file_path])

    def test_validate_valid_file(self, tmp_path):
        """Test validating a file within allowed root."""
        file_path = tmp_path / "test.txt"
        file_path.write_text("test content")

        validator = PathValidator([tmp_path])

        assert validator.validate(file_path) is True
        assert validator.is_valid(file_path) is True

    def test_validate_nonexistent_file(self, tmp_path):
        """Test that nonexistent file raises ValidationError."""
        validator = PathValidator([tmp_path])
        nonexistent = tmp_path / "nonexistent.txt"

        with pytest.raises(ValidationError, match="does not exist"):
            validator.validate(nonexistent)

    def test_validate_directory_path(self, tmp_path):
        """Test that directory path raises ValidationError."""
        subdir = tmp_path / "subdir"
        subdir.mkdir()

        validator = PathValidator([tmp_path])

        with pytest.raises(ValidationError, match="not a regular file"):
            validator.validate(subdir)

    def test_validate_file_outside_roots(self, tmp_path):
        """Test that file outside allowed roots raises ValidationError."""
        allowed_dir = tmp_path / "allowed"
        other_dir = tmp_path / "other"
        allowed_dir.mkdir()
        other_dir.mkdir()

        file_in_other = other_dir / "test.txt"
        file_in_other.write_text("test")

        validator = PathValidator([allowed_dir])

        with pytest.raises(ValidationError, match="not within allowed roots"):
            validator.validate(file_in_other)

    def test_validate_multiple_roots(self, tmp_path):
        """Test validating files in multiple allowed roots."""
        dir1 = tmp_path / "dir1"
        dir2 = tmp_path / "dir2"
        dir1.mkdir()
        dir2.mkdir()

        file1 = dir1 / "file1.txt"
        file2 = dir2 / "file2.txt"
        file1.write_text("content1")
        file2.write_text("content2")

        validator = PathValidator([dir1, dir2])

        assert validator.validate(file1) is True
        assert validator.validate(file2) is True

    def test_validate_with_relative_path(self, tmp_path):
        """Test validating with relative path (gets resolved)."""
        import os

        file_path = tmp_path / "test.txt"
        file_path.write_text("test")

        validator = PathValidator([tmp_path])

        # Change to tmp_path directory
        original_cwd = os.getcwd()
        try:
            os.chdir(tmp_path)
            # Use relative path
            assert validator.validate(Path("test.txt")) is True
        finally:
            os.chdir(original_cwd)

    def test_validate_with_path_traversal(self, tmp_path):
        """Test that path traversal attacks are blocked."""
        allowed_dir = tmp_path / "allowed"
        other_dir = tmp_path / "other"
        allowed_dir.mkdir()
        other_dir.mkdir()

        file_in_other = other_dir / "secret.txt"
        file_in_other.write_text("secret")

        validator = PathValidator([allowed_dir])

        # Try to access file outside root using ..
        traversal_path = allowed_dir / ".." / "other" / "secret.txt"

        with pytest.raises(ValidationError, match="not within allowed roots"):
            validator.validate(traversal_path)

    def test_is_valid_doesnt_raise(self, tmp_path):
        """Test that is_valid() never raises exceptions."""
        validator = PathValidator([tmp_path])

        # These should all return False, not raise
        assert validator.is_valid(Path("/nonexistent")) is False
        assert validator.is_valid(tmp_path) is False  # Directory, not file

        # Valid file should return True
        file_path = tmp_path / "test.txt"
        file_path.write_text("test")
        assert validator.is_valid(file_path) is True


class TestPathValidatorEdgeCases:
    """Test edge cases in path validation."""

    def test_symlink_to_valid_file(self, tmp_path):
        """Test validating a symlink to a valid file."""
        # Skip on Windows if symlink creation fails
        actual_file = tmp_path / "actual.txt"
        actual_file.write_text("content")

        try:
            symlink = tmp_path / "link.txt"
            symlink.symlink_to(actual_file)

            validator = PathValidator([tmp_path])
            # Symlinks resolve to their target, should validate
            assert validator.validate(symlink) is True

        except (OSError, NotImplementedError):
            # Skip if symlinks not supported
            pytest.skip("Symlinks not supported on this platform")

    def test_nested_files_in_allowed_root(self, tmp_path):
        """Test validating files in nested subdirectories."""
        nested_dir = tmp_path / "a" / "b" / "c"
        nested_dir.mkdir(parents=True)

        nested_file = nested_dir / "file.txt"
        nested_file.write_text("content")

        validator = PathValidator([tmp_path])

        assert validator.validate(nested_file) is True
