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

class BW_WebP_Converter_Cwebp implements BW_WebP_Converter {

	private string $binary;

	public function __construct( ?string $binary = null ) {
		$this->binary = $binary ?: self::find_binary();
	}

	public static function is_available(): bool {
		return self::find_binary() !== '';
	}

	public function name(): string {
		return 'cwebp';
	}

	public function convert( string $src, string $dest, int $quality ): void {
		bw_webp_assert_safe_paths( $src, $dest );

		$quality = max( 1, min( 100, $quality ) );

		$cmd = array(
			$this->binary,
			'-q', (string) $quality,
			'-mt',
			'-quiet',
			'-o', $dest,
			'--', $src,
		);

		$descriptors = array(
			0 => array( 'pipe', 'r' ),
			1 => array( 'pipe', 'w' ),
			2 => array( 'pipe', 'w' ),
		);

		$proc = proc_open( $cmd, $descriptors, $pipes, null, null, array( 'bypass_shell' => true ) );
		if ( ! is_resource( $proc ) ) {
			throw new RuntimeException( 'cwebp: failed to start process' );
		}

		fclose( $pipes[0] );
		stream_get_contents( $pipes[1] );
		$stderr = stream_get_contents( $pipes[2] );
		fclose( $pipes[1] );
		fclose( $pipes[2] );

		$exit = proc_close( $proc );
		if ( 0 !== $exit ) {
			throw new RuntimeException(
				sprintf( 'cwebp exit %d: %s', $exit, trim( (string) $stderr ) )
			);
		}

		if ( ! is_file( $dest ) || filesize( $dest ) === 0 ) {
			throw new RuntimeException( 'cwebp: destination missing or empty after run' );
		}
	}

	private static function find_binary(): string {
		$candidates = array( '/usr/bin/cwebp', '/usr/local/bin/cwebp', '/opt/homebrew/bin/cwebp' );
		foreach ( $candidates as $path ) {
			if ( is_executable( $path ) ) {
				return $path;
			}
		}

		$which = trim( (string) shell_exec( 'command -v cwebp 2>/dev/null' ) );
		if ( '' !== $which && is_executable( $which ) ) {
			return $which;
		}

		return '';
	}
}
