"""
Path security utilities for preventing path traversal attacks and sanitizing error output.

This module provides functions to validate and normalize file paths, ensuring they:
1. Don't contain path traversal sequences (../)
2. Resolve to actual filesystem locations (no symlink attacks)
3. Are readable by the current process
"""
import os
import re
import logging
from pathlib import Path

_logger = logging.getLogger(__name__)


def validate_and_normalize_path(target_path: str) -> str:
    """
    Validate and normalize a file path to prevent path traversal attacks.

    This function:
    1. Resolves the path to an absolute path (eliminates .. and . components)
    2. Resolves symlinks to their actual targets
    3. Validates the path exists and is readable

    Args:
        target_path: User-provided file or directory path

    Returns:
        Normalized absolute path as string

    Raises:
        ValueError: If path is invalid, doesn't exist, or isn't readable

    Examples:
        >>> # Basic validation
        >>> validate_and_normalize_path("/home/user/project/file.py")
        '/home/user/project/file.py'

        >>> # Path traversal blocked
        >>> validate_and_normalize_path("../../etc/passwd")
        ValueError: Path does not exist

        >>> # Symlinks resolved
        >>> validate_and_normalize_path("/tmp/link-to-file")
        '/home/user/real-file.py'
    """
    try:
        # Resolve to absolute path, eliminating .. components and symlinks
        # This prevents path traversal attacks like ../../etc/passwd
        resolved_path = Path(target_path).resolve(strict=True)

        # Verify the path is readable
        if not os.access(resolved_path, os.R_OK):
            raise ValueError(f"Path is not readable: {resolved_path}")

        return str(resolved_path)

    except FileNotFoundError:
        raise ValueError(f"Path does not exist: {target_path}")
    except RuntimeError as e:
        # Can occur with infinite symlink loops
        raise ValueError(f"Invalid path (possible symlink loop): {target_path}")
    except Exception as e:
        raise ValueError(f"Invalid path: {target_path} ({str(e)})")


def sanitize_error_message(error: Exception, context: str = "Operation") -> str:
    """Sanitize error message to remove internal paths and sensitive details.

    Logs the full error internally, returns a safe message for the client.
    """
    _logger.error(f"{context} failed: {error}", exc_info=True)
    msg = str(error)
    # Remove Windows absolute paths (C:\...) and UNC paths (\\server\share)
    msg = re.sub(r'[A-Za-z]:[\\\/][^\s:]*', '[path]', msg)
    msg = re.sub(r'\\\\[^\s:]+', '[path]', msg)
    # Remove all Unix absolute paths (any /something/...)
    msg = re.sub(r'/[^\s:]*', '[path]', msg)
    if len(msg) > 200:
        msg = msg[:200] + '...'
    return msg
