<?php
/**
 * Fatal Error Recovery Module
 *
 * Recover from fatal errors by disabling problematic plugins without FTP access.
 *
 * @package BlaminhorEssentials
 */

// Prevent direct access.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Fatal Error Recovery module class
 */
class Blaminhor_Essentials_Module_Fatal_Error_Recovery extends Blaminhor_Essentials_Module {

	/**
	 * Module ID
	 *
	 * @var string
	 */
	protected $id = 'fatal-error-recovery';

	/**
	 * Module name
	 *
	 * @var string
	 */
	protected $name = 'Fatal Error Recovery';

	/**
	 * Recovery key file path
	 *
	 * @var string
	 */
	private $recovery_key_file;

	/**
	 * Initialize module
	 */
	protected function init() {
		$this->name        = __( 'Fatal Error Recovery', 'blaminhor-essentials' );
		$this->description = __( 'Recover from fatal errors by disabling problematic plugins without FTP access.', 'blaminhor-essentials' );

		// Set recovery key file path.
		$upload_dir = wp_upload_dir();
		$this->recovery_key_file = $upload_dir['basedir'] . '/blaminhor-essentials/.recovery-key';
	}

	/**
	 * Get default settings
	 *
	 * @return array
	 */
	protected function get_default_settings() {
		return array(
			'recovery_key'          => '',
			'show_admin_bar'        => false,
			'email_notification'    => true,
			'last_error_email_hash' => '',
			// Map of error_signature => unix_timestamp of last email sent.
			// Used to dedupe notifications within DAY_IN_SECONDS.
			'signature_emails'      => array(),
		);
	}

	/**
	 * Register hooks
	 */
	protected function register_hooks() {
		// Generate recovery key if not exists.
		$this->ensure_recovery_key();

		// Register shutdown handler to catch fatal errors.
		register_shutdown_function( array( $this, 'handle_shutdown' ) );

		// Check if we're in recovery mode.
		add_action( 'init', array( $this, 'check_recovery_mode' ), 1 );

		// Deferred verification (fires via wp-cron after a fatal is logged).
		add_action( 'blaminhor_essentials_fer_verify_and_send', array( $this, 'verify_and_send_email' ), 10, 1 );

		// Admin hooks.
		add_action( 'admin_init', array( $this, 'handle_settings_submission' ) );
		add_action( 'admin_init', array( $this, 'maybe_send_pending_error_email' ) );

		// Admin bar indicator.
		if ( $this->get_setting( 'show_admin_bar', false ) ) {
			add_action( 'admin_bar_menu', array( $this, 'add_admin_bar_item' ), 100 );
		}

		// AJAX handlers.
		add_action( 'wp_ajax_ap_fer_disable_plugin', array( $this, 'ajax_disable_plugin' ) );
		add_action( 'wp_ajax_ap_fer_enable_plugin', array( $this, 'ajax_enable_plugin' ) );
		add_action( 'wp_ajax_ap_fer_regenerate_key', array( $this, 'ajax_regenerate_key' ) );
	}

