<?php
/**
 * Freefactu Filesystem Helper Class
 *
 * Provides a wrapper around WP_Filesystem API for safe file operations.
 *
 * @package Freefactu
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class Freefactu_Filesystem
 *
 * Handles all file system operations using WordPress WP_Filesystem API.
 */
class Freefactu_Filesystem {

	/**
	 * WP_Filesystem instance.
	 *
	 * @var WP_Filesystem_Base|null
	 */
	private static $wp_filesystem = null;

	/**
	 * Initialize the WP_Filesystem.
	 *
	 * What this function does:
	 * - Initializes WordPress WP_Filesystem API
	 * - Caches the filesystem instance for reuse
	 * - Loads required WordPress file functions if needed
	 * - Returns true if initialization succeeds
	 *
	 * Where it's called from:
	 * - class-freefactu-filesystem.php: All public methods (put_contents, get_contents, delete, exists, is_dir, rmdir)
	 *
	 * @return bool True on success, false on failure.
	 */
	public static function init() {
		global $wp_filesystem;

		if ( self::$wp_filesystem !== null ) {
			return true;
		}

		if ( ! function_exists( 'WP_Filesystem' ) ) {
			require_once ABSPATH . 'wp-admin/includes/file.php';
		}

		$result = WP_Filesystem();

		if ( $result && $wp_filesystem ) {
			self::$wp_filesystem = $wp_filesystem;
			return true;
		}

		return false;
	}

	/**
	 * Write contents to a file.
	 *
	 * What this function does:
	 * - Writes content to a file using WP_Filesystem API
	 * - Sets proper file permissions (FS_CHMOD_FILE)
	 * - Falls back to direct file_put_contents if WP_Filesystem fails
	 * - Returns true on success, false on failure
	 *
	 * Where it's called from:
	 * - class-freefactu-logger.php: init() method for security files (.htaccess, index.php)
	 * - class-freefactu-logger.php: clear_logs() method
	 * - abstract-base-generator.php: save_pdf() method
	 * - abstract-base-generator.php: save_regenerated_pdf() method
	 * - class-freefactu-client.php: save_pdf_snapshot_by_id() method
	 * - And many other locations for file writing
	 *
	 * @param string $file     Path to the file.
	 * @param string $contents Contents to write.
	 * @return bool True on success, false on failure.
	 */
	public static function put_contents( $file, $contents ) {
		if ( ! self::init() ) {
			// Fallback to direct file write if WP_Filesystem fails
			return @file_put_contents( $file, $contents ) !== false;
		}

		return self::$wp_filesystem->put_contents( $file, $contents, FS_CHMOD_FILE );
	}

	/**
	 * Read contents from a file.
	 *
	 * What this function does:
	 * - Reads entire file content using WP_Filesystem API
	 * - Falls back to direct file_get_contents if WP_Filesystem fails
	 * - Returns file contents as string or false on failure
	 *
	 * Where it's called from:
	 * - abstract-base-generator.php: get_saved_pdf() method
	 *
	 * @param string $file Path to the file.
	 * @return string|false File contents or false on failure.
	 */
	public static function get_contents( $file ) {
		if ( ! self::init() ) {
			// Fallback to direct file read if WP_Filesystem fails
			return @file_get_contents( $file );
		}

		return self::$wp_filesystem->get_contents( $file );
	}

	/**
	 * Create a directory.
	 *
	 * What this function does:
	 * - Creates a directory with proper permissions
	 * - Uses wp_mkdir_p which handles parent directories automatically
	 * - Sets correct permissions for WordPress environment
	 *
	 * Where it's called from:
	 * - class-freefactu-logger.php: init() method
	 * - abstract-base-generator.php: save_pdf() method
	 * - abstract-base-generator.php: save_regenerated_pdf() method
	 * - class-freefactu-client.php: save_pdf_snapshot_by_id() method
	 *
	 * @param string $path Path to the directory.
	 * @return bool True on success, false on failure.
	 */
	public static function mkdir( $path ) {
		// Use wp_mkdir_p which handles permissions correctly
		return wp_mkdir_p( $path );
	}

