<?php
/**
 * Admin UI: menu, screens, assets, AJAX, form actions.
 *
 * All actions are nonce-protected and capability-gated (manage_options).
 * Notices are scoped to our own screens.
 *
 * @package Upgrade_Pilot
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class UPilot_Admin
 */
class UPilot_Admin {

	/**
	 * Capability required for every screen and every action.
	 *
	 * On a network this resolves to manage_network_options, because everything
	 * Upgrade Pilot touches — plugin files, the freeze list, the settings — is
	 * network-wide. Letting a subsite administrator change it would let one tenant
	 * of a network change behaviour for all the others.
	 *
	 * @return string
	 */
	public static function cap() {
		return UPilot_Store::cap();
	}

	/**
	 * Plugin container.
	 *
	 * @var UPilot_Plugin
	 */
	private $plugin;

	/**
	 * Constructor.
	 *
	 * @param UPilot_Plugin $plugin Container.
	 */
	public function __construct( UPilot_Plugin $plugin ) {
		$this->plugin = $plugin;

		// On a network the screens belong in the NETWORK admin, where a super admin
		// manages plugins. `admin_menu` stays registered as well so that a plugin
		// activated on a single subsite (rather than network-activated) still has a
		// reachable UI. The capability check keeps subsite administrators out of
		// both paths.
		add_action( 'admin_menu', array( $this, 'register_menu' ) );
		if ( is_multisite() ) {
			add_action( 'network_admin_menu', array( $this, 'register_menu' ) );
		}
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) );
		add_action( 'wp_dashboard_setup', array( $this, 'register_dashboard_widget' ) );

		add_action( 'wp_ajax_upilot_start_scan', array( $this, 'ajax_start_scan' ) );
		add_action( 'wp_ajax_upilot_tick', array( $this, 'ajax_tick' ) );
		add_action( 'wp_ajax_upilot_run_snapshot', array( $this, 'ajax_run_snapshot' ) );

		add_action( 'admin_post_upilot_save_settings', array( $this, 'handle_save_settings' ) );
		add_action( 'admin_post_upilot_suppress', array( $this, 'handle_suppress' ) );
		add_action( 'admin_post_upilot_freeze', array( $this, 'handle_freeze' ) );
		add_action( 'admin_post_upilot_ack_event', array( $this, 'handle_ack_event' ) );

		add_filter( 'plugin_action_links_' . UPILOT_BASENAME, array( $this, 'action_links' ) );
	}

	/**
	 * Menu registration.
	 */
	public function register_menu() {
		add_menu_page(
			__( 'Upgrade Pilot', 'upgrade-pilot' ),
			__( 'Upgrade Pilot', 'upgrade-pilot' ),
			self::cap(),
			'upgrade-pilot',
			array( $this, 'render_dashboard' ),
			'dashicons-shield-alt',
			80
		);
		add_submenu_page( 'upgrade-pilot', __( 'Readiness Report', 'upgrade-pilot' ), __( 'Readiness Report', 'upgrade-pilot' ), self::cap(), 'upgrade-pilot', array( $this, 'render_dashboard' ) );
		add_submenu_page( 'upgrade-pilot', __( 'Scan Results', 'upgrade-pilot' ), __( 'Scan Results', 'upgrade-pilot' ), self::cap(), 'upgrade-pilot-scan', array( $this, 'render_scan' ) );
		add_submenu_page( 'upgrade-pilot', __( 'Update Trust', 'upgrade-pilot' ), __( 'Update Trust', 'upgrade-pilot' ), self::cap(), 'upgrade-pilot-trust', array( $this, 'render_trust' ) );
		add_submenu_page( 'upgrade-pilot', __( 'Settings', 'upgrade-pilot' ), __( 'Settings', 'upgrade-pilot' ), self::cap(), 'upgrade-pilot-settings', array( $this, 'render_settings' ) );

		// "Reports & Automation" is a Pro screen. When Pro code is not usable — no
		// licence, no trial, or the free build — the Pro class is never instantiated,
		// so that page does not exist. Without this, anyone who follows a link or a
		// bookmark to it (a trial user who has just let their trial lapse, say) gets
		// a bare "you are not allowed to access this page" error at the exact moment
		// they were most likely to pay. Register an explanation instead, and a way
		// forward. This lives in the FREE code on purpose: it must exist precisely
		// when the premium file does not.
		if ( ! self::pro_active() ) {
			add_submenu_page(
				'upgrade-pilot',
				__( 'Reports & Automation (Pro)', 'upgrade-pilot' ),
				__( 'Reports & Automation (Pro)', 'upgrade-pilot' ),
				self::cap(),
				'upgrade-pilot-pro',
				array( $this, 'render_pro_upsell' )
			);
		}
	}

	/**
	 * Is the Pro code actually LOADED — i.e. is something else already rendering the
	 * "Reports & Automation" page?
	 *
	 * This asks about the CODE, and nothing else. It is not an entitlement check, and
	 * this plugin's own code contains no entitlement checks of any kind.
	 *
	 * Asking the payment SDK "has this person paid?" would be wrong here, and wrong in
	 * the worst possible place: entitlement turns true the moment somebody starts a
	 * trial, but that person is still running the free build from WordPress.org, which
	 * contains no UPilot_Pro_Admin. This page would deregister itself, nothing would
	 * register in its place, and they would land on WordPress's bare "Sorry, you are
	 * not allowed to access this page" at the exact moment they decided to pay — the
	 * very failure this page exists to prevent, reintroduced one layer down.
	 *
	 * class_exists() cannot make that mistake. It is false in the free build by
	 * construction (the file is not in the zip) and true in the premium build exactly
	 * when the Pro bootstrap ran.
	 *
	 * @return bool
	 */
	public static function pro_active() {
		return class_exists( 'UPilot_Pro_Admin', false );
	}

	/**
	 * The screen someone lands on when they open a Pro page without Pro.
	 *
	 * Says what Pro would add, and points at the pricing page. No dark patterns, no
	 * nagging anywhere else in the plugin — this page only exists because somebody
	 * navigated to it.
	 *
	 * It asks the payment SDK nothing at all. A person who has already paid, or who
	 * has started a trial, and is still running the free build gets told how to
	 * install Pro by the SDK's own notice. That is the right place for it: it is a
	 * fact about their account, not about this plugin, and this plugin's own code
	 * therefore contains no entitlement check anywhere.
	 *
	 * ------------------------------------------------------------------------
	 * GUIDELINE 9. This page shows BOTH lists, side by side, deliberately.
	 *
	 * "Implying users must pay to unlock included features" is the failure mode,
	 * and the way you fall into it is by publishing a list of what Pro adds and
	 * no list of what is already there. Every claim below was checked against the
	 * free build's own code in both directions:
	 *
	 *   - nothing in the Pro column is something the free build can do;
	 *   - everything the free build can do is named in the free column.
	 *
	 * Where a Pro line sits NEXT TO a free capability rather than replacing it —
	 * the digest, the re-scan, the freeze, multisite — the line says so out loud
	 * in parentheses. Those four are exactly the ones a reader would otherwise
	 * assume they had to pay for.
	 * ------------------------------------------------------------------------
	 */
	public function render_pro_upsell() {
		if ( ! current_user_can( self::cap() ) ) {
			wp_die( esc_html__( 'You do not have permission to view this page.', 'upgrade-pilot' ) );
		}

		// The pricing screen, which the payment SDK registers next to our own menu.
		$pricing_url = UPilot_Store::admin_url( 'admin.php?page=upgrade-pilot-pricing' );

		// What Pro ADDS. Not one line here exists anywhere in the free build.
		$pro = array(
			__( 'A branded, printable client report (PDF/print) — the document you attach to an upgrade quote.', 'upgrade-pilot' ),
			__( 'White-label it: your name, your colour, and no "Generated by Upgrade Pilot" footer.', 'upgrade-pilot' ),
			__( 'Email the readiness summary to your client, on a schedule you choose, to as many addresses as you like — and be told if it fails to send. (The daily digest to you, the site admin, is free.)', 'upgrade-pilot' ),
			__( 'Slack and webhook alerts, the moment a plugin changes hands or is closed.', 'upgrade-pilot' ),
			__( 'Automatic update-hold policies: hold a plugin\'s automatic updates for a cooling-off period the moment it changes owner or gains a contributor. (Freezing a plugin yourself is free.)', 'upgrade-pilot' ),
			__( 'Re-scan daily or twice daily. (The weekly re-scan is free.)', 'upgrade-pilot' ),
			__( 'On a network, the Network Overview: every subsite\'s status on one screen, and which of your sites actually run a given plugin. (Upgrade Pilot itself runs on multisite for free.)', 'upgrade-pilot' ),
			__( 'A private email channel with the developer.', 'upgrade-pilot' ),
		);

		// What is already here. Complete: if the free build does it, it is on this list.
		$free = array(
			__( 'The readiness report and its verdict, with server checks: PHP version and security-support window, database version, memory limit, extensions, HTTPS.', 'upgrade-pilot' ),
			__( 'Every plugin AND theme cross-referenced against its WordPress.org record.', 'upgrade-pilot' ),
			__( 'The local PHP compatibility scan, with the target PHP version you choose.', 'upgrade-pilot' ),
			__( 'Findings labelled FACT or ADVISORY — an advisory never turns your verdict red — and per-finding suppression that survives future scans.', 'upgrade-pilot' ),
			__( 'The daily update-trust snapshot: author changes, new and removed contributors, closures with the reason given, reopenings, and your abandonment thresholds.', 'upgrade-pilot' ),
			__( 'The known-compromise advisory check, driven by a list bundled with the plugin — no external security feed.', 'upgrade-pilot' ),
			__( 'The per-plugin update freeze, and the warning that will not let you hide from a security fix.', 'upgrade-pilot' ),
			__( 'Email alerts: immediately when a plugin is closed or flagged, plus a daily digest of everything else.', 'upgrade-pilot' ),
			__( 'The weekly scheduled re-scan.', 'upgrade-pilot' ),
			__( 'Site Health integration and the dashboard widget.', 'upgrade-pilot' ),
			__( 'The machine-readable export: WP-CLI scan and report, each in table or JSON, plus a status command and the REST endpoint. Not gated, not metered, not licence-checked.', 'upgrade-pilot' ),
			__( 'Full multisite: network-wide storage, the Network Admin screens, and a freeze that holds across the whole network.', 'upgrade-pilot' ),
		);
		?>
		<div class="wrap upilot-wrap">
			<h1><?php esc_html_e( 'Reports & Automation', 'upgrade-pilot' ); ?></h1>

			<p style="font-size:15px;max-width:52em;">
				<?php esc_html_e( 'This one screen is part of Upgrade Pilot Pro. Nothing else in the plugin is restricted, metered or licence-checked — Pro adds the reports and the delivery channels below, it does not switch anything on that is already installed.', 'upgrade-pilot' ); ?>
			</p>

			<div style="display:flex;gap:34px;flex-wrap:wrap;margin-top:22px;">
				<div style="flex:1 1 26em;min-width:22em;">
					<h2 style="margin-top:0;"><?php esc_html_e( 'What Pro adds', 'upgrade-pilot' ); ?></h2>
					<ul style="list-style:disc;padding-left:20px;">
						<?php foreach ( $pro as $upilot_line ) : ?>
							<li style="margin-bottom:9px;"><?php echo esc_html( $upilot_line ); ?></li>
						<?php endforeach; ?>
					</ul>
				</div>

				<div style="flex:1 1 26em;min-width:22em;">
					<h2 style="margin-top:0;"><?php esc_html_e( 'What you already have, free and unlimited', 'upgrade-pilot' ); ?></h2>
					<ul style="list-style:disc;padding-left:20px;">
						<?php foreach ( $free as $upilot_line ) : ?>
							<li style="margin-bottom:9px;"><?php echo esc_html( $upilot_line ); ?></li>
						<?php endforeach; ?>
					</ul>
				</div>
			</div>

			<p style="margin-top:28px;">
				<a href="<?php echo esc_url( $pricing_url ); ?>" class="button button-primary button-hero">
					<?php esc_html_e( 'See pricing', 'upgrade-pilot' ); ?>
				</a>
				<span class="description" style="margin-left:10px;"><?php esc_html_e( 'A 14-day trial is available, and it does not ask for a payment method.', 'upgrade-pilot' ); ?></span>
			</p>
		</div>
		<?php
	}

	/**
	 * Enqueue assets on our screens only.
	 *
	 * @param string $hook Current admin page hook.
	 */
	public function enqueue( $hook ) {
		if ( false === strpos( $hook, 'upgrade-pilot' ) ) {
			return;
		}
		wp_enqueue_style( 'upilot-admin', UPILOT_URL . 'admin/css/upilot-admin.css', array(), UPILOT_VERSION );
		wp_enqueue_script( 'upilot-admin', UPILOT_URL . 'admin/js/upilot-admin.js', array(), UPILOT_VERSION, true );
		wp_localize_script(
			'upilot-admin',
			'upilotAdmin',
			array(
				'ajaxUrl' => admin_url( 'admin-ajax.php' ),
				'nonce'   => wp_create_nonce( 'upilot_ajax' ),
				'i18n'    => array(
					'scanning'   => __( 'Scanning…', 'upgrade-pilot' ),
					'files'      => __( 'files', 'upgrade-pilot' ),
					'done'       => __( 'Scan complete. Reloading…', 'upgrade-pilot' ),
					'failed'     => __( 'Scan tick failed — retrying…', 'upgrade-pilot' ),
					'terminal'   => __( 'Scan requests are failing. The scan continues in the background via WP-Cron — reload this page later to check progress.', 'upgrade-pilot' ),
					'rescan'     => __( 'Run full readiness scan', 'upgrade-pilot' ),
					'snapDone'   => __( 'Provenance check complete. Reloading…', 'upgrade-pilot' ),
					'snapFailed' => __( 'Could not complete the check — WordPress.org may be slow right now. Your daily scheduled check will retry automatically.', 'upgrade-pilot' ),
				),
			)
		);
	}

	/**
	 * Dashboard "at a glance" widget (small, informative, natively removable).
	 */
	public function register_dashboard_widget() {
		if ( ! current_user_can( self::cap() ) ) {
			return;
		}
		wp_add_dashboard_widget(
			'upilot_glance',
			__( 'Upgrade Pilot', 'upgrade-pilot' ),
			array( $this, 'render_dashboard_widget' )
		);
	}

	/**
	 * Render the dashboard widget.
	 */
	public function render_dashboard_widget() {
		$report = UPilot_Store::get_option( 'upilot_last_report' );
		if ( ! is_array( $report ) || empty( $report['verdict'] ) ) {
			printf(
				'<p>%s</p><p><a class="button button-primary" href="%s">%s</a></p>',
				esc_html__( 'No readiness report yet. Find out whether this site survives the next WordPress and PHP upgrade.', 'upgrade-pilot' ),
				esc_url( UPilot_Store::admin_url( 'admin.php?page=upgrade-pilot' ) ),
				esc_html__( 'Run first check', 'upgrade-pilot' )
			);
			return;
		}
		$colors = array(
			'green' => '#00a32a',
			'amber' => '#996800',
			'red'   => '#d63638',
		);
		$color  = isset( $colors[ $report['verdict'] ] ) ? $colors[ $report['verdict'] ] : '#646970';
		printf(
			'<p><span style="display:inline-block;width:12px;height:12px;border-radius:50%%;background:%s;margin-right:6px;"></span><strong>%s</strong></p><p><a href="%s">%s</a></p>',
			esc_attr( $color ),
			esc_html( $report['headline'] ),
			esc_url( UPilot_Store::admin_url( 'admin.php?page=upgrade-pilot' ) ),
			esc_html__( 'Open the full readiness report', 'upgrade-pilot' )
		);
	}

	/**
	 * Plugins-list action links.
	 *
	 * @param string[] $links Links.
	 * @return string[]
	 */
	public function action_links( $links ) {
		array_unshift(
			$links,
			sprintf( '<a href="%s">%s</a>', esc_url( UPilot_Store::admin_url( 'admin.php?page=upgrade-pilot' ) ), esc_html__( 'Readiness Report', 'upgrade-pilot' ) )
		);
		return $links;
	}

	// ---------------------------------------------------------------- Screens.

	/**
	 * Readiness dashboard.
	 */
	public function render_dashboard() {
		if ( ! current_user_can( self::cap() ) ) {
			wp_die( esc_html__( 'You do not have permission to view this page.', 'upgrade-pilot' ) );
		}
		$report = $this->plugin->get( 'report' )->get();
		$state  = $this->plugin->get( 'scanner' )->get_state();
		require UPILOT_DIR . 'admin/views/dashboard.php';
	}

	/**
	 * Scan results screen.
	 */
	public function render_scan() {
		if ( ! current_user_can( self::cap() ) ) {
			wp_die( esc_html__( 'You do not have permission to view this page.', 'upgrade-pilot' ) );
		}
		$scanner  = $this->plugin->get( 'scanner' );
		$settings = UPilot_Store::get_option( 'upilot_settings', array() );
		$state    = $scanner->get_state();
		$results  = $scanner->get_results( isset( $settings['target_php'] ) ? $settings['target_php'] : '' );
		require UPILOT_DIR . 'admin/views/scan.php';
	}

	/**
	 * Update Trust screen.
	 */
	public function render_trust() {
		if ( ! current_user_can( self::cap() ) ) {
			wp_die( esc_html__( 'You do not have permission to view this page.', 'upgrade-pilot' ) );
		}
		$snapshot = $this->plugin->get( 'snapshot' );
		$freeze   = $this->plugin->get( 'freeze' );
		$identity = $this->plugin->get( 'identity' );
		$events   = $snapshot->get_events( 200 );
		$states   = $snapshot->get_latest_states();
		$frozen   = $freeze->get_frozen();
		$map      = $identity->get_map();
		require UPILOT_DIR . 'admin/views/trust.php';
	}

	/**
	 * Settings screen.
	 */
	public function render_settings() {
		if ( ! current_user_can( self::cap() ) ) {
			wp_die( esc_html__( 'You do not have permission to view this page.', 'upgrade-pilot' ) );
		}
		$settings = UPilot_Store::get_option( 'upilot_settings', array() );
		require UPILOT_DIR . 'admin/views/settings.php';
	}

	// ---------------------------------------------------------------- AJAX.

	/**
	 * Common AJAX guard.
	 */
	private function ajax_guard() {
		check_ajax_referer( 'upilot_ajax', 'nonce' );
		if ( ! current_user_can( self::cap() ) ) {
			wp_send_json_error( array( 'message' => 'forbidden' ), 403 );
		}
	}

	/**
	 * Start a scan.
	 */
	public function ajax_start_scan() {
		$this->ajax_guard();
		$state = $this->plugin->get( 'scanner' )->start_scan();
		wp_send_json_success( $state );
	}

	/**
	 * Process one tick, return progress.
	 */
	public function ajax_tick() {
		$this->ajax_guard();
		$state = $this->plugin->get( 'scanner' )->process_tick();
		if ( isset( $state['status'] ) && 'done' === $state['status'] ) {
			UPilot_Store::delete_option( 'upilot_last_report' ); // Force reassembly with fresh findings.
		}
		wp_send_json_success( $state );
	}

	/**
	 * Run a provenance snapshot now (fresh data).
	 */
	public function ajax_run_snapshot() {
		$this->ajax_guard();
		// 18s live-fetch budget: plugins not reached in time use their 24h-cached
		// record instead of stalling the request into a timeout.
		$result = $this->plugin->get( 'snapshot' )->run( true, 18 );
		UPilot_Store::delete_option( 'upilot_last_report' );
		wp_send_json_success( $result );
	}

	// ---------------------------------------------------------------- Form actions.

	/**
	 * Save settings.
	 */
	public function handle_save_settings() {
		if ( ! current_user_can( self::cap() ) ) {
			wp_die( esc_html__( 'You do not have permission to do this.', 'upgrade-pilot' ) );
		}
		check_admin_referer( 'upilot_settings' );

		$settings = UPilot_Store::get_option( 'upilot_settings', array() );

		$target_php = isset( $_POST['target_php'] ) ? preg_replace( '/[^0-9.]/', '', sanitize_text_field( wp_unslash( $_POST['target_php'] ) ) ) : '8.4';
		if ( ! preg_match( '/^\d\.\d$/', $target_php ) ) {
			$target_php = '8.4';
		}

		$settings['target_php'] = $target_php;

		// A typo'd email must not be silently blanked: keep the previous value
		// when a non-empty submission fails validation.
		$submitted_email = isset( $_POST['alert_email'] ) ? sanitize_text_field( wp_unslash( $_POST['alert_email'] ) ) : '';
		$clean_email     = sanitize_email( $submitted_email );
		if ( '' !== $submitted_email && '' === $clean_email ) {
			$clean_email = isset( $settings['alert_email'] ) ? $settings['alert_email'] : get_option( 'admin_email' );
		}
		$settings['alert_email'] = $clean_email;
		$settings['abandoned_months']    = isset( $_POST['abandoned_months'] ) ? max( 6, min( 60, absint( wp_unslash( $_POST['abandoned_months'] ) ) ) ) : 24;
		$settings['stale_tested_majors'] = isset( $_POST['stale_tested_majors'] ) ? max( 1, min( 10, absint( wp_unslash( $_POST['stale_tested_majors'] ) ) ) ) : 3;
		$settings['weekly_rescan']       = empty( $_POST['weekly_rescan'] ) ? 0 : 1;
		$settings['email_critical']      = empty( $_POST['email_critical'] ) ? 0 : 1;
		$settings['email_digest']        = empty( $_POST['email_digest'] ) ? 0 : 1;

		UPilot_Store::update_option( 'upilot_settings', $settings );
		UPilot_Store::delete_option( 'upilot_last_report' );

		wp_safe_redirect( add_query_arg( array( 'page' => 'upgrade-pilot-settings', 'updated' => '1' ), UPilot_Store::admin_url( 'admin.php' ) ) );
		exit;
	}

	/**
	 * Suppress / unsuppress a finding.
	 */
	public function handle_suppress() {
		if ( ! current_user_can( self::cap() ) ) {
			wp_die( esc_html__( 'You do not have permission to do this.', 'upgrade-pilot' ) );
		}
		check_admin_referer( 'upilot_suppress' );

		$slug    = isset( $_POST['slug'] ) ? sanitize_key( wp_unslash( $_POST['slug'] ) ) : '';
		$rule_id = isset( $_POST['rule_id'] ) ? sanitize_key( wp_unslash( $_POST['rule_id'] ) ) : '';
		$undo    = ! empty( $_POST['undo'] );

		if ( '' !== $slug && '' !== $rule_id ) {
			if ( $undo ) {
				$this->plugin->get( 'verdict' )->unsuppress( $slug, $rule_id );
			} else {
				$this->plugin->get( 'verdict' )->suppress( $slug, $rule_id );
			}
		}

		wp_safe_redirect( add_query_arg( array( 'page' => 'upgrade-pilot-scan' ), UPilot_Store::admin_url( 'admin.php' ) ) );
		exit;
	}

	/**
	 * Freeze / unfreeze a plugin.
	 */
	public function handle_freeze() {
		if ( ! current_user_can( self::cap() ) ) {
			wp_die( esc_html__( 'You do not have permission to do this.', 'upgrade-pilot' ) );
		}
		check_admin_referer( 'upilot_freeze' );

		$file = isset( $_POST['plugin_file'] ) ? sanitize_text_field( wp_unslash( $_POST['plugin_file'] ) ) : '';
		$undo = ! empty( $_POST['undo'] );

		// Validate the target is a real installed plugin.
		if ( ! function_exists( 'get_plugins' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}
		$installed = get_plugins();

		if ( '' !== $file && isset( $installed[ $file ] ) ) {
			$freeze = $this->plugin->get( 'freeze' );
			if ( $undo ) {
				$freeze->unfreeze( $file );
			} else {
				$freeze->freeze( $file, __( 'Frozen manually from the Update Trust screen', 'upgrade-pilot' ) );
			}
		}

		wp_safe_redirect( add_query_arg( array( 'page' => 'upgrade-pilot-trust' ), UPilot_Store::admin_url( 'admin.php' ) ) );
		exit;
	}

	/**
	 * Acknowledge an event.
	 */
	public function handle_ack_event() {
		if ( ! current_user_can( self::cap() ) ) {
			wp_die( esc_html__( 'You do not have permission to do this.', 'upgrade-pilot' ) );
		}
		check_admin_referer( 'upilot_ack' );

		global $wpdb;
		$id = isset( $_POST['event_id'] ) ? (int) $_POST['event_id'] : 0;
		if ( $id > 0 ) {
			$wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- own events table; single-row acknowledge write.
				UPilot_Store::table( 'events' ),
				array( 'acknowledged' => 1 ),
				array( 'id' => $id ),
				array( '%d' ),
				array( '%d' )
			);
		}

		wp_safe_redirect( add_query_arg( array( 'page' => 'upgrade-pilot-trust' ), UPilot_Store::admin_url( 'admin.php' ) ) );
		exit;
	}
}
