//! Path normalization and containment checking.
//!
//! This module provides cross-platform path utilities with special attention to Windows:
//! - Normalizes path separators (backslash vs forward slash)
//! - Handles Windows drive letters (C:\, /c/, etc.)
//! - Handles UNC paths (\\server\share\...)
//! - Handles long path prefixes (\\?\...)
//! - Handles MINGW-style paths (/c/Users/...)
//! - Canonical path resolution (symlinks, junctions)
//! - Containment checking for sandbox enforcement
//!
//! # Security-Critical
//!
//! This module is the security foundation for tool sandboxing. All operations must:
//! - Prevent path traversal attacks
//! - Prevent symlink escape attacks
//! - Handle all Windows path edge cases correctly
//! - Never expose disallowed paths in error messages

pub mod canonicalize;
pub mod containment;
pub mod blocklist;
pub mod validate;

// Re-export public API
pub use canonicalize::{canonicalize_path, SymlinkPolicy};
pub use containment::check_containment;
pub use blocklist::check_blocklist;
pub use validate::validate_path;

use std::path::{Path, PathBuf};

/// Get the basename (final component) of a path for safe error messages.
///
/// This is used to avoid leaking full paths in error messages.
pub fn basename(path: &Path) -> String {
    path.file_name()
        .and_then(|s| s.to_str())
        .map(|s| s.to_string())
        .unwrap_or_else(|| String::new())
}

/// Check if a path is absolute.
///
/// On Windows, this handles:
/// - Standard absolute paths (C:\...)
/// - UNC paths (\\server\share\...)
/// - Long-path prefixes (\\?\...)
/// - Verbatim UNC (\\?\UNC\server\share\...)
pub fn is_absolute(path: &Path) -> bool {
    path.is_absolute()
}

/// Normalize a path to use the platform's standard separator.
///
/// On Windows, this converts forward slashes to backslashes.
/// On Unix, this is a no-op.
pub fn normalize_separators(path: &Path) -> PathBuf {
    #[cfg(windows)]
    {
        let s = path.to_string_lossy();
        let normalized = s.replace('/', "\\");
        PathBuf::from(normalized)
    }

    #[cfg(not(windows))]
    {
        path.to_path_buf()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_basename() {
        assert_eq!(basename(Path::new("/foo/bar/baz.txt")), "baz.txt");
        assert_eq!(basename(Path::new("baz.txt")), "baz.txt");
        assert_eq!(basename(Path::new("/")), "");
    }

    #[cfg(windows)]
    #[test]
    fn test_is_absolute_windows() {
        assert!(is_absolute(Path::new("C:\\Users")));
        assert!(is_absolute(Path::new("C:/Users")));
        assert!(is_absolute(Path::new("\\\\server\\share")));
        assert!(is_absolute(Path::new("\\\\?\\C:\\Users")));
        assert!(!is_absolute(Path::new("relative\\path")));
        assert!(!is_absolute(Path::new("..\\parent")));
    }

    #[cfg(unix)]
    #[test]
    fn test_is_absolute_unix() {
        assert!(is_absolute(Path::new("/home/user")));
        assert!(!is_absolute(Path::new("relative/path")));
        assert!(!is_absolute(Path::new("../parent")));
    }

    #[cfg(windows)]
    #[test]
    fn test_normalize_separators_windows() {
        assert_eq!(
            normalize_separators(Path::new("C:/Users/foo/bar.txt")),
            PathBuf::from("C:\\Users\\foo\\bar.txt")
        );
        assert_eq!(
            normalize_separators(Path::new("C:\\Users\\foo\\bar.txt")),
            PathBuf::from("C:\\Users\\foo\\bar.txt")
        );
    }
}
