<?php
/**
 * CacheLoop WP-CLI Commands
 *
 * Provides CLI commands for cache management, status, and preload.
 *
 * Usage:
 *   wp cacheloop clear
 *   wp cacheloop status
 *   wp cacheloop preload --url=/my-post
 *   wp cacheloop log
 *   wp cacheloop log --clear
 *
 * @package CacheLoop
 */

// Security: Prevent direct file access.
defined( 'ABSPATH' ) || exit;

// This file is loaded by WP-CLI only.
if ( ! defined( 'WP_CLI' ) || ! WP_CLI ) {
	return;
}

/**
 * CacheLoop CLI commands.
 */
class CacheLoop_Command extends WP_CLI_Command {

	/**
	 * Clears all CacheLoop cache.
	 *
	 * ## EXAMPLES
	 *
	 *     wp cacheloop clear
	 *
	 * @subcommand clear
	 */
	public function clear() {
		require_once CACHELOOP_PLUGIN_DIR . 'includes/class-cl-page-cache.php';
		require_once CACHELOOP_PLUGIN_DIR . 'includes/class-cl-rest-cache.php';

		CACHELOOP_Page_Cache::flush_all();
		CACHELOOP_REST_Cache::flush_rest_cache();

		// Clear runtime config to force regeneration.
		$config_path = WP_CONTENT_DIR . '/cache/cacheloop/runtime-config.json';
		if ( file_exists( $config_path ) ) {
			// phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
			unlink( $config_path );
		}

		WP_CLI::success( 'All CacheLoop cache cleared.' );
	}

	/**
	 * Shows CacheLoop status and statistics.
	 *
	 * ## EXAMPLES
	 *
	 *     wp cacheloop status
	 *
	 * @subcommand status
	 */
	public function status() {
		$cache_dir = CACHELOOP_CACHE_DIR;

		WP_CLI::line( '─── CacheLoop Status ───' );
		WP_CLI::line( '' );

		// Cache directory info.
		if ( is_dir( $cache_dir ) ) {
			$file_count = $this->count_files_recursive( $cache_dir );
			$dir_size   = $this->dir_size( $cache_dir );

			WP_CLI::line( sprintf( 'Cache directory: %s', $cache_dir ) );
			WP_CLI::line( sprintf( 'Total files:     %d', $file_count ) );
			WP_CLI::line( sprintf( 'Total size:      %s', $this->format_bytes( $dir_size ) ) );
		} else {
			WP_CLI::line( 'Cache directory: Not found' );
			WP_CLI::line( 'Total files:     0' );
			WP_CLI::line( 'Total size:      0 B' );
		}

		// REST cache.
		$rest_dir = $cache_dir . 'rest/';
		if ( is_dir( $rest_dir ) ) {
			$rest_count = $this->count_files_recursive( $rest_dir );
			$rest_size  = $this->dir_size( $rest_dir );

			WP_CLI::line( '' );
			WP_CLI::line( '─── REST Cache ───' );
			WP_CLI::line( sprintf( 'Total files: %d', $rest_count ) );
			WP_CLI::line( sprintf( 'Total size:  %s', $this->format_bytes( $rest_size ) ) );
		}

		// Debug log.
		$log_file = $cache_dir . 'debug.log';
		if ( file_exists( $log_file ) ) {
			$log_size = filesize( $log_file );
			WP_CLI::line( '' );
			WP_CLI::line( '─── Debug Log ───' );
			WP_CLI::line( sprintf( 'Log size: %s', $this->format_bytes( $log_size ) ) );

			// Ensure logger class is available.
			if ( ! class_exists( 'CACHELOOP_Logger' ) ) {
				require_once CACHELOOP_PLUGIN_DIR . 'includes/class-cl-logger.php';
			}

			WP_CLI::line( 'Last entries:' );
			WP_CLI::line( CACHELOOP_Logger::get_last_lines( 5 ) );
		}

		WP_CLI::line( '' );
		WP_CLI::line( '─── Done ───' );
	}