	/**
	 * Enqueue module assets
	 *
	 * @param string $hook Current admin page hook.
	 */
	public function enqueue_assets( $hook ) {
		if ( strpos( $hook, 'blaminhor-essentials-fatal-error-recovery' ) === false ) {
			return;
		}

		$config = array(
			'nonce'   => wp_create_nonce( 'blaminhor_essentials_admin' ),
			'ajaxurl' => admin_url( 'admin-ajax.php' ),
			'strings' => array(
				'copied'             => __( 'Copied!', 'blaminhor-essentials' ),
				'copy'               => __( 'Copy', 'blaminhor-essentials' ),
				'confirmRegenerate'  => __( 'Are you sure you want to regenerate the recovery key? The old URL will no longer work.', 'blaminhor-essentials' ),
				'keyRegenerated'     => __( 'Recovery key regenerated. Please save the new URL.', 'blaminhor-essentials' ),
				'error'              => __( 'Error:', 'blaminhor-essentials' ),
			),
		);

		wp_add_inline_script(
			'blaminhor-essentials-modules',
			'jQuery(document).ready(function($) {
				var FatalErrorRecovery = {
					config: ' . wp_json_encode( $config ) . ',
					init: function() {
						this.bindEvents();
					},
					bindEvents: function() {
						var self = this;

						// Copy URL.
						$("#ap-fer-copy-url").on("click", function() {
							var $input = $("#ap-fer-url");
							$input.select();
							document.execCommand("copy");
							$(this).text(self.config.strings.copied);
							setTimeout(function() {
								$("#ap-fer-copy-url").html(\'<span class="dashicons dashicons-clipboard ap-mt-xs"></span> \' + self.config.strings.copy);
							}, 2000);
						});

						// Regenerate key.
						$("#ap-fer-regenerate-key").on("click", function() {
							if (!confirm(self.config.strings.confirmRegenerate)) {
								return;
							}

							var $btn = $(this);
							$btn.prop("disabled", true);

							$.post(self.config.ajaxurl, {
								action: "ap_fer_regenerate_key",
								nonce: self.config.nonce
							}, function(response) {
								if (response.success) {
									$("#ap-fer-url").val(response.data.url);
									alert(self.config.strings.keyRegenerated);
								} else {
									alert(self.config.strings.error + " " + response.data);
								}
								$btn.prop("disabled", false);
							});
						});
					}
				};
				FatalErrorRecovery.init();
			});'
		);
	}

	/**
	 * Ensure recovery key exists
	 */
	private function ensure_recovery_key() {
		$key = $this->get_setting( 'recovery_key', '' );

		if ( empty( $key ) ) {
			$key = $this->generate_recovery_key();
			$this->settings['recovery_key'] = $key;
			$this->save_settings( $this->settings );
		}

		// Also store in file for when DB is not accessible.
		$this->store_recovery_key_file( $key );
	}

	/**
	 * Generate a new recovery key
	 *
	 * @return string
	 */
	private function generate_recovery_key() {
		return wp_generate_password( 32, false );
	}

	/**
	 * Store recovery key in file
	 *
	 * @param string $key Recovery key.
	 */
	private function store_recovery_key_file( $key ) {
		$dir = dirname( $this->recovery_key_file );

		if ( ! file_exists( $dir ) ) {
			wp_mkdir_p( $dir );
		}

		// Add .htaccess protection.
		$htaccess = $dir . '/.htaccess';
		if ( ! file_exists( $htaccess ) ) {
			file_put_contents( $htaccess, "Order deny,allow\nDeny from all" );
		}

		// Add index.php protection.
		$index = $dir . '/index.php';
		if ( ! file_exists( $index ) ) {
			file_put_contents( $index, '<?php // Silence is golden.' );
		}

		file_put_contents( $this->recovery_key_file, $key );
	}

	/**
	 * Get recovery key from file
	 *
	 * @return string|false
	 */
	private function get_recovery_key_from_file() {
		if ( file_exists( $this->recovery_key_file ) ) {
			return trim( file_get_contents( $this->recovery_key_file ) );
		}
		return false;
	}

	/**
	 * Handle PHP shutdown to detect fatal errors
	 */
	public function handle_shutdown() {
		$error = error_get_last();

		if ( $error && in_array( $error['type'], array( E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR ), true ) ) {
			$this->log_fatal_error( $error );
		}
	}

	/**
	 * Log fatal error for recovery
	 *
	 * @param array $error Error details.
	 */
	private function log_fatal_error( $error ) {
		$error_data = array(
			'type'          => $error['type'],
			'message'       => $error['message'],
			'file'          => $error['file'],
			'line'          => $error['line'],
			'time'          => time(),
			'plugin'        => $this->detect_plugin_from_error( $error ),
			'url'           => isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '',
			'email_pending' => true,
		);

		// Store in a file since the database might be unavailable.
		$upload_dir = wp_upload_dir();
		$error_file = $upload_dir['basedir'] . '/blaminhor-essentials/.last-error.json';

		file_put_contents( $error_file, wp_json_encode( $error_data ) );

		// Notify the Cache module's drop-in to bypass cache while we're in
		// recovery — serving a cached page while WP itself is broken can hide
		// the issue from the operator and confuse visitors. Sentinel file is
		// removed by the recovery flow once the issue is acknowledged.
		$cache_sentinel_dir = $upload_dir['basedir'] . '/blaminhor-essentials/cache';
		if ( ! is_dir( $cache_sentinel_dir ) ) {
			@mkdir( $cache_sentinel_dir, 0755, true );
		}
		@file_put_contents( $cache_sentinel_dir . '/.fer_recovery', (string) time() );

		// Try to update option too if database is accessible.
		if ( function_exists( 'update_option' ) ) {
			update_option( 'ap_fer_last_error', $error_data, false );
		}

		// Catastrophic errors (parse, OOM, no plugin context) brick PHP entirely
		// and warrant immediate notification. Plugin-scoped errors are deferred
		// behind an HTTP probe so we don't email for caught exceptions that
		// don't actually take the site down.
		if ( $this->is_catastrophic_error( $error ) ) {
			$this->try_send_recovery_email_immediate( $error_data );
		} else {
			$this->defer_email_with_probe( $error_data );
		}
	}

	/**
	 * Decide whether a fatal error must trigger an immediate email
	 * (no probe possible) or can be deferred behind an accessibility probe.
	 *
	 * @param array $error error_get_last() payload.
	 * @return bool
	 */
	private function is_catastrophic_error( $error ) {
		// Parse / core / compile errors prevent PHP from running anything.
		if ( in_array( (int) $error['type'], array( E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR ), true ) ) {
			return true;
		}

		$message = isset( $error['message'] ) ? (string) $error['message'] : '';

		// Out-of-memory: subsequent requests will likely OOM again.
		if ( stripos( $message, 'allowed memory size' ) !== false || stripos( $message, 'out of memory' ) !== false ) {
			return true;
		}

		// Error not located in a plugin → core / theme / mu-plugin issue.
		// These typically affect every request, so probing won't save us.
		if ( ! $this->detect_plugin_from_error( $error ) ) {
			return true;
		}

		return false;
	}

	/**
	 * Defer the recovery email until a verification cron job confirms the site
	 * is actually inaccessible from outside.
	 *
	 * @param array $error_data Error details.
	 */
	private function defer_email_with_probe( $error_data ) {
		if ( ! $this->get_setting( 'email_notification', true ) ) {
			return;
		}

		$hash = $this->error_signature( $error_data );

		// Already emailed for this signature within the dedup window.
		if ( $this->is_signature_deduped( $hash ) ) {
			return;
		}

		// Persist the error data so the cron callback can reach it without
		// rewalking the file/option dance. Transient TTL is generous to allow
		// for low-traffic sites where wp-cron fires late.
		set_transient( 'blaminhor_essentials_fer_pending_' . $hash, $error_data, HOUR_IN_SECONDS );

		// If WP-Cron is disabled and no external cron is wired up, deferral is
		// unreliable. Fall back to the legacy immediate behavior.
		if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) {
			delete_transient( 'blaminhor_essentials_fer_pending_' . $hash );
			$this->try_send_recovery_email_immediate( $error_data );
			return;
		}

		// Pass $wp_error=true (WP 5.7+) to distinguish "duplicate event already
		// scheduled" — a legitimate no-op for our purposes — from a real cron
		// failure that should trigger the immediate fallback.
		$scheduled = wp_schedule_single_event( time() + 30, 'blaminhor_essentials_fer_verify_and_send', array( $hash ), true );

		if ( is_wp_error( $scheduled ) ) {
			$code = $scheduled->get_error_code();
			if ( 'duplicate_event' === $code ) {
				// Already scheduled with the same args → existing run will handle it.
				return;
			}
			delete_transient( 'blaminhor_essentials_fer_pending_' . $hash );
			$this->try_send_recovery_email_immediate( $error_data );
			return;
		}

		// Legacy boolean return path (older WP / vetoed by filter).
		if ( false === $scheduled ) {
			delete_transient( 'blaminhor_essentials_fer_pending_' . $hash );
			$this->try_send_recovery_email_immediate( $error_data );
		}
	}

	/**
	 * Cron callback: probe the home page, send the recovery email only if
	 * the site is actually inaccessible.
	 *
	 * @param string $hash Error signature stored in the pending transient.
	 */
	public function verify_and_send_email( $hash ) {
		if ( ! is_string( $hash ) || '' === $hash ) {
			return;
		}

		$transient_key = 'blaminhor_essentials_fer_pending_' . $hash;
		$error_data    = get_transient( $transient_key );

		if ( ! $error_data || ! is_array( $error_data ) ) {
			return;
		}

		// Single-shot: clear before doing work to avoid concurrent dispatch.
		delete_transient( $transient_key );

		// Re-check dedup in case another path emailed in the meantime.
		if ( $this->is_signature_deduped( $hash ) ) {
			return;
		}

		// Probe the public home page. If it answers 2xx with a real body,
		// the fatal was contained inside a plugin and visitors are unaffected.
		if ( $this->probe_site_is_accessible() ) {
			return;
		}

		$this->send_recovery_email_now( $error_data, $hash );
	}

	/**
	 * Probe the public-facing home page to check whether the site responds.
	 *
	 * @return bool true if the site responds normally, false otherwise.
	 */
	private function probe_site_is_accessible() {
		/**
		 * Filter the URL probed to determine site accessibility after a fatal.
		 *
		 * Useful when the home page is not representative (e.g., a checkout
		 * or member-area path is the one breaking).
		 *
		 * @param string $url Default home_url('/').
		 */
		$url = apply_filters( 'blaminhor_essentials_fer_probe_url', home_url( '/' ) );

		// Cache-bust query arg so a cached 200 doesn't mask a broken origin.
		$url = add_query_arg( '_be_probe', wp_generate_password( 8, false, false ), $url );

		$response = wp_remote_get(
			$url,
			array(
				'timeout'     => 10,
				'redirection' => 3,
				// SSL itself can be the cause of the incident; don't add a
				// second failure mode on top of the probe.
				'sslverify'   => false,
				'user-agent'  => 'Blaminhor-Essentials-Probe/1.0',
				'blocking'    => true,
				'headers'     => array( 'Cache-Control' => 'no-cache' ),
			)
		);

		if ( is_wp_error( $response ) ) {
			return false;
		}

		$code = (int) wp_remote_retrieve_response_code( $response );

		if ( 0 === $code || $code >= 500 ) {
			return false;
		}

		if ( $code >= 200 && $code < 400 ) {
			$body = wp_remote_retrieve_body( $response );
			return strlen( $body ) >= 100;
		}

		// 4xx on the home page is unusual; treat as anomaly.
		return false;
	}

	/**
	 * Send the recovery email through the normal wp_mail path.
	 *
	 * Used by the deferred (post-probe) flow. The shutdown-time path keeps
	 * its own near-duplicate sender because wp_mail is unreliable during
	 * PHP shutdown.
	 *
	 * @param array  $error_data Error details.
	 * @param string $hash       Error signature.
	 */
	private function send_recovery_email_now( $error_data, $hash ) {
		$recovery_key = $this->get_setting( 'recovery_key', '' );
		if ( empty( $recovery_key ) ) {
			$recovery_key = $this->get_recovery_key_from_file();
		}
		if ( empty( $recovery_key ) ) {
			return;
		}

		$admin_email = get_option( 'admin_email' );
		if ( empty( $admin_email ) ) {
			return;
		}

		$recovery_url = add_query_arg( 'ap_recovery', $recovery_key, home_url( '/' ) );
		$site_name    = get_bloginfo( 'name' );

		$subject = sprintf(
			/* translators: %s: site name */
			__( '[%s] Fatal Error Detected - Recovery Link', 'blaminhor-essentials' ),
			$site_name
		);

		$message = sprintf(
			/* translators: 1: site name, 2: error message, 3: file path, 4: line number, 5: plugin name, 6: recovery URL */
			__( "A fatal error was detected on %1\$s.\n\nError: %2\$s\n\nFile: %3\$s (line %4\$d)\n\nSuspected Plugin: %5\$s\n\nRecovery URL:\n%6\$s\n\nUse this link to disable problematic plugins and recover access to your site.\n\nIMPORTANT: Keep this URL secret! Anyone with this link can disable plugins.\n\n--\nBlaminhor Essentials - Fatal Error Recovery", 'blaminhor-essentials' ),
			$site_name,
			$error_data['message'],
			$error_data['file'],
			$error_data['line'],
			! empty( $error_data['plugin'] ) ? $error_data['plugin'] : __( 'Unknown', 'blaminhor-essentials' ),
			$recovery_url
		);

		if ( wp_mail( $admin_email, $subject, $message ) ) {
			$this->mark_signature_sent( $hash );
			$this->mark_pending_error_resolved( $error_data );
		}
	}

	/**
	 * Build a stable signature for an error, used as dedup key.
	 *
	 * @param array $error_data Error details.
	 * @return string md5 hash.
	 */
	private function error_signature( $error_data ) {
		$file    = isset( $error_data['file'] ) ? (string) $error_data['file'] : '';
		$line    = isset( $error_data['line'] ) ? (int) $error_data['line'] : 0;
		$message = isset( $error_data['message'] ) ? substr( (string) $error_data['message'], 0, 200 ) : '';

		return md5( $file . ':' . $line . ':' . $message );
	}

	/**
	 * Whether an email was already sent for this signature within the
	 * dedup window (24h).
	 *
	 * @param string $hash Error signature.
	 * @return bool
	 */
	private function is_signature_deduped( $hash ) {
		$signatures = (array) $this->get_setting( 'signature_emails', array() );

		if ( ! isset( $signatures[ $hash ] ) ) {
			return false;
		}

		return ( time() - (int) $signatures[ $hash ] ) < DAY_IN_SECONDS;
	}

	/**
	 * Mark a signature as emailed and garbage-collect stale entries.
	 *
	 * @param string $hash Error signature.
	 */
	private function mark_signature_sent( $hash ) {
		$signatures = (array) $this->get_setting( 'signature_emails', array() );
		$signatures[ $hash ] = time();

		// GC entries older than 7 days to keep the option lightweight.
		$gc_threshold = 7 * DAY_IN_SECONDS;
		foreach ( $signatures as $h => $ts ) {
			if ( ( time() - (int) $ts ) > $gc_threshold ) {
				unset( $signatures[ $h ] );
			}
		}

		$this->settings['signature_emails']      = $signatures;
		$this->settings['last_error_email_hash'] = $hash; // Kept for backward compat.
		$this->save_settings( $this->settings );
	}

	/**
	 * Clear the pending flag on the persisted error record after a successful
	 * email send.
	 *
	 * @param array $error_data Error details.
	 */
	private function mark_pending_error_resolved( $error_data ) {
		$error_data['email_pending'] = false;
		$error_data['email_sent']    = true;
		update_option( 'ap_fer_last_error', $error_data, false );

		$upload_dir = wp_upload_dir();
		$error_file = $upload_dir['basedir'] . '/blaminhor-essentials/.last-error.json';
		if ( file_exists( $error_file ) ) {
			file_put_contents( $error_file, wp_json_encode( $error_data ) );
		}
	}

	/**
	 * Try to send recovery email immediately during shutdown
	 *
	 * This attempts to send the email before PHP terminates completely.
	 * Uses native PHP mail() as fallback since wp_mail() may not be reliable during shutdown.
	 *
	 * @param array $error_data Error details.
	 */
	private function try_send_recovery_email_immediate( $error_data ) {
		// Read settings via the in-memory copy populated by the parent class
		// constructor — get_option() during shutdown can hit a half-torn-down
		// state, and the previous direct read used the wrong option key anyway.
		if ( ! $this->get_setting( 'email_notification', true ) ) {
			return;
		}

		// Dedup by signature (24h window) — shared with the deferred path.
		$error_hash = $this->error_signature( $error_data );
		if ( $this->is_signature_deduped( $error_hash ) ) {
			return;
		}

		// Get recovery key (settings → file fallback when DB is unavailable).
		$recovery_key = $this->get_setting( 'recovery_key', '' );
		if ( empty( $recovery_key ) ) {
			$recovery_key = $this->get_recovery_key_from_file();
		}

		if ( empty( $recovery_key ) ) {
			return;
		}

		// Build recovery URL.
		$site_url     = get_option( 'siteurl', '' );
		$recovery_url = $site_url ? trailingslashit( $site_url ) . '?ap_recovery=' . $recovery_key : '';

		if ( empty( $recovery_url ) ) {
			return;
		}

		$admin_email = get_option( 'admin_email', '' );
		if ( empty( $admin_email ) ) {
			return;
		}

		$site_name = get_option( 'blogname', 'WordPress' );
		$plugin_name = $error_data['plugin'] ? $error_data['plugin'] : 'Unknown';

		$subject = sprintf( '[%s] Fatal Error Detected - Recovery Link', $site_name );

		$message = sprintf(
			"A fatal error was detected on %s.\n\n" .
			"Error: %s\n\n" .
			"File: %s (line %d)\n\n" .
			"Suspected Plugin: %s\n\n" .
			"Recovery URL:\n%s\n\n" .
			"Use this link to disable problematic plugins and recover access to your site.\n\n" .
			"IMPORTANT: Keep this URL secret! Anyone with this link can disable plugins.\n\n" .
			"--\nBlaminhor Essentials - Fatal Error Recovery",
			$site_name,
			$error_data['message'],
			$error_data['file'],
			$error_data['line'],
			$plugin_name,
			$recovery_url
		);

		// Try wp_mail first, fallback to native mail().
		$sent = false;

		if ( function_exists( 'wp_mail' ) ) {
			// Suppress errors during shutdown.
			$sent = @wp_mail( $admin_email, $subject, $message );
		}

		// Fallback to native PHP mail() if wp_mail failed or unavailable.
		if ( ! $sent && function_exists( 'mail' ) ) {
			$headers = 'From: WordPress <' . $admin_email . '>' . "\r\n";
			$sent = @mail( $admin_email, $subject, $message, $headers );
		}

		if ( $sent ) {
			$this->mark_signature_sent( $error_hash );
			$this->mark_pending_error_resolved( $error_data );
		}
	}

	/**
	 * Detect which plugin caused the error
	 *
	 * @param array $error Error details.
	 * @return string|null Plugin folder name or null.
	 */
	private function detect_plugin_from_error( $error ) {
		$file = $error['file'];
		$plugins_dir = WP_PLUGIN_DIR;

		// Check if error is in a plugin.
		if ( strpos( $file, $plugins_dir ) === 0 ) {
			$relative = substr( $file, strlen( $plugins_dir ) + 1 );
			$parts = explode( '/', $relative );
			if ( ! empty( $parts[0] ) ) {
				return $parts[0];
			}
		}

		return null;
	}

	/**
	 * Check for pending error emails and send them
	 */
	public function maybe_send_pending_error_email() {
		$error_data = $this->get_last_error();

		if ( $error_data && ! empty( $error_data['email_pending'] ) && ! empty( $error_data['plugin'] ) ) {
			$this->send_recovery_email( $error_data );
		}
	}

	/**
	 * Admin-init fallback to send a recovery email when the shutdown-time
	 * send and the deferred cron send both failed (or were skipped).
	 *
	 * Probes the home page first so a stale pending flag — left over after a
	 * fully resolved transient incident — never produces a late false-positive
	 * email. If the site responds normally, we silently clear the pending
	 * flag instead of emailing.
	 *
	 * @param array $error_data Error details.
	 */
	private function send_recovery_email( $error_data ) {
		if ( ! $this->get_setting( 'email_notification', true ) ) {
			return;
		}

		$error_hash = $this->error_signature( $error_data );
		if ( $this->is_signature_deduped( $error_hash ) ) {
			return;
		}

		// Probe before sending: this path only fires after admin_init, so the
		// backend is alive — but the frontend may already be repaired.
		if ( $this->probe_site_is_accessible() ) {
			$this->mark_pending_error_resolved( $error_data );
			return;
		}

		$this->send_recovery_email_now( $error_data, $error_hash );
	}

	/**
	 * Check if we're in recovery mode
	 */
	public function check_recovery_mode() {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( ! isset( $_GET['ap_recovery'] ) ) {
			return;
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$provided_key = sanitize_text_field( wp_unslash( $_GET['ap_recovery'] ) );
		$stored_key = $this->get_setting( 'recovery_key', '' );

		// Also check file-based key.
		if ( empty( $stored_key ) ) {
			$stored_key = $this->get_recovery_key_from_file();
		}

		if ( empty( $stored_key ) || ! hash_equals( $stored_key, $provided_key ) ) {
			wp_die(
				esc_html__( 'Invalid recovery key.', 'blaminhor-essentials' ),
				esc_html__( 'Access Denied', 'blaminhor-essentials' ),
				array( 'response' => 403 )
			);
		}

		// Valid recovery key - show recovery interface.
		$this->display_recovery_interface();
		exit;
	}

	/**
	 * Display the recovery interface
	 */
	private function display_recovery_interface() {
		// Handle plugin actions.
		if ( isset( $_POST['ap_fer_action'] ) && isset( $_POST['ap_fer_nonce'] ) ) {
			if ( wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['ap_fer_nonce'] ) ), 'ap_fer_action' ) ) {
				$action = sanitize_text_field( wp_unslash( $_POST['ap_fer_action'] ) );
				$plugin = isset( $_POST['ap_fer_plugin'] ) ? sanitize_file_name( wp_unslash( $_POST['ap_fer_plugin'] ) ) : '';

				if ( $action === 'disable' && ! empty( $plugin ) ) {
					$this->deactivate_plugin_db( $plugin );
				} elseif ( $action === 'enable' && ! empty( $plugin ) ) {
					$this->activate_plugin_db( $plugin );
				}
			}
		}

		// Get last error.
		$last_error = $this->get_last_error();

		// Get all plugins.
		$plugins = $this->get_all_plugins();

		// Output recovery page.
		$this->render_recovery_page( $last_error, $plugins );
	}

	/**
	 * Get last logged error
	 *
	 * @return array|null
	 */
	private function get_last_error() {
		// Try file first.
		$upload_dir = wp_upload_dir();
		$error_file = $upload_dir['basedir'] . '/blaminhor-essentials/.last-error.json';

		if ( file_exists( $error_file ) ) {
			$data = json_decode( file_get_contents( $error_file ), true );
			if ( $data ) {
				return $data;
			}
		}

		// Try option.
		return get_option( 'ap_fer_last_error', null );
	}

	/**
	 * Get all plugins (active and inactive based on database)
	 *
	 * @return array
	 */
	private function get_all_plugins() {
		$plugins        = array();
		$plugins_dir    = WP_PLUGIN_DIR;
		$active_plugins = get_option( 'active_plugins', array() );

		if ( ! is_dir( $plugins_dir ) ) {
			return $plugins;
		}

		$items = scandir( $plugins_dir );

		foreach ( $items as $item ) {
			if ( $item === '.' || $item === '..' || $item === 'index.php' ) {
				continue;
			}

			// Skip items starting with underscore (legacy)
			if ( substr( $item, 0, 1 ) === '_' ) {
				continue;
			}

			$path = $plugins_dir . '/' . $item;

			// Handle folder-based plugins (e.g., akismet/akismet.php)
			if ( is_dir( $path ) ) {
				$plugin_file = $this->find_plugin_file( $path );
				if ( ! $plugin_file ) {
					continue;
				}

				$plugin_basename = $item . '/' . basename( $plugin_file );
				$is_active       = in_array( $plugin_basename, $active_plugins, true );

				// Get plugin data
				$plugin_data = array( 'Name' => $item );
				if ( function_exists( 'get_plugin_data' ) ) {
					$data = get_plugin_data( $plugin_file, false, false );
					if ( ! empty( $data['Name'] ) ) {
						$plugin_data = $data;
					}
				}

				$plugins[] = array(
					'folder'   => $item,
					'file'     => $plugin_basename,
					'name'     => $plugin_data['Name'],
					'active'   => $is_active,
					'is_self'  => ( $item === 'blaminhor-essentials' ),
				);
			}
			// Handle single-file plugins (e.g., hello.php)
			elseif ( is_file( $path ) && pathinfo( $path, PATHINFO_EXTENSION ) === 'php' ) {
				// Check if it's a valid plugin file
				$content = file_get_contents( $path, false, null, 0, 8192 );
				if ( strpos( $content, 'Plugin Name:' ) === false ) {
					continue;
				}

				$plugin_basename = $item; // e.g., "hello.php"
				$is_active       = in_array( $plugin_basename, $active_plugins, true );

				// Get plugin data
				$plugin_data = array( 'Name' => pathinfo( $item, PATHINFO_FILENAME ) );
				if ( function_exists( 'get_plugin_data' ) ) {
					$data = get_plugin_data( $path, false, false );
					if ( ! empty( $data['Name'] ) ) {
						$plugin_data = $data;
					}
				}

				$plugins[] = array(
					'folder'   => $item, // For single-file plugins, folder = filename
					'file'     => $plugin_basename,
					'name'     => $plugin_data['Name'],
					'active'   => $is_active,
					'is_self'  => false, // Single-file plugins can't be this plugin
				);
			}
		}

		// Sort: active first, then alphabetically.
		usort( $plugins, function( $a, $b ) {
			if ( $a['active'] !== $b['active'] ) {
				return $a['active'] ? -1 : 1;
			}
			return strcasecmp( $a['name'], $b['name'] );
		} );

		return $plugins;
	}

	/**
	 * Find the main plugin file in a directory
	 *
	 * @param string $path Plugin directory path.
	 * @return string|false Full path to plugin file.
	 */
	private function find_plugin_file( $path ) {
		$files = glob( $path . '/*.php' );

		foreach ( $files as $file ) {
			$content = file_get_contents( $file, false, null, 0, 8192 );
			if ( strpos( $content, 'Plugin Name:' ) !== false ) {
				return $file;
			}
		}

		return false;
	}

	/**
	 * Deactivate plugin via database
	 *
	 * @param string $plugin_identifier Plugin folder name or single-file plugin filename.
	 * @return bool
	 */
	private function deactivate_plugin_db( $plugin_identifier ) {
		$active_plugins = get_option( 'active_plugins', array() );
		$path           = WP_PLUGIN_DIR . '/' . $plugin_identifier;

		// Check if it's a single-file plugin (e.g., hello.php)
		if ( is_file( $path ) && pathinfo( $path, PATHINFO_EXTENSION ) === 'php' ) {
			$plugin_basename = $plugin_identifier; // e.g., "hello.php"
		} else {
			// Folder-based plugin
			$plugin_file = $this->find_plugin_file( $path );
			if ( ! $plugin_file ) {
				return false;
			}
			$plugin_basename = $plugin_identifier . '/' . basename( $plugin_file );
		}

		$key = array_search( $plugin_basename, $active_plugins, true );

		if ( false !== $key ) {
			unset( $active_plugins[ $key ] );
			update_option( 'active_plugins', array_values( $active_plugins ) );
			return true;
		}

		return false;
	}

	/**
	 * Activate plugin via database
	 *
	 * @param string $plugin_identifier Plugin folder name or single-file plugin filename.
	 * @return bool
	 */
	private function activate_plugin_db( $plugin_identifier ) {
		$active_plugins = get_option( 'active_plugins', array() );
		$path           = WP_PLUGIN_DIR . '/' . $plugin_identifier;

		// Check if it's a single-file plugin (e.g., hello.php)
		if ( is_file( $path ) && pathinfo( $path, PATHINFO_EXTENSION ) === 'php' ) {
			$plugin_basename = $plugin_identifier; // e.g., "hello.php"
		} else {
			// Folder-based plugin
			$plugin_file = $this->find_plugin_file( $path );
			if ( ! $plugin_file ) {
				return false;
			}
			$plugin_basename = $plugin_identifier . '/' . basename( $plugin_file );
		}

		if ( ! in_array( $plugin_basename, $active_plugins, true ) ) {
			$active_plugins[] = $plugin_basename;
			update_option( 'active_plugins', $active_plugins );
			return true;
		}

		return false;
	}


	/**
	 * Render recovery page
	 *
	 * @param array|null $last_error Last error data.
	 * @param array      $plugins    List of plugins.
	 */
	private function render_recovery_page( $last_error, $plugins ) {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Recovery mode uses secret key authentication
		$recovery_key = isset( $_GET['ap_recovery'] ) ? sanitize_text_field( wp_unslash( $_GET['ap_recovery'] ) ) : '';
		$nonce = wp_create_nonce( 'ap_fer_action' );
		$detected_plugin = $last_error ? $last_error['plugin'] : null;

		header( 'Content-Type: text/html; charset=utf-8' );
		?>
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title><?php esc_html_e( 'Blaminhor Essentials Recovery Mode', 'blaminhor-essentials' ); ?></title>
	<style>
		* { box-sizing: border-box; margin: 0; padding: 0; }
		body {
			font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
			background: #f0f0f1;
			color: #1d2327;
			line-height: 1.5;
			padding: 20px;
		}
		.recovery-container {
			max-width: 800px;
			margin: 0 auto;
		}
		.recovery-header {
			background: #fff;
			border: 1px solid #c3c4c7;
			border-radius: 4px;
			padding: 20px;
			margin-bottom: 20px;
		}
		.recovery-header h1 {
			font-size: 24px;
			margin-bottom: 10px;
			display: flex;
			align-items: center;
			gap: 10px;
		}
		.recovery-header h1 .icon {
			width: 32px;
			height: 32px;
			background: #d63638;
			border-radius: 50%;
			display: flex;
			align-items: center;
			justify-content: center;
			color: #fff;
			font-size: 18px;
		}
		.recovery-header p {
			color: #646970;
		}
		.error-box {
			background: #fcf0f1;
			border: 1px solid #d63638;
			border-radius: 4px;
			padding: 16px;
			margin-bottom: 20px;
		}
		.error-box h3 {
			color: #d63638;
			margin-bottom: 10px;
			font-size: 14px;
		}
		.error-box pre {
			background: #fff;
			padding: 10px;
			border-radius: 4px;
			overflow-x: auto;
			font-size: 12px;
			line-height: 1.4;
		}
		.error-box .error-meta {
			margin-top: 10px;
			font-size: 12px;
			color: #646970;
		}
		.plugins-section {
			background: #fff;
			border: 1px solid #c3c4c7;
			border-radius: 4px;
			padding: 20px;
		}
		.plugins-section h2 {
			font-size: 16px;
			margin-bottom: 15px;
		}
		.plugin-list {
			list-style: none;
		}
		.plugin-item {
			display: flex;
			align-items: center;
			justify-content: space-between;
			padding: 12px 0;
			border-bottom: 1px solid #f0f0f1;
		}
		.plugin-item:last-child {
			border-bottom: none;
		}
		.plugin-item.detected {
			background: #fcf9e8;
			margin: 0 -20px;
			padding: 12px 20px;
			border-left: 4px solid #dba617;
		}
		.plugin-item.disabled {
			opacity: 0.7;
		}
		.plugin-info {
			flex: 1;
		}
		.plugin-name {
			font-weight: 600;
		}
		.plugin-folder {
			font-size: 12px;
			color: #646970;
			font-family: monospace;
		}
		.plugin-badge {
			display: inline-block;
			padding: 2px 8px;
			border-radius: 3px;
			font-size: 11px;
			font-weight: 600;
			text-transform: uppercase;
			margin-left: 8px;
		}
		.plugin-badge.detected {
			background: #dba617;
			color: #fff;
		}
		.plugin-badge.disabled {
			background: #d63638;
			color: #fff;
		}
		.plugin-actions form {
			display: inline;
		}
		.btn {
			display: inline-block;
			padding: 6px 12px;
			border: none;
			border-radius: 4px;
			font-size: 13px;
			cursor: pointer;
			text-decoration: none;
		}
		.btn-disable {
			background: #d63638;
			color: #fff;
		}
		.btn-disable:hover {
			background: #b32d2e;
		}
		.btn-enable {
			background: #00a32a;
			color: #fff;
		}
		.btn-enable:hover {
			background: #008a20;
		}
		.btn-secondary {
			background: #f0f0f1;
			color: #1d2327;
		}
		.btn-secondary:hover {
			background: #dcdcde;
		}
		.recovery-footer {
			margin-top: 20px;
			text-align: center;
		}
		.recovery-footer a {
			color: #2271b1;
			text-decoration: none;
		}
		.recovery-footer a:hover {
			text-decoration: underline;
		}
		.info-box {
			background: #f0f6fc;
			border: 1px solid #72aee6;
			border-radius: 4px;
			padding: 12px;
			margin-bottom: 20px;
			font-size: 13px;
		}
		.success-message {
			background: #edfaef;
			border: 1px solid #00a32a;
			border-radius: 4px;
			padding: 12px;
			margin-bottom: 20px;
			color: #1e4620;
		}
	</style>
</head>
<body>
	<div class="recovery-container">
		<div class="recovery-header">
			<h1>
				<span class="icon">!</span>
				<?php esc_html_e( 'Blaminhor Essentials Recovery Mode', 'blaminhor-essentials' ); ?>
			</h1>
			<p><?php esc_html_e( 'Your site encountered an error. Use this interface to disable problematic plugins and regain access to your admin area.', 'blaminhor-essentials' ); ?></p>
		</div>

		<?php if ( $last_error ) : ?>
		<div class="error-box">
			<h3><?php esc_html_e( 'Last Fatal Error Detected', 'blaminhor-essentials' ); ?></h3>
			<pre><?php echo esc_html( $last_error['message'] ); ?></pre>
			<div class="error-meta">
				<strong><?php esc_html_e( 'File:', 'blaminhor-essentials' ); ?></strong> <?php echo esc_html( $last_error['file'] ); ?>:<?php echo esc_html( $last_error['line'] ); ?><br>
				<strong><?php esc_html_e( 'Time:', 'blaminhor-essentials' ); ?></strong> <?php echo esc_html( date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $last_error['time'] ) ); ?>
				<?php if ( $detected_plugin ) : ?>
				<br><strong><?php esc_html_e( 'Detected Plugin:', 'blaminhor-essentials' ); ?></strong> <code><?php echo esc_html( $detected_plugin ); ?></code>
				<?php endif; ?>
			</div>
		</div>
		<?php endif; ?>

		<div class="info-box">
			<?php esc_html_e( 'Disabling a plugin deactivates it in the database. You can re-enable it from here or from the WordPress admin.', 'blaminhor-essentials' ); ?>
		</div>

		<div class="plugins-section">
			<h2><?php esc_html_e( 'Installed Plugins', 'blaminhor-essentials' ); ?></h2>
			<ul class="plugin-list">
				<?php foreach ( $plugins as $plugin ) : ?>
				<li class="plugin-item <?php echo ! $plugin['active'] ? 'disabled' : ''; ?> <?php echo ( $detected_plugin === $plugin['folder'] ) ? 'detected' : ''; ?>">
					<div class="plugin-info">
						<span class="plugin-name"><?php echo esc_html( $plugin['name'] ); ?></span>
						<?php if ( $detected_plugin === $plugin['folder'] ) : ?>
							<span class="plugin-badge detected"><?php esc_html_e( 'Likely cause', 'blaminhor-essentials' ); ?></span>
						<?php endif; ?>
						<?php if ( ! $plugin['active'] ) : ?>
							<span class="plugin-badge disabled"><?php esc_html_e( 'Inactive', 'blaminhor-essentials' ); ?></span>
						<?php endif; ?>
						<div class="plugin-folder"><?php echo esc_html( $plugin['folder'] ); ?></div>
					</div>
					<div class="plugin-actions">
						<?php if ( $plugin['is_self'] && $plugin['active'] ) : ?>
							<form method="post" onsubmit="return confirm('<?php echo esc_js( __( 'Warning: Disabling Blaminhor Essentials will also disable this recovery interface. You will need to re-enable it from WordPress admin. Are you sure?', 'blaminhor-essentials' ) ); ?>');">
								<input type="hidden" name="ap_fer_nonce" value="<?php echo esc_attr( $nonce ); ?>">
								<input type="hidden" name="ap_fer_action" value="disable">
								<input type="hidden" name="ap_fer_plugin" value="<?php echo esc_attr( $plugin['folder'] ); ?>">
								<button type="submit" class="btn btn-disable" style="background: #8b0000;"><?php esc_html_e( 'Deactivate (Self)', 'blaminhor-essentials' ); ?></button>
							</form>
						<?php elseif ( ! $plugin['active'] ) : ?>
							<form method="post">
								<input type="hidden" name="ap_fer_nonce" value="<?php echo esc_attr( $nonce ); ?>">
								<input type="hidden" name="ap_fer_action" value="enable">
								<input type="hidden" name="ap_fer_plugin" value="<?php echo esc_attr( $plugin['folder'] ); ?>">
								<button type="submit" class="btn btn-enable"><?php esc_html_e( 'Activate', 'blaminhor-essentials' ); ?></button>
							</form>
						<?php else : ?>
							<form method="post">
								<input type="hidden" name="ap_fer_nonce" value="<?php echo esc_attr( $nonce ); ?>">
								<input type="hidden" name="ap_fer_action" value="disable">
								<input type="hidden" name="ap_fer_plugin" value="<?php echo esc_attr( $plugin['folder'] ); ?>">
								<button type="submit" class="btn btn-disable"><?php esc_html_e( 'Deactivate', 'blaminhor-essentials' ); ?></button>
							</form>
						<?php endif; ?>
					</div>
				</li>
				<?php endforeach; ?>
			</ul>
		</div>

		<div class="recovery-footer">
			<p>
				<a href="<?php echo esc_url( admin_url() ); ?>"><?php esc_html_e( 'Try accessing admin', 'blaminhor-essentials' ); ?></a>
				&nbsp;|&nbsp;
				<a href="<?php echo esc_url( home_url() ); ?>"><?php esc_html_e( 'Go to site', 'blaminhor-essentials' ); ?></a>
			</p>
			<p style="margin-top: 10px; font-size: 12px; color: #646970;">
				<?php esc_html_e( 'Powered by Blaminhor Essentials', 'blaminhor-essentials' ); ?>
			</p>
		</div>
	</div>
