<?php
namespace R2_Offload;

defined( 'ABSPATH' ) || exit;

final class Plugin {

	private static ?Plugin $instance = null;

	public Settings $settings;
	public Logger $logger;
	public Storage\Storage_Client $storage;
	public Media\Offloader $offloader;
	public Media\URL_Rewriter $rewriter;
	public Admin\Admin $admin;
	public Admin\Ajax $ajax;
	public Queue\Scheduler $queue;
	public Migration\Scanner $scanner;
	public Migration\Migrator $migrator;
	public Migration\Verifier $verifier;
	public Media\Pruner $pruner;

	public static function instance(): Plugin {
		if ( self::$instance === null ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	public function boot(): void {
		load_plugin_textdomain( 'r2-offload', false, dirname( R2_OFFLOAD_BASENAME ) . '/languages' );

		DB::maybe_upgrade();

		$this->settings  = new Settings();
		$this->logger    = new Logger();
		$this->storage   = Storage\Storage_Factory::make( $this->settings, $this->logger );
		$this->queue     = new Queue\Scheduler( $this->logger );
		$this->offloader = new Media\Offloader( $this->storage, $this->settings, $this->logger, $this->queue );
		$this->rewriter  = new Media\URL_Rewriter( $this->settings );
		$this->scanner   = new Migration\Scanner( $this->settings, $this->logger );
		$this->migrator  = new Migration\Migrator( $this->queue, $this->offloader, $this->logger );
		$this->verifier  = new Migration\Verifier( $this->storage, $this->settings, $this->logger );
		$this->pruner    = new Media\Pruner( $this->storage, $this->settings, $this->logger );

		if ( is_admin() ) {
			$this->admin = new Admin\Admin( $this->settings, $this->logger, $this->storage, $this->scanner, $this->migrator );
			$this->admin->register();
			$this->ajax = new Admin\Ajax( $this->scanner, $this->migrator, $this->verifier, $this->pruner, $this->settings );
			$this->ajax->register();
		}

		if ( defined( 'WP_CLI' ) && WP_CLI ) {
			require_once R2_OFFLOAD_DIR . 'includes/CLI/Commands.php';
		}

		$this->offloader->register();
		$this->rewriter->register();
		$this->queue->register();
		$this->migrator->register();

		// Force a clean TLS 1.2 handshake for storage-provider requests. Fixes
		// cURL 35 (SSL23_GET_SERVER_HELLO handshake failure) on hosts with an old
		// OpenSSL whose auto-negotiated ClientHello strict endpoints (e.g.
		// Cloudflare R2) reject. Scoped by host so other plugins are untouched.
		add_action( 'http_api_curl', [ $this, 'force_tls12_for_providers' ], 10, 3 );
	}

	/** @param \CurlHandle|resource $handle */
	public function force_tls12_for_providers( $handle, array $args, string $url ): void {
		if ( ! defined( 'CURL_SSLVERSION_TLSv1_2' ) ) {
			return;
		}
		$host = (string) wp_parse_url( $url, PHP_URL_HOST );
		if ( $host === '' ) {
			return;
		}
		$provider_hosts = [
			'r2.cloudflarestorage.com',
			'amazonaws.com',
			'backblazeb2.com',
			'wasabisys.com',
			'digitaloceanspaces.com',
			'storage.bunnycdn.com',
			'storage.googleapis.com',
		];
		foreach ( $provider_hosts as $suffix ) {
			if ( $host === $suffix || str_ends_with( $host, '.' . $suffix ) ) {
				curl_setopt( $handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2 );
				return;
			}
		}
	}
}
