<?php
/**
 * Plugin bootstrap and lifecycle logic.
 *
 * @package NaveenCodesImageOptimizer
 */

namespace NaveenCodesImageOptimizer;

defined( 'ABSPATH' ) || exit;

/**
 * Creates service instances and sets activation/deactivation routines.
 */
class Init {

	/**
	 * Option key for the site API token.
	 */
	const API_KEY_OPTION = 'naveencodes_engine_api_key';

	/**
	 * Singleton instance.
	 *
	 * @var Init|null
	 */
	protected static $instance = null;

	/**
	 * Settings service.
	 *
	 * @var Settings
	 */
	protected $settings;

	/**
	 * Logger.
	 *
	 * @var Logger
	 */
	protected $logger;

	/**
	 * Queue.
	 *
	 * @var Queue
	 */
	protected $queue;

	/**
	 * API client.
	 *
	 * @var Api_Client
	 */
	protected $api_client;

	/**
	 * Optimizer.
	 *
	 * @var Optimizer
	 */
	protected $optimizer;

	/**
	 * Admin UI.
	 *
	 * @var Admin|null
	 */
	protected $admin = null;

	/**
	 * Retrieve singleton.
	 *
	 * @return Init
	 */
	public static function get_instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}

		return self::$instance;
	}

	/**
	 * Constructor.
	 */
	protected function __construct() {
		$this->logger     = new Logger();
		$this->settings   = new Settings();
		$this->queue      = new Queue( $this->logger );
		$this->api_client = new Api_Client( $this->settings, $this->logger );
		$this->optimizer  = new Optimizer( $this->settings, $this->api_client, $this->queue, $this->logger );

		new Lazyload( $this->settings );
		new Review_Notice();

		add_filter( 'cron_schedules', array( $this, 'register_cron_schedule' ) );
		add_action( 'admin_init', array( $this, 'maybe_provision_site_key' ) );

		if ( is_admin() ) {
			$this->admin = new Admin( $this->settings, $this->queue, $this->logger, $this->optimizer, $this->api_client );
		}

		$this->ensure_cron_schedule();
	}

	/**
	 * Plugin activation.
	 *
	 * @param bool $network_wide Network activation.
	 * @return void
	 */
	public static function activate( $network_wide ) {
		if ( is_multisite() && $network_wide ) {
			$site_ids = get_sites( array( 'fields' => 'ids' ) );

			foreach ( $site_ids as $site_id ) {
				switch_to_blog( $site_id );
				self::activate_site();
				restore_current_blog();
			}

			return;
		}

		self::activate_site();
	}

	/**
	 * Plugin deactivation.
	 *
	 * @param bool $network_wide Network deactivation.
	 * @return void
	 */
	public static function deactivate( $network_wide ) {
		if ( is_multisite() && $network_wide ) {
			$site_ids = get_sites( array( 'fields' => 'ids' ) );

			foreach ( $site_ids as $site_id ) {
				switch_to_blog( $site_id );
				wp_clear_scheduled_hook( 'naveencodes_process_queue_event' );
				restore_current_blog();
			}

			return;
		}

		wp_clear_scheduled_hook( 'naveencodes_process_queue_event' );
	}

	/**
	 * Register a five-minute cron schedule.
	 *
	 * @param array $schedules Registered schedules.
	 * @return array
	 */
	public function register_cron_schedule( $schedules ) {
		return self::add_cron_schedule( $schedules );
	}

	/**
	 * Add the custom schedule definition.
	 *
	 * @param array $schedules Registered schedules.
	 * @return array
	 */
	public static function add_cron_schedule( $schedules ) {
		$schedules['naveencodes_every_five_minutes'] = array(
			'interval' => 5 * MINUTE_IN_SECONDS,
			'display'  => __( 'Every Five Minutes', 'naveencodes-image-optimizer' ),
		);

		return $schedules;
	}

	/**
	 * Ensure the background cron event exists.
	 *
	 * @return void
	 */
	protected function ensure_cron_schedule() {
		if ( ! wp_next_scheduled( 'naveencodes_process_queue_event' ) ) {
			wp_schedule_event( time() + MINUTE_IN_SECONDS, 'naveencodes_every_five_minutes', 'naveencodes_process_queue_event' );
		}
	}

	/**
	 * Perform site-level activation tasks.
	 *
	 * @return void
	 */
	protected static function activate_site() {
		$logger = new Logger();
		$queue  = new Queue( $logger );

		$queue->create_table();
		add_filter( 'cron_schedules', array( __CLASS__, 'add_cron_schedule' ) );

		if ( ! get_option( Settings::OPTION_KEY, false ) ) {
			add_option( Settings::OPTION_KEY, Settings::get_defaults(), '', false );
		}

		if ( ! get_option( Settings::STATS_OPTION, false ) ) {
			add_option( Settings::STATS_OPTION, Settings::get_default_stats(), '', false );
		}

		if ( ! get_option( Settings::ACTIVATED_OPTION, false ) ) {
			add_option( Settings::ACTIVATED_OPTION, time(), '', false );
		}

		if ( ! wp_next_scheduled( 'naveencodes_process_queue_event' ) ) {
			wp_schedule_event( time() + MINUTE_IN_SECONDS, 'naveencodes_every_five_minutes', 'naveencodes_process_queue_event' );
		}
	}

	/**
	 * Auto-provision the engine API key for this site.
	 *
	 * @return void
	 */
	public function maybe_provision_site_key() {
		$this->provision_site_key();
	}

	/**
	 * Request or refresh a site-scoped API token.
	 *
	 * @param bool $force Force a new provisioning attempt.
	 * @return true|\WP_Error
	 */
	public function provision_site_key( $force = false ) {
		if ( ! current_user_can( 'manage_options' ) ) {
			return new \WP_Error( 'naveencodes_forbidden', __( 'You are not allowed to register this website.', 'naveencodes-image-optimizer' ) );
		}

		if ( $force ) {
			delete_option( self::API_KEY_OPTION );
			delete_transient( 'naveencodes_provision_attempt' );
		}

		$existing_api_key = $this->get_existing_api_key();

		if ( '' !== $existing_api_key ) {
			if ( '' === (string) get_option( self::API_KEY_OPTION, '' ) ) {
				$this->persist_api_key( $existing_api_key );
			}

			return true;
		}

		if ( get_transient( 'naveencodes_provision_attempt' ) ) {
			return new \WP_Error( 'naveencodes_registration_pending', __( 'Registration is already in progress. Please wait a moment and refresh.', 'naveencodes-image-optimizer' ) );
		}

		set_transient( 'naveencodes_provision_attempt', 1, 15 * MINUTE_IN_SECONDS );

		$headers = array(
			'Content-Type' => 'application/json',
			'Accept'       => 'application/json',
		);

		if ( '' !== $existing_api_key ) {
			$headers['x-api-key'] = $existing_api_key;
		}

		$response = wp_remote_post(
			untrailingslashit( NAVEENCODES_ENGINE_URL ) . '/register-site',
			array(
				'timeout' => 20,
				'headers' => $headers,
				'body'    => wp_json_encode(
					array(
						'site_url' => esc_url_raw( home_url( '/' ) ),
					)
				),
			)
		);

		if ( is_wp_error( $response ) ) {
			$this->logger->log(
				'warning',
				'Website registration failed.',
				array(
					'error' => $response->get_error_message(),
				)
			);
			return $response;
		}

		if ( 200 !== (int) wp_remote_retrieve_response_code( $response ) ) {
			$message = __( 'The registration request did not complete successfully.', 'naveencodes-image-optimizer' );
			$this->logger->log(
				'warning',
				'Website registration returned a non-200 response.',
				array(
					'status' => (string) wp_remote_retrieve_response_code( $response ),
				)
			);
			return new \WP_Error( 'naveencodes_registration_failed', $message );
		}

		$body = json_decode( wp_remote_retrieve_body( $response ), true );

		if ( is_array( $body ) && ! empty( $body['api_key'] ) && is_string( $body['api_key'] ) ) {
			$api_key = sanitize_text_field( $body['api_key'] );

			if ( strlen( $api_key ) >= 32 ) {
				$this->persist_api_key( $api_key );
				delete_transient( 'naveencodes_provision_attempt' );

				return true;
			}
		}

		$this->logger->log(
			'warning',
			'Website registration returned an invalid payload.',
			array()
		);

		return new \WP_Error(
			'naveencodes_invalid_registration',
			__( 'The registration response was invalid.', 'naveencodes-image-optimizer' )
		);
	}

	/**
	 * Retrieve the current engine API key from the canonical option or legacy settings.
	 *
	 * @return string
	 */
	protected function get_existing_api_key() {
		$api_key = (string) get_option( self::API_KEY_OPTION, '' );

		if ( '' !== $api_key ) {
			return $api_key;
		}

		$settings = get_option( Settings::OPTION_KEY, array() );

		if ( is_array( $settings ) && ! empty( $settings['api_key'] ) ) {
			return sanitize_text_field( (string) $settings['api_key'] );
		}

		return '';
	}

	/**
	 * Persist the engine API key in both the canonical option and settings array.
	 *
	 * @param string $api_key API key value.
	 * @return void
	 */
	protected function persist_api_key( $api_key ) {
		$api_key = sanitize_text_field( (string) $api_key );

		if ( '' === $api_key ) {
			return;
		}

		update_option( self::API_KEY_OPTION, $api_key, false );

		$settings            = get_option( Settings::OPTION_KEY, array() );
		$settings            = is_array( $settings ) ? $settings : array();
		$settings['api_key'] = $api_key;

		update_option( Settings::OPTION_KEY, $settings, false );
	}
}