</body>
</html>
		<?php
	}

	/**
	 * Add admin bar item
	 *
	 * @param WP_Admin_Bar $admin_bar Admin bar instance.
	 */
	public function add_admin_bar_item( $admin_bar ) {
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}

		$recovery_key = $this->get_setting( 'recovery_key', '' );
		$recovery_url = add_query_arg( 'ap_recovery', $recovery_key, home_url( '/' ) );

		$admin_bar->add_node( array(
			'id'    => 'ap-fatal-error-recovery',
			'title' => '<span class="ab-icon dashicons dashicons-shield" style="font-family: dashicons; font-size: 20px; margin-top: 2px;"></span>' . __( 'Recovery', 'blaminhor-essentials' ),
			'href'  => $recovery_url,
			'meta'  => array(
				'title' => __( 'Open Fatal Error Recovery Mode', 'blaminhor-essentials' ),
			),
		) );
	}

	/**
	 * AJAX: Disable plugin
	 */
	public function ajax_disable_plugin() {
		check_ajax_referer( 'blaminhor_essentials_admin', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( __( 'Unauthorized', 'blaminhor-essentials' ) );
		}

		$plugin = isset( $_POST['plugin'] ) ? sanitize_file_name( wp_unslash( $_POST['plugin'] ) ) : '';

		if ( empty( $plugin ) ) {
			wp_send_json_error( __( 'No plugin specified', 'blaminhor-essentials' ) );
		}

		if ( $this->deactivate_plugin_db( $plugin ) ) {
			$this->clear_cache_recovery_sentinel();
			wp_send_json_success( array( 'message' => __( 'Plugin disabled', 'blaminhor-essentials' ) ) );
		} else {
			wp_send_json_error( __( 'Could not disable plugin', 'blaminhor-essentials' ) );
		}
	}

	/**
	 * Remove the .fer_recovery sentinel so the Cache drop-in resumes serving
	 * cached pages once the operator has acknowledged the fatal (by disabling
	 * a plugin or re-enabling it). Idempotent.
	 */
	private function clear_cache_recovery_sentinel() {
		$upload_dir = wp_upload_dir();
		if ( empty( $upload_dir['basedir'] ) ) {
			return;
		}
		$sentinel = $upload_dir['basedir'] . '/blaminhor-essentials/cache/.fer_recovery';
		if ( file_exists( $sentinel ) ) {
			wp_delete_file( $sentinel );
		}
	}

	/**
	 * AJAX: Enable plugin
	 */
	public function ajax_enable_plugin() {
		check_ajax_referer( 'blaminhor_essentials_admin', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( __( 'Unauthorized', 'blaminhor-essentials' ) );
		}

		$plugin = isset( $_POST['plugin'] ) ? sanitize_file_name( wp_unslash( $_POST['plugin'] ) ) : '';

		if ( empty( $plugin ) ) {
			wp_send_json_error( __( 'No plugin specified', 'blaminhor-essentials' ) );
		}

		if ( $this->activate_plugin_db( $plugin ) ) {
			$this->clear_cache_recovery_sentinel();
			wp_send_json_success( array( 'message' => __( 'Plugin enabled', 'blaminhor-essentials' ) ) );
		} else {
			wp_send_json_error( __( 'Could not enable plugin', 'blaminhor-essentials' ) );
		}
	}

	/**
	 * AJAX: Regenerate recovery key
	 */
	public function ajax_regenerate_key() {
		check_ajax_referer( 'blaminhor_essentials_admin', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( __( 'Unauthorized', 'blaminhor-essentials' ) );
		}

		$new_key = $this->generate_recovery_key();
		$this->settings['recovery_key'] = $new_key;
		$this->save_settings( $this->settings );
		$this->store_recovery_key_file( $new_key );

		wp_send_json_success( array(
			'key' => $new_key,
			'url' => add_query_arg( 'ap_recovery', $new_key, home_url( '/' ) ),
		) );
	}

	/**
	 * Process settings
	 *
	 * @param array $data Form data.
	 */
	protected function process_settings( $data ) {
		$this->settings['show_admin_bar'] = isset( $data['show_admin_bar'] );
		$this->settings['email_notification'] = isset( $data['email_notification'] );

		$this->save_settings( $this->settings );

		add_action( 'admin_notices', function () {
			blaminhor_essentials_admin_notice( __( 'Settings saved.', 'blaminhor-essentials' ), 'success' );
		} );
	}

	/**
	 * Render settings page
	 */
	public function render_settings_page() {
		$recovery_key = $this->get_setting( 'recovery_key', '' );
		$recovery_url = add_query_arg( 'ap_recovery', $recovery_key, home_url( '/' ) );
		$last_error = $this->get_last_error();
		?>
		<div class="wrap blaminhor-essentials-wrap">
			<h1><?php esc_html_e( 'Fatal Error Recovery', 'blaminhor-essentials' ); ?></h1>

			<div class="blaminhor-essentials-tabs-wrapper">
				<div class="blaminhor-essentials-tabs">
					<button type="button" class="blaminhor-essentials-tab active" data-tab="recovery"><?php esc_html_e( 'Recovery', 'blaminhor-essentials' ); ?></button>
					<button type="button" class="blaminhor-essentials-tab" data-tab="settings"><?php esc_html_e( 'Settings', 'blaminhor-essentials' ); ?></button>
				</div>

			<!-- Recovery Tab -->
			<div class="blaminhor-essentials-tab-content active" data-tab-content="recovery">
				<h3><?php esc_html_e( 'Recovery URL', 'blaminhor-essentials' ); ?></h3>
				<p class="description"><?php esc_html_e( 'Bookmark this URL or save it somewhere safe. If your site crashes due to a plugin error, you can use this URL to disable plugins and recover access.', 'blaminhor-essentials' ); ?></p>

				<div class="ap-fer-recovery-url">
					<div class="ap-label-flex">
						<input type="text" id="ap-fer-url" value="<?php echo esc_attr( $recovery_url ); ?>" readonly style="flex: 1; font-family: monospace;">
						<button type="button" class="button ap-btn-icon" id="ap-fer-copy-url">
							<span class="dashicons dashicons-clipboard"></span>
							<?php esc_html_e( 'Copy', 'blaminhor-essentials' ); ?>
						</button>
						<a href="<?php echo esc_url( $recovery_url ); ?>" class="button button-primary" target="_blank">
							<?php esc_html_e( 'Test Recovery Mode', 'blaminhor-essentials' ); ?>
						</a>
					</div>
					<p class="ap-backup-file-meta" style="margin: 10px 0 0;">
						<span class="dashicons dashicons-warning ap-icon-14" class="ap-text-warning"></span>
						<?php esc_html_e( 'Keep this URL secret! Anyone with this link can disable plugins on your site.', 'blaminhor-essentials' ); ?>
					</p>
				</div>

				<p>
					<button type="button" class="button ap-btn-icon" id="ap-fer-regenerate-key">
						<span class="dashicons dashicons-update"></span>
						<?php esc_html_e( 'Regenerate Recovery Key', 'blaminhor-essentials' ); ?>
					</button>
				</p>

				<?php if ( $last_error ) : ?>
				<hr>
				<h3><?php esc_html_e( 'Last Detected Error', 'blaminhor-essentials' ); ?></h3>
				<div class="blaminhor-essentials-notice warning ap-mt-md">
					<strong><?php esc_html_e( 'Error:', 'blaminhor-essentials' ); ?></strong> <?php echo esc_html( $last_error['message'] ); ?><br>
					<strong><?php esc_html_e( 'File:', 'blaminhor-essentials' ); ?></strong> <?php echo esc_html( $last_error['file'] ); ?>:<?php echo esc_html( $last_error['line'] ); ?><br>
					<strong><?php esc_html_e( 'Time:', 'blaminhor-essentials' ); ?></strong> <?php echo esc_html( date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $last_error['time'] ) ); ?>
					<?php if ( ! empty( $last_error['plugin'] ) ) : ?>
					<br><strong><?php esc_html_e( 'Detected Plugin:', 'blaminhor-essentials' ); ?></strong> <?php echo esc_html( $last_error['plugin'] ); ?>
					<?php endif; ?>
				</div>
				<?php endif; ?>

				<hr>
				<h3><?php esc_html_e( 'How It Works', 'blaminhor-essentials' ); ?></h3>
				<ol style="margin-left: 20px; line-height: 2;">
					<li><?php esc_html_e( 'When a fatal error occurs, the error details are logged.', 'blaminhor-essentials' ); ?></li>
					<li><?php esc_html_e( 'Use the recovery URL above to access the recovery interface.', 'blaminhor-essentials' ); ?></li>
					<li><?php esc_html_e( 'The recovery page shows all plugins and highlights the likely cause.', 'blaminhor-essentials' ); ?></li>
					<li><?php esc_html_e( 'Disable problematic plugins by clicking the "Disable" button.', 'blaminhor-essentials' ); ?></li>
					<li><?php esc_html_e( 'Disabled plugins are deactivated in the database (the plugin files remain intact).', 'blaminhor-essentials' ); ?></li>
					<li><?php esc_html_e( 'Once the error is resolved, you can re-enable plugins from the same interface or from the WordPress admin.', 'blaminhor-essentials' ); ?></li>
				</ol>
			</div>

			<!-- Settings Tab -->
			<div class="blaminhor-essentials-tab-content" data-tab-content="settings">
				<form method="post">
					<?php $this->render_nonce_field(); ?>

					<table class="form-table">
						<tr>
							<th scope="row"><?php esc_html_e( 'Admin Bar', 'blaminhor-essentials' ); ?></th>
							<td>
								<label>
									<input type="checkbox" name="show_admin_bar" <?php checked( $this->get_setting( 'show_admin_bar', false ) ); ?>>
									<?php esc_html_e( 'Show recovery link in admin bar', 'blaminhor-essentials' ); ?>
								</label>
								<p class="description"><?php esc_html_e( 'Adds a quick link to the recovery mode in the WordPress admin bar.', 'blaminhor-essentials' ); ?></p>
							</td>
						</tr>
						<tr>
							<th scope="row"><?php esc_html_e( 'Email Notification', 'blaminhor-essentials' ); ?></th>
							<td>
								<label>
									<input type="checkbox" name="email_notification" <?php checked( $this->get_setting( 'email_notification', true ) ); ?>>
									<?php esc_html_e( 'Send email notification when fatal error is detected', 'blaminhor-essentials' ); ?>
								</label>
								<p class="description"><?php esc_html_e( 'When enabled, an email with the recovery URL will be sent to the admin email when a fatal error caused by a plugin is detected.', 'blaminhor-essentials' ); ?></p>
							</td>
						</tr>
					</table>

					<?php submit_button(); ?>
				</form>
			</div>
			</div><!-- .blaminhor-essentials-tabs-wrapper -->
		</div>
		<?php
	}
}
