<?php
/**
 * Admin surface for plugin RSA registration (zero-touch).
 *
 * Registration is fully automatic and never surfaced as a merchant action: the shop
 * self-registers over the API-key start route (Plugin_Rsa_Registrar::auto_register()).
 * It is queued the moment the API key connects (Simplyin_Sync → Plugin::schedule_rsa_auto_register(),
 * an off-request one-off event), on plugin upgrade, and via the throttled admin_init
 * backstop + daily cron. This surface reports success once ("signing is active") and runs
 * the silent auto-register backstop on any admin page — there is deliberately NO
 * merchant-facing register / retry / disable control. The one exception is a stuck
 * already-registered state (Plugin_Rsa_Registrar::was_already_registered()): auto-register
 * would otherwise fail silently on every cron tick forever, so that one failure gets a
 * standing notice with the actual fix (ask an admin to Revoke the key in the Merchant Panel).
 *
 * @package Simplyin\Simplyin_W_Plugin\Admin
 */

declare( strict_types=1 );

namespace Simplyin\Simplyin_W_Plugin\Admin;

use Simplyin\Simplyin_W_Plugin\Lib\Remote\Sin_Core_Api\Plugin_Rsa_Registrar;
use Simplyin\Simplyin_W_Plugin\Lib\Remote\Sin_Core_Api\Plugin_Rsa_Signer;
use Simplyin\Simplyin_W_Plugin\Sync\Simplyin_Sync;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

class Plugin_Rsa_Registration_Admin {

	/** One-time flag: the "signing is active" confirmation has been shown once (autoload:false). */
	const OPTION_NOTICE_SHOWN = 'simplyin_rsa_admin_notice_shown';

	private string $plugin_name;

	public function __construct( string $plugin_name ) {
		$this->plugin_name = $plugin_name;
	}

	public function init(): void {
		add_action( 'admin_init', [ $this, 'maybe_auto_register' ] );
		add_action( 'admin_notices', [ $this, 'render_box' ] );
	}

	/** Throttle key: at most one synchronous auto-register attempt per this window (seconds). */
	const AUTOREG_THROTTLE = 'simplyin_rsa_autoreg_throttle';

	/**
	 * Deterministic zero-touch registration on ANY wp-admin page load (not only the Simply.IN
	 * settings screen), so a plugin update completes registration as soon as the admin lands on the
	 * next admin page — without having to open Simply.IN and without relying on the version-bump
	 * migration or wp-cron (both unreliable: shared version, DISABLE_WP_CRON, low-traffic shop).
	 *
	 * Runs synchronously (two quick HTTP calls) but only when: connected, not yet registered, kill
	 * switch off, a real admin request (not AJAX/cron), and not attempted within the throttle window
	 * — so it fires essentially once right after an upgrade, then no-ops. Idempotent + best-effort.
	 */
	public function maybe_auto_register(): void {
		if ( wp_doing_ajax() || wp_doing_cron() || ! current_user_can( 'manage_options' ) ) {
			return;
		}
		if ( (string) get_option( 'SimplyInApiConnection' ) !== '1' ) {
			return;
		}
		// Admin backstop for the HMAC webhook signing secret, independent of RSA state (a shop can be
		// RSA-registered yet still be missing the secret). Throttled + best-effort inside the helper —
		// the "manual" mirror of the plugin-update trigger, like the RSA auto-register just below.
		Simplyin_Sync::sync_webhook_secret_now();

		$signer = new Plugin_Rsa_Signer();
		if ( $signer->is_registered() || $signer->is_disabled() ) {
			return;
		}
		// Throttle so a persistently-unregistered shop (e.g. backend down) does not fire two HTTP
		// calls on every single admin page load; the recurring daily cron remains the backstop.
		if ( false !== get_transient( self::AUTOREG_THROTTLE ) ) {
			return;
		}
		set_transient( self::AUTOREG_THROTTLE, 1, 5 * MINUTE_IN_SECONDS );
		try {
			( new Plugin_Rsa_Registrar() )->auto_register();
		} catch ( \Throwable $e ) {
			// Best-effort: the daily cron backstop retries if this never completes.
		}
	}

	/**
	 * Settings page slug this plugin registers via add_menu_page().
	 */
	private function settings_slug(): string {
		return $this->plugin_name . '-settings';
	}

	private function is_settings_page(): bool {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- page read only for screen gating.
		$page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : '';

		return $this->settings_slug() === $page;
	}

	/**
	 * Top-of-page notice. Registration is zero-touch (completed automatically on API-key connect,
	 * plugin upgrade, and the admin_init + daily-cron backstops), so the merchant sees at most a
	 * single, one-time "signing is active" confirmation and nothing thereafter — there is no
	 * merchant-facing register / retry / disable control. The durable kill switch is a
	 * read-only guard honored by the signer, set out-of-band by support only (the
	 * `simplyin_plugin_rsa_disabled` option) and cleared automatically on the next successful
	 * (re-)registration.
	 */
	public function render_box(): void {
		if ( ! current_user_can( 'manage_options' ) || ! $this->is_settings_page() ) {
			return;
		}

		// Gate on is_active() (registered AND kill switch off AND OpenSSL present) FIRST, before
		// the already-registered check below: auto_register() has no lock around its is_registered()
		// check (see Plugin::on_plugin_upgraded()'s TOCTOU comment), so two concurrent attempts can
		// race — the loser's start call gets the backend's "already exists" rejection even though
		// the winner completes registration and the shop ends up signing correctly. Once that is the
		// case, was_already_registered() can still read a stale flag from the losing attempt; it must
		// never override signing that is actually active.
		if ( ( new Plugin_Rsa_Signer() )->is_active() ) {
			// One-time confirmation, then never again.
			if ( '1' === (string) get_option( self::OPTION_NOTICE_SHOWN, '' ) ) {
				return;
			}
			update_option( self::OPTION_NOTICE_SHOWN, '1', false );

			echo '<div class="notice notice-success is-dismissible"><div style="padding:6px 4px 12px;">';
			echo '<p style="margin:0;"><strong>Simply.IN</strong> — '
				. esc_html__( 'secure request signing is active. No action needed.', 'simplyin' ) . '</p>';
			echo '</div></div>';

			return;
		}

		// Not active. The backend may still have a public key on file for this merchant (a stale
		// pairing from a previous session) and keep refusing new registration attempts — the API
		// key itself is fine. Show the actual fix instead of leaving the merchant with no signal
		// while auto-register silently fails on every cron tick.
		if ( ( new Plugin_Rsa_Registrar() )->was_already_registered() ) {
			echo '<div class="notice notice-error"><div style="padding:6px 4px 12px;">';
			echo '<p style="margin:0;"><strong>Simply.IN</strong> — '
				. esc_html__( 'RSA is already registered for this shop from a previous session. Ask an admin to click "Revoke" for this API key in the SimplyIN merchant panel, then click Save again.', 'simplyin' )
				. '</p>';
			echo '</div></div>';
		}

		// not signing yet, and not the already-registered case — auto-registers; no nagging.
	}
}
