<?php

namespace App\Http\Traits;

use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;

/**
 * Trait FileSanitizationTrait
 *
 * Provides utility methods to sanitize file paths and file names to prevent
 * directory traversal, restrict files to a specific base directory, and ensure
 * safe handling of file operations.
 */
trait FileSanitizationTrait
{
    /**
     * Sanitize file path by removing directory traversal and dangerous characters.
     *
     * @param  string  $filePath  The file path to sanitize.
     * @param  string  $basePath  The base directory to restrict the file path.
     * @return string The sanitized file path.
     *
     * @throws \Exception If the file path is invalid or unsafe.
     */
    public function sanitizeFilePath(string $filePath, string $basePath): string
    {
        // Handle empty file path.
        if (empty($filePath)) {
            throw new \Exception('Invalid file path. File path cannot be empty.');
        }

        // Ensure that the path doesn't end with a slash.
        if (substr($filePath, -1) === '/') {
            throw new \Exception('Invalid file path. Trailing slashes are not allowed.');
        }

        // Ensure that the base path is an absolute path.
        $basePath = rtrim($basePath, '/\\');

        if (! File::isDirectory($basePath)) {
            throw new \Exception('Base path is not a valid directory.');
        }

        // Ensure the file path does not start with invalid characters or patterns.
        $filePath = ltrim($filePath, '/\\');

        // Strip special characters (other than alphanumerics, dots, underscores, hyphens, and slashes).
        $filePath = preg_replace('/[^a-zA-Z0-9_\-\/.]/', '', $filePath);

        // Check for relative path elements like '..' (dot-dot) and '.' (dot).
        if (strpos($filePath, '..') !== false || strpos($filePath, './') !== false) {
            throw new \Exception('Invalid file path. Relative path elements like ".." or "./" are not allowed.');
        }

        // Resolve the full path
        $fullPath = realpath($filePath) ?: (realpath($basePath) . '/' . basename($filePath));

        // Validate that the resolved path exists and is within the allowed base path.
        if (! realpath($fullPath) || ! str_starts_with($fullPath, $basePath)) {
            throw new \Exception('Invalid file path.');
        }

        return $fullPath;
    }

    /**
     * Sanitize file name to ensure it contains only safe characters.
     *
     * This method removes any directory components and allows only
     * alphanumeric characters, dots, underscores, and hyphens in the file name.
     *
     * @param  string  $fileName  The file name to sanitize.
     * @return string The sanitized file name.
     */
    public function sanitizeFileName(string $fileName): string
    {
        // Extract the file name without any directory path components.
        $baseName = Str::afterLast($fileName, '/');

        // Remove invalid characters, keeping only alphanumerics, dots, underscores, and hyphens.
        return preg_replace('/[^a-zA-Z0-9_\-\.]/', '', $baseName);
    }
}
