"""Path validation for secure file operations."""

import logging
from pathlib import Path
from typing import List, Optional

logger = logging.getLogger(__name__)


class ValidationError(Exception):
    """Raised when path validation fails."""

    pass


class PathValidator:
    """Validates file paths against security whitelist.

    Ensures that only files within allowed root directories can be accessed.
    All paths are resolved to absolute form before validation to prevent
    directory traversal attacks.
    """

    def __init__(self, allowed_roots: List[Path], check_file_exists: bool = True):
        """Initialize validator with allowed root directories.

        Args:
            allowed_roots: List of Path objects representing allowed root dirs
            check_file_exists: Whether to validate that files exist

        Raises:
            ValidationError: If any root doesn't exist or isn't a directory
        """
        self.allowed_roots = []
        self.check_file_exists = check_file_exists

        for root in allowed_roots:
            root_path = Path(root).resolve()
            if not root_path.exists():
                logger.warning(f"Allowed root '{root}' does not exist")
            elif not root_path.is_dir():
                raise ValidationError(f"Allowed root '{root}' is not a directory")
            else:
                self.allowed_roots.append(root_path)

    def validate(self, path: Path) -> bool:
        """Validate that path is within an allowed root directory.

        Args:
            path: File path to validate

        Returns:
            True if path is valid and accessible

        Raises:
            ValidationError: If path fails validation
        """
        try:
            # Resolve to absolute path (handles symlinks, .., etc)
            file_path = Path(path).resolve()
        except (OSError, ValueError) as e:
            raise ValidationError(f"Cannot resolve path '{path}': {e}") from e

        # Check file exists if required
        if self.check_file_exists:
            if not file_path.exists():
                raise ValidationError(f"File does not exist: {path}")

            # Check it's a regular file (not directory, symlink to dir, etc)
            if not file_path.is_file():
                raise ValidationError(f"Path is not a regular file: {path}")

        # Check path is within an allowed root (if roots configured)
        if self.allowed_roots:
            for allowed_root in self.allowed_roots:
                try:
                    # This raises ValueError if file_path is not relative to root
                    file_path.relative_to(allowed_root)
                    return True
                except ValueError:
                    continue

            # Not in any allowed root
            raise ValidationError(
                f"Path '{file_path}' is not within allowed roots: "
                f"{self.allowed_roots}"
            )

        return True

    def is_valid(self, path: Path) -> bool:
        """Check if path is valid without raising exception.

        Args:
            path: File path to check

        Returns:
            True if valid, False otherwise
        """
        try:
            return self.validate(path)
        except ValidationError:
            return False