	/**
	 * Preloads cache for one or more URLs.
	 *
	 * ## OPTIONS
	 *
	 * [<url>...]
	 * : One or more URLs to preload.
	 *
	 * ## EXAMPLES
	 *
	 *     wp cacheloop preload /my-post
	 *     wp cacheloop preload / /about /contact
	 *
	 * @subcommand preload
	 */
	public function preload( $args ) {
		if ( empty( $args ) ) {
			WP_CLI::error( 'Please provide one or more URLs to preload.' );
		}

		$loaded = 0;
		$failed = 0;

		foreach ( $args as $url ) {
			// Convert relative path to full URL.
			if ( strpos( $url, 'http' ) !== 0 ) {
				$url = home_url( $url );
			}

			$response = wp_remote_get(
				$url,
				array(
					'timeout'    => 30,
					'blocking'   => true,
					'sslverify'  => false,
					'user-agent' => 'WP-CLI CacheLoop Preloader',
				)
			);

			if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) >= 400 ) {
				WP_CLI::warning( sprintf( 'Failed: %s', $url ) );
				++$failed;
			} else {
				WP_CLI::line( sprintf( 'Cached: %s', $url ) );
				++$loaded;
			}
		}

		WP_CLI::success(
			sprintf(
				'Preload complete: %d loaded, %d failed.',
				$loaded,
				$failed
			)
		);
	}

	/**
	 * Converts existing media library images to WebP (Pro).
	 *
	 * Only new uploads are optimized automatically; this command backfills
	 * images that were already in the library before Image Optimization was
	 * turned on. Safe to re-run — already-optimized images are skipped.
	 *
	 * ## OPTIONS
	 *
	 * [--batch=<number>]
	 * : How many images to convert before reporting progress. Default 20.
	 *
	 * ## EXAMPLES
	 *
	 *     wp cacheloop optimize-images
	 *     wp cacheloop optimize-images --batch=50
	 *
	 * @subcommand optimize-images
	 */
	public function optimize_images( $args, $assoc_args ) {
		if ( ! function_exists( 'cacheloop_fs' ) || ! cacheloop_fs()->can_use_premium_code() ) {
			WP_CLI::error( 'Image Optimization is a CacheLoop Pro feature.' );
		}

		$class_file = CACHELOOP_PLUGIN_DIR . 'includes/class-cl-image-optimizer__premium_only.php';
		if ( ! file_exists( $class_file ) ) {
			WP_CLI::error( 'Image Optimizer class not found.' );
		}
		require_once $class_file;

		// Defensive: make sure the tracking table exists even if this runs
		// before the plugin's own activation/upgrade hook ever fired.
		require_once CACHELOOP_PLUGIN_DIR . 'includes/class-cl-image-optimize-db.php';
		CACHELOOP_Image_Optimize_DB::create_table();

		$settings  = get_option( 'cacheloop_settings', array() );
		$optimizer = new CACHELOOP_Image_Optimizer( $settings );

		$ids = $optimizer->find_unoptimized_ids();

		if ( empty( $ids ) ) {
			WP_CLI::success( 'All images are already optimized.' );
			return;
		}

		$batch_report = isset( $assoc_args['batch'] ) ? max( 1, (int) $assoc_args['batch'] ) : 20;
		$total        = count( $ids );

		WP_CLI::log( sprintf( 'Optimizing %d image(s)…', $total ) );
		$progress = \WP_CLI\Utils\make_progress_bar( 'Converting', $total );

		$done = 0;
		foreach ( $ids as $attachment_id ) {
			$optimizer->optimize_attachment( (int) $attachment_id );
			++$done;
			$progress->tick();

			if ( 0 === $done % $batch_report ) {
				WP_CLI::log( sprintf( '  %d / %d done…', $done, $total ) );
			}
		}

		$progress->finish();
		WP_CLI::success( sprintf( 'Optimized %d image(s).', $done ) );
	}

	/**
	 * Generates Critical CSS (Pro).
	 *
	 * ## OPTIONS
	 *
	 * <action>
	 * : Currently only "generate" is supported.
	 *
	 * [--all]
	 * : Generate for the front page plus one sample published post per public post type.
	 *
	 * [--url=<url>]
	 * : Generate for a single URL only.
	 *
	 * ## EXAMPLES
	 *
	 *     wp cacheloop critical-css generate --all
	 *     wp cacheloop critical-css generate --url=https://example.com/sample-post/
	 *
	 * @subcommand critical-css
	 */
	public function critical_css( $args, $assoc_args ) {
		if ( ! function_exists( 'cacheloop_fs' ) || ! cacheloop_fs()->can_use_premium_code() ) {
			WP_CLI::error( 'Critical CSS is a CacheLoop Pro feature.' );
		}

		if ( empty( $args[0] ) || 'generate' !== $args[0] ) {
			WP_CLI::error( 'Usage: wp cacheloop critical-css generate [--all|--url=<url>]' );
		}

		$class_file = CACHELOOP_PLUGIN_DIR . 'includes/class-cl-critical-css__premium_only.php';
		if ( ! file_exists( $class_file ) ) {
			WP_CLI::error( 'Critical CSS class not found.' );
		}
		require_once $class_file;

		$has_url = ! empty( $assoc_args['url'] );
		$has_all = isset( $assoc_args['all'] );

		if ( ! $has_url && ! $has_all ) {
			WP_CLI::error( 'Specify --all or --url=<url>.' );
		}

		$settings = get_option( 'cacheloop_settings', array() );
		$critical = new CACHELOOP_Critical_CSS( $settings );

		$targets = array();

		if ( $has_url ) {
			$url = esc_url_raw( $assoc_args['url'] );
			$key = $critical->resolve_template_key_from_url( $url );
			if ( ! $key ) {
				// Not recognized as the front page or a singular post — still
				// let the user warm an arbitrary URL under its own bucket.
				$key = 'url-' . substr( md5( $url ), 0, 12 );
			}
			$targets[ $key ] = $url;
		}

		if ( $has_all ) {
			$targets['front_page'] = home_url( '/' );

			foreach ( get_post_types( array( 'public' => true ), 'names' ) as $post_type ) {
				if ( 'attachment' === $post_type ) {
					continue;
				}
				$sample = get_posts(
					array(
						'post_type'      => $post_type,
						'posts_per_page' => 1,
						'post_status'    => 'publish',
						'orderby'        => 'ID',
						'order'          => 'DESC',
					)
				);
				if ( ! empty( $sample ) ) {
					$targets[ 'single-' . $post_type ] = get_permalink( $sample[0] );
				}
			}
		}

		if ( empty( $targets ) ) {
			WP_CLI::error( 'Nothing to generate.' );
		}

		WP_CLI::log( sprintf( 'Generating critical CSS for %d template(s)…', count( $targets ) ) );

		foreach ( $targets as $key => $url ) {
			WP_CLI::log( sprintf( '  %-30s %s', $key, $url ) );
			$critical->generate_for_template( $key, $url );
		}

		WP_CLI::success( sprintf( 'Critical CSS generated for %d template(s).', count( $targets ) ) );
	}

	/**
	 * Shows or clears the debug log.
	 *
	 * ## OPTIONS
	 *
	 * [--clear]
	 * : Clear the debug log file.
	 *
	 * ## EXAMPLES
	 *
	 *     wp cacheloop log
	 *     wp cacheloop log --clear
	 *
	 * @subcommand log
	 */
	public function log( $args, $assoc_args ) {
		if ( isset( $assoc_args['clear'] ) ) {
			CACHELOOP_Logger::clear_log();
			WP_CLI::success( 'Debug log cleared.' );
			return;
		}

		$log_content = CACHELOOP_Logger::get_last_lines( 100 );
		if ( empty( $log_content ) ) {
			WP_CLI::line( '(No log entries found.)' );
		} else {
			WP_CLI::line( $log_content );
		}
	}

	// ---------------------------------------------------------------------------
	// Helpers
	// ---------------------------------------------------------------------------

	/**
	 * Counts files recursively in a directory.
	 *
	 * @param string $dir Directory path.
	 * @return int File count.
	 */
	private function count_files_recursive( $dir ) {
		$count    = 0;
		$iterator = new RecursiveIteratorIterator(
			new RecursiveDirectoryIterator( $dir, RecursiveDirectoryIterator::SKIP_DOTS )
		);
		foreach ( $iterator as $file ) {
			if ( $file->isFile() ) {
				++$count;
			}
		}
		return $count;
	}

	/**
	 * Calculates directory size in bytes.
	 *
	 * @param string $dir Directory path.
	 * @return int Size in bytes.
	 */
	private function dir_size( $dir ) {
		$size     = 0;
		$iterator = new RecursiveIteratorIterator(
			new RecursiveDirectoryIterator( $dir, RecursiveDirectoryIterator::SKIP_DOTS )
		);
		foreach ( $iterator as $file ) {
			if ( $file->isFile() ) {
				$size += $file->getSize();
			}
		}
		return $size;
	}

	/**
	 * Formats bytes to human-readable string.
	 *
	 * @param int $bytes Number of bytes.
	 * @return string Formatted string.
	 */
	private function format_bytes( $bytes ) {
		$units = array( 'B', 'KB', 'MB', 'GB' );
		$i     = 0;
		while ( $bytes >= 1024 && $i < 3 ) {
			$bytes /= 1024;
			++$i;
		}
		return round( $bytes, 2 ) . ' ' . $units[ $i ];
	}
}

// Register the command.
WP_CLI::add_command( 'cacheloop', 'CacheLoop_Command' );
