<?php
/**
 * File Path: includes/admin/tabs/imp-exp/class-mbs-csv-temp-manager.php
 *
 * CSV Import — Temp File Manager
 *
 * Static utility class responsible for all temporary file and directory
 * lifecycle management during CSV imports. Extracted from
 * MBS_Tab_Child_Import_CSV (REF-21) so the utilities can be tested and
 * called independently of the AJAX handler class.
 *
 * @package MyBookShowroom
 * @since   1.0.0
 */

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

class MBS_CSV_Temp_Manager { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- mbs_/MBS_ prefix is correct; scanner not configured with plugin prefix

	/**
	 * Resolve the temp directory path for CSV uploads.
	 *
	 * C-004: Uses the WordPress get_temp_dir() wrapper for sys_get_temp_dir()
	 * rather than the wp-content/uploads/ tree. The system temp directory is
	 * not served by the web server on any platform.
	 *
	 * @return string Absolute path with trailing slash.
	 */
	public static function get_temp_dir(): string {
		// get_temp_dir() always returns a path with a trailing slash.
		return get_temp_dir() . 'mbs-temp-import/';
	}

	/**
	 * Remove stale temp CSV files for the current user.
	 *
	 * C-008: Called at the start of each new upload so files orphaned by
	 * abandoned mid-flow sessions do not accumulate indefinitely. Only
	 * files belonging to the current user (by user ID prefix) are removed,
	 * and only those older than 35 minutes (5-minute buffer beyond the
	 * 30-minute transient window).
	 *
	 * @param string $dir The temp directory path.
	 * @return void
	 */
	public static function cleanup_stale_temp_files( string $dir ): void {
		if ( ! is_dir( $dir ) ) {
			return;
		}

		$cutoff = time() - ( 35 * MINUTE_IN_SECONDS );
		$uid    = get_current_user_id();

		// Remove stale flat import files (CSV, XLSX, ZIP) for this user.
		// Covers both the legacy 'csv_import_' prefix and the current 'import_' prefix.
		$patterns = array(
			$dir . 'csv_import_' . $uid . '_*.csv',
			$dir . 'import_'     . $uid . '_*.csv',
			$dir . 'import_'     . $uid . '_*.xlsx',
			$dir . 'import_'     . $uid . '_*.zip',
			$dir . 'xlsx_' . $uid . '_*.csv', // ST-022 fix: user ID prefix prevents cross-admin cleanup race.
		);

		foreach ( $patterns as $pattern ) {
			foreach ( glob( $pattern ) ?: array() as $file ) {
				if ( is_file( $file ) && filemtime( $file ) < $cutoff ) {
					// phpcs:ignore WordPress.PHP.NoSilencedErrors -- cleanup; error not actionable
					wp_delete_file( $file );
				}
			}
		}

		// Remove stale ZIP extraction subdirectories for this user.
		foreach ( glob( $dir . 'zip_' . $uid . '_*', GLOB_ONLYDIR ) ?: array() as $subdir ) {
			if ( filemtime( $subdir ) < $cutoff ) {
				self::rmdir_recursive( $subdir );
			}
		}
	}

	/**
	 * Recursively delete a directory and all its contents.
	 *
	 * @param string $dir Absolute path to the directory to remove.
	 * @return void
	 */
	public static function rmdir_recursive( string $dir ): void {
		if ( ! is_dir( $dir ) ) {
			return;
		}
		// Use WP_Filesystem when available (PC-FILESYS).
		global $wp_filesystem;
		if ( ! empty( $wp_filesystem ) && is_object( $wp_filesystem ) ) {
			$wp_filesystem->delete( $dir, true );
			return;
		}
		// Fallback: manual recursive delete when WP_Filesystem is not initialized.
		$entries = scandir( $dir );
		foreach ( $entries ?: array() as $entry ) {
			if ( '.' === $entry || '..' === $entry ) {
				continue;
			}
			$path = $dir . DIRECTORY_SEPARATOR . $entry;
			if ( is_dir( $path ) ) {
				self::rmdir_recursive( $path );
			} else {
				// phpcs:ignore WordPress.PHP.NoSilencedErrors -- cleanup; error not actionable
				wp_delete_file( $path );
			}
		}
		// phpcs:ignore WordPress.PHP.NoSilencedErrors, WordPress.WP.AlternativeFunctions.file_system_operations_rmdir -- fallback when WP_Filesystem unavailable; safe (temp dir, never inside wp-content)
		@rmdir( $dir );
	}
}
