<?php
defined( 'ABSPATH' ) || exit;

if ( ! defined( 'WP_CLI' ) || ! WP_CLI ) {
	return;
}

/**
 * Manage BW WebP conversions from the command line.
 */
class BW_WebP_Cli {

	private BW_WebP_Manifest $manifest;
	private BW_WebP_Settings $settings;
	private BW_WebP_Queue $queue;

	public function __construct( BW_WebP_Manifest $manifest, BW_WebP_Settings $settings, BW_WebP_Queue $queue ) {
		$this->manifest = $manifest;
		$this->settings = $settings;
		$this->queue    = $queue;
	}

	/**
	 * Show the current converter, counts, and settings.
	 *
	 * ## EXAMPLES
	 *     wp bw-webp status
	 */
	public function status(): void {
		$cfg = $this->settings->get();
		try {
			$active = BW_WebP_Converter_Factory::detect( $cfg['converter'] )->name();
		} catch ( Throwable $e ) {
			$active = 'none';
		}

		WP_CLI::log( 'Active converter: ' . $active );
		WP_CLI::log( 'Available: ' . wp_json_encode( BW_WebP_Converter_Factory::available() ) );
		WP_CLI::log( 'Counts:    ' . wp_json_encode( $this->manifest->counts() ) );
		WP_CLI::log( 'Settings:  ' . wp_json_encode( $cfg ) );
	}

	/**
	 * Scan the uploads directory and update the manifest.
	 *
	 * ## EXAMPLES
	 *     wp bw-webp scan
	 */
	public function scan(): void {
		$pending = $this->queue->scan();
		WP_CLI::success( sprintf( 'Scan complete. %d pending.', $pending ) );
	}

	/**
	 * Bulk-convert images.
	 *
	 * ## OPTIONS
	 *
	 * [--workers=<n>]
	 * : Number of parallel workers (1–8). Defaults to settings.
	 *
	 * [--quality=<q>]
	 * : Override quality (1–100) for this run only (does not persist).
	 *
	 * [--inline]
	 * : Run conversion inline in this CLI process instead of dispatching async actions.
	 *
	 * ## EXAMPLES
	 *     wp bw-webp convert --workers=8
	 *     wp bw-webp convert --inline --quality=90
	 *
	 * @param array $args
	 * @param array $assoc
	 */
	public function convert( $args, $assoc ): void {
		$this->queue->scan();

		if ( ! empty( $assoc['quality'] ) ) {
			$q = max( 1, min( 100, (int) $assoc['quality'] ) );
			add_filter( 'bw_webp_quality', static function () use ( $q ) {
				return $q;
			} );
		}

		$workers = isset( $assoc['workers'] ) ? max( 1, min( 8, (int) $assoc['workers'] ) ) : null;

		if ( ! empty( $assoc['inline'] ) ) {
			$this->run_inline( $workers ?? (int) $this->settings->get()['workers'] );
			return;
		}

		$started = $this->queue->enqueue_bulk( $workers );
		WP_CLI::success( sprintf( 'Dispatched %d worker(s). Run `wp action-scheduler run --group=bw-webp` to drain, or `wp bw-webp convert --inline` to run inline.', $started ) );
	}

	/**
	 * Truncate the manifest. Does not delete .webp files.
	 *
	 * ## OPTIONS
	 *
	 * [--yes]
	 * : Skip confirmation.
	 *
	 * @param array $args
	 * @param array $assoc
	 */
	public function clear( $args, $assoc ): void {
		WP_CLI::confirm( 'Truncate BW WebP manifest?', $assoc );
		$this->manifest->clear();
		WP_CLI::success( 'Manifest cleared.' );
	}

	private function run_inline( int $workers ): void {
		$counts = $this->manifest->counts();
		$total  = (int) $counts['pending'];
		if ( 0 === $total ) {
			WP_CLI::success( 'Nothing to do.' );
			return;
		}

		$progress = WP_CLI\Utils\make_progress_bar( 'Converting', $total );

		// Inline mode runs sequentially in the CLI process. Useful for debugging or
		// when Action Scheduler isn't trusted on the host.
		while ( true ) {
			$before = $this->manifest->counts();
			if ( 0 === (int) $before['pending'] ) {
				break;
			}
			$this->queue->process_batch( 0 );
			$after = $this->manifest->counts();
			$delta = (int) $before['pending'] - (int) $after['pending'];
			for ( $i = 0; $i < $delta; $i++ ) {
				$progress->tick();
			}
			if ( $delta <= 0 ) {
				break;
			}
		}

		$progress->finish();
		WP_CLI::success( 'Inline conversion complete: ' . wp_json_encode( $this->manifest->counts() ) );
	}
}