	/**
	 * Delete a file.
	 *
	 * What this function does:
	 * - Deletes a file using WP_Filesystem API
	 * - Falls back to wp_delete_file if WP_Filesystem fails
	 * - Returns true on success, false on failure
	 *
	 * Where it's called from:
	 * - Not currently used in the codebase (available for future use)
	 *
	 * @param string $file Path to the file.
	 * @return bool True on success, false on failure.
	 */
	public static function delete( $file ) {
		if ( ! self::init() ) {
			// Fallback to wp_delete_file if WP_Filesystem fails
			return wp_delete_file( $file );
		}

		return self::$wp_filesystem->delete( $file );
	}

	/**
	 * Check if a file exists.
	 *
	 * What this function does:
	 * - Checks if a file or directory exists using WP_Filesystem API
	 * - Falls back to direct file_exists if WP_Filesystem fails
	 * - Returns true if exists, false otherwise
	 *
	 * Where it's called from:
	 * - class-freefactu-logger.php: init() method
	 * - class-freefactu-logger.php: get_logs() method
	 * - class-freefactu-logger.php: clear_logs() method
	 * - class-freefactu-logger.php: check_directory_writable() method
	 * - abstract-base-generator.php: save_pdf() method
	 * - abstract-base-generator.php: get_saved_pdf() method
	 * - class-freefactu-history.php: render_history_page() method
	 * - And many other locations for file existence checks
	 *
	 * @param string $file Path to the file.
	 * @return bool True if file exists, false otherwise.
	 */
	public static function exists( $file ) {
		if ( ! self::init() ) {
			return file_exists( $file );
		}

		return self::$wp_filesystem->exists( $file );
	}

	/**
	 * Check if path is a directory.
	 *
	 * What this function does:
	 * - Checks if a path is a directory using WP_Filesystem API
	 * - Falls back to direct is_dir if WP_Filesystem fails
	 * - Returns true if directory, false otherwise
	 *
	 * Where it's called from:
	 * - class-freefactu-logger.php: check_directory_writable() method
	 *
	 * @param string $path Path to check.
	 * @return bool True if directory, false otherwise.
	 */
	public static function is_dir( $path ) {
		if ( ! self::init() ) {
			return is_dir( $path );
		}

		return self::$wp_filesystem->is_dir( $path );
	}

	/**
	 * Check if a file or directory is writable.
	 *
	 * What this function does:
	 * - Checks if a file or directory is writable using WP_Filesystem API
	 * - Falls back to direct is_writable if WP_Filesystem fails
	 * - Returns true if writable, false otherwise
	 *
	 * @param string $path Path to check.
	 * @return bool True if writable, false otherwise.
	 */
	public static function is_writable( $path ) {
		if ( ! self::init() ) {
			return wp_is_writable( $path );
		}

		return self::$wp_filesystem->is_writable( $path );
	}

	/**
	 * Delete a directory recursively.
	 *
	 * What this function does:
	 * - Deletes a directory and all its contents recursively
	 * - Uses WP_Filesystem API with recursive flag
	 *
	 * Where it's called from:
	 * - uninstall.php: Plugin uninstallation to remove plugin directories
	 *
	 * @param string $path Path to the directory.
	 * @return bool True on success, false on failure.
	 */
	public static function rmdir( $path ) {
		if ( ! self::init() ) {
			// If WP_Filesystem cannot be initialized, we cannot safely delete directories recursively
			// honoring WordPress standards (avoiding direct rmdir).
			return false;
		}

		return self::$wp_filesystem->rmdir( $path, true );
	}

	/**
	 * Convert an absolute path to a relative path (relative to uploads directory).
	 *
	 * What this function does:
	 * - Takes an absolute filesystem path
	 * - Removes the wp_upload_dir basedir prefix
	 * - Returns a path relative to uploads, making it portable across hosting environments
	 * - Returns empty string if path is empty or not within uploads
	 * - Normalizes slashes to forward slashes for cross-platform compatibility
	 *
	 * Where it's called from:
	 * - abstract-base-generator.php: save_pdf() method when storing PDF path
	 * - class-freefactu-client.php: When saving PDF paths to database
	 * - class-freefactu-admin.php: When saving PDF paths after regeneration
	 *
	 * @param string $absolute_path The absolute filesystem path.
	 * @return string Relative path or empty string if invalid.
	 */
	public static function to_relative_path( $absolute_path ) {
		if ( empty( $absolute_path ) ) {
			return '';
		}

		$upload_dir = wp_upload_dir();
		$base_dir = $upload_dir['basedir'];

		// Normalize slashes for comparison (handle Windows backslashes)
		$normalized_path = str_replace( '\\', '/', $absolute_path );
		$normalized_base = str_replace( '\\', '/', $base_dir );

		// Check if path starts with the uploads base directory
		if ( strpos( $normalized_path, $normalized_base ) === 0 ) {
			// Remove base directory and leading slash
			$relative = substr( $normalized_path, strlen( $normalized_base ) );
			return ltrim( $relative, '/' );
		}

		// Path is not within uploads directory - return as-is for backwards compatibility
		// This handles legacy absolute paths that exist before this change
		return $absolute_path;
	}

	/**
	 * Convert a relative path to an absolute path (prepends uploads directory).
	 *
	 * What this function does:
	 * - Takes a relative path (relative to uploads directory)
	 * - Prepends the wp_upload_dir basedir
	 * - Returns the full absolute filesystem path
	 * - Handles both old absolute paths (for backwards compatibility) and new relative paths
	 * - Returns empty string if path is empty
	 *
	 * Where it's called from:
	 * - class-freefactu-admin.php: handle_download_invoice() when serving PDFs
	 * - class-freefactu-admin.php: handle_standalone_pdf_output() when serving PDFs
	 * - class-freefactu-history.php: When checking if PDF exists
	 * - abstract-base-generator.php: get_saved_pdf() when reading stored PDFs
	 *
	 * @param string $relative_path The relative path or absolute path.
	 * @return string Absolute filesystem path.
	 */
	public static function to_absolute_path( $relative_path ) {
		if ( empty( $relative_path ) ) {
			return '';
		}

		// Check if it's already an absolute path (backwards compatibility with existing data)
		// On Windows: starts with drive letter (C:\) or UNC path (\\)
		// On Unix: starts with /
		// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_is_writable
		$is_absolute = (
			preg_match( '/^[A-Za-z]:[\\\\\\/]/', $relative_path ) || // Windows drive letter
			strpos( $relative_path, '\\\\' ) === 0 ||                  // Windows UNC path
			strpos( $relative_path, '/' ) === 0                        // Unix absolute path
		);

		if ( $is_absolute ) {
			// Already absolute, return as-is
			return $relative_path;
		}

		// Convert relative to absolute
		$upload_dir = wp_upload_dir();
		return $upload_dir['basedir'] . '/' . $relative_path;
	}

	/**
	 * Check if a path is relative (not absolute).
	 *
	 * @param string $path The path to check.
	 * @return bool True if relative, false if absolute.
	 */
	public static function is_relative_path( $path ) {
		if ( empty( $path ) ) {
			return true;
		}

		// Check for absolute path patterns
		// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_is_writable
		$is_absolute = (
			preg_match( '/^[A-Za-z]:[\\\\\\/]/', $path ) || // Windows drive letter
			strpos( $path, '\\\\' ) === 0 ||                  // Windows UNC path
			strpos( $path, '/' ) === 0                        // Unix absolute path
		);

		return ! $is_absolute;
	}
}
