<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class ASA_WP_Activity_Log {

	const EVENTS_OPTION   = 'asa_wp_activity_log_events';
	const SETTINGS_OPTION = 'asa_wp_activity_log_settings';
	const LAST_ID_OPTION  = 'asa_wp_activity_log_last_id';
	const MAX_EVENTS      = 500;

	protected static $request_dedupe = array();
	protected static $file_edit_watch = array();
	protected static $pending_events = array();
	protected static $pending_last_id = null;
	protected static $shutdown_registered = false;
	protected static $settings_cache = null;
	protected static $request_event_count = 0;

	const MAX_EVENTS_PER_REQUEST = 25;

	public function init() {
		self::ensure_storage();

		if ( ! self::$shutdown_registered ) {
			add_action( 'shutdown', array( __CLASS__, 'flush_pending_events' ), 1 );
			self::$shutdown_registered = true;
		}

		add_action( 'created_term', array( $this, 'handle_created_term' ), 10, 3 );
		add_action( 'edited_term', array( $this, 'handle_edited_term' ), 10, 3 );
		add_action( 'delete_term', array( $this, 'handle_delete_term' ), 10, 5 );

		add_action( 'wp_insert_comment', array( $this, 'handle_comment_creation' ), 10, 2 );
		add_action( 'edit_comment', array( $this, 'handle_comment_update' ), 10, 1 );
		add_action( 'wp_set_comment_status', array( $this, 'handle_comment_status_transition' ), 10, 2 );
		add_action( 'trash_comment', array( $this, 'handle_comment_trashed' ), 10, 2 );
		add_action( 'untrash_comment', array( $this, 'handle_comment_restored' ), 10, 1 );
		add_action( 'spam_comment', array( $this, 'handle_comment_spammed' ), 10, 2 );
		add_action( 'delete_comment', array( $this, 'handle_comment_deletion' ), 10, 2 );

		add_action( 'add_attachment', array( $this, 'handle_media_creation' ), 10, 1 );
		add_action( 'edit_attachment', array( $this, 'handle_media_update' ), 10, 1 );
		add_action( 'delete_attachment', array( $this, 'handle_media_deletion' ), 10, 1 );

		add_action( 'wp_create_nav_menu', array( $this, 'handle_menu_creation' ), 10, 2 );
		add_action( 'wp_update_nav_menu', array( $this, 'handle_menu_update' ), 10, 2 );
		add_action( 'wp_delete_nav_menu', array( $this, 'handle_menu_deletion' ), 10, 1 );

		add_action( 'added_option', array( $this, 'handle_option_added' ), 10, 2 );
		add_action( 'updated_option', array( $this, 'handle_option_updated' ), 10, 3 );
		add_action( 'deleted_option', array( $this, 'handle_option_deleted' ), 10, 1 );

		add_action( 'activated_plugin', array( $this, 'handle_plugin_activation' ), 10, 2 );
		add_action( 'deactivated_plugin', array( $this, 'handle_plugin_deactivation' ), 10, 2 );
		add_action( 'deleted_plugin', array( $this, 'handle_plugin_deletion' ), 10, 2 );
		add_action( 'upgrader_process_complete', array( $this, 'handle_upgrader_process_complete' ), 10, 2 );

		add_action( 'save_post', array( $this, 'handle_post_save' ), 10, 3 );
		add_action( 'transition_post_status', array( $this, 'handle_post_status_transition' ), 10, 3 );
		add_action( 'trash_post', array( $this, 'handle_post_trashed' ), 10, 1 );
		add_action( 'before_delete_post', array( $this, 'handle_post_deletion' ), 10, 1 );
		add_action( 'set_object_terms', array( $this, 'handle_set_object_terms' ), 10, 6 );

		add_action( 'switch_theme', array( $this, 'handle_theme_switch' ), 10, 3 );
		add_action( 'deleted_theme', array( $this, 'handle_theme_deletion' ), 10, 2 );

		add_action( 'user_register', array( $this, 'handle_user_registration' ), 10, 1 );
		add_action( 'profile_update', array( $this, 'handle_user_update' ), 10, 3 );
		add_action( 'wp_login', array( $this, 'handle_user_login' ), 10, 2 );
		add_action( 'wp_login_failed', array( $this, 'handle_user_login_failed' ), 10, 2 );
		add_action( 'wp_logout', array( $this, 'handle_user_logout' ), 10, 0 );
		add_action( 'retrieve_password', array( $this, 'handle_password_reset_requested' ), 10, 1 );
		add_action( 'delete_user', array( $this, 'handle_user_deletion' ), 10, 3 );

		add_action( 'wp_ajax_edit-theme-plugin-file', array( $this, 'capture_file_editor_request' ), 1 );
	}

	public static function ensure_storage() {
		if ( false === get_option( self::SETTINGS_OPTION, false ) ) {
			add_option( self::SETTINGS_OPTION, self::get_default_settings(), '', false );
		}
		if ( false === get_option( self::EVENTS_OPTION, false ) ) {
			add_option( self::EVENTS_OPTION, array(), '', false );
		}
		if ( false === get_option( self::LAST_ID_OPTION, false ) ) {
			add_option( self::LAST_ID_OPTION, 0, '', false );
		}
	}

	public static function get_severity_levels() {
		return array(
			'emergency' => __( 'Emergency', 'aegisify-audit-agent' ),
			'severe'    => __( 'Severe', 'aegisify-audit-agent' ),
			'high'      => __( 'High', 'aegisify-audit-agent' ),
			'medium'    => __( 'Medium', 'aegisify-audit-agent' ),
			'low'       => __( 'Low', 'aegisify-audit-agent' ),
			'debug'     => __( 'Debug', 'aegisify-audit-agent' ),
		);
	}

	public static function get_sensor_definitions() {
		return array(
			'core_update' => array(
				'category'         => 'Core',
				'label'            => __( 'Core update', 'aegisify-audit-agent' ),
				'remarks'          => __( 'Records when the WordPress core version is updated.', 'aegisify-audit-agent' ),
				'default_enabled'  => 1,
				'default_severity' => 'high',
			),
			'category_creation' => array(
				'category' => 'Category', 'label' => __( 'Category creation', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a new category is created.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'low',
			),
			'category_update' => array(
				'category' => 'Category', 'label' => __( 'Category update', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when an existing category is edited.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'low',
			),
			'category_deletion' => array(
				'category' => 'Category', 'label' => __( 'Category deletion', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a category is permanently deleted.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'comment_creation' => array(
				'category' => 'Comment', 'label' => __( 'Comment creation', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a new comment is added.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'low',
			),
			'comment_update' => array(
				'category' => 'Comment', 'label' => __( 'Comment update', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a comment is edited.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'low',
			),
			'comment_deletion' => array(
				'category' => 'Comment', 'label' => __( 'Comment deletion', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a comment is permanently deleted.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'comment_trashed' => array(
				'category' => 'Comment', 'label' => __( 'Comment trashed', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a comment is moved to the trash.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'low',
			),
			'comment_restored' => array(
				'category' => 'Comment', 'label' => __( 'Comment restored', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a comment is restored from the trash.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'low',
			),
			'comment_approved' => array(
				'category' => 'Comment', 'label' => __( 'Comment approved', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a comment is approved.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'low',
			),
			'comment_unapproved' => array(
				'category' => 'Comment', 'label' => __( 'Comment unapproved', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a comment is put on hold.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'low',
			),
			'comment_spammed' => array(
				'category' => 'Comment', 'label' => __( 'Comment spammed', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a comment is marked as spam.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'plugin_file_edit' => array(
				'category' => 'File', 'label' => __( 'Plugin file edit', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a plugin file is changed in the built-in editor.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'high',
			),
			'theme_file_edit' => array(
				'category' => 'File', 'label' => __( 'Theme file edit', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a theme file is changed in the built-in editor.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'high',
			),
			'media_creation' => array(
				'category' => 'Media', 'label' => __( 'Media creation', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a new media item is uploaded.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'low',
			),
			'media_update' => array(
				'category' => 'Media', 'label' => __( 'Media update', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a media item is edited.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'low',
			),
			'media_deletion' => array(
				'category' => 'Media', 'label' => __( 'Media deletion', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a media item is deleted.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'menu_creation' => array(
				'category' => 'Menu', 'label' => __( 'Menu creation', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a new navigation menu is created.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'low',
			),
			'menu_update' => array(
				'category' => 'Menu', 'label' => __( 'Menu update', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a navigation menu is updated.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'low',
			),
			'menu_deletion' => array(
				'category' => 'Menu', 'label' => __( 'Menu deletion', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a navigation menu is deleted.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'option_creation' => array(
				'category' => 'Option', 'label' => __( 'Option creation', 'aegisify-audit-agent' ), 'remarks' => __( 'Records new option keys. Disabled by default because this can be noisy.', 'aegisify-audit-agent' ), 'default_enabled' => 0, 'default_severity' => 'debug',
			),
			'option_update_wp_core' => array(
				'category' => 'Option', 'label' => __( 'Option update (WP core)', 'aegisify-audit-agent' ), 'remarks' => __( 'Records updates to core WordPress options. Disabled by default because this can be noisy.', 'aegisify-audit-agent' ), 'default_enabled' => 0, 'default_severity' => 'debug',
			),
			'option_update_non_wp_core' => array(
				'category' => 'Option', 'label' => __( 'Option update (Non-WP core)', 'aegisify-audit-agent' ), 'remarks' => __( 'Records updates to non-core options. Disabled by default because this can be noisy.', 'aegisify-audit-agent' ), 'default_enabled' => 0, 'default_severity' => 'debug',
			),
			'option_deletion' => array(
				'category' => 'Option', 'label' => __( 'Option deletion', 'aegisify-audit-agent' ), 'remarks' => __( 'Records deleted option keys. Disabled by default because this can be noisy.', 'aegisify-audit-agent' ), 'default_enabled' => 0, 'default_severity' => 'debug',
			),
			'plugin_installation' => array(
				'category' => 'Plugin', 'label' => __( 'Plugin installation', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when one or more plugins are installed.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'plugin_update' => array(
				'category' => 'Plugin', 'label' => __( 'Plugin update', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when one or more plugins are updated.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'plugin_activation' => array(
				'category' => 'Plugin', 'label' => __( 'Plugin activation', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a plugin is activated.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'plugin_deactivation' => array(
				'category' => 'Plugin', 'label' => __( 'Plugin deactivation', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a plugin is deactivated.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'plugin_deletion' => array(
				'category' => 'Plugin', 'label' => __( 'Plugin deletion', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a plugin is deleted.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'high',
			),
			'post_creation' => array(
				'category' => 'Post', 'label' => __( 'Post creation', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a post or page is created.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'low',
			),
			'post_update' => array(
				'category' => 'Post', 'label' => __( 'Post update', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a post or page is updated.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'low',
			),
			'post_published' => array(
				'category' => 'Post', 'label' => __( 'Post published', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a post or page becomes published.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'post_unpublished' => array(
				'category' => 'Post', 'label' => __( 'Post unpublished', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a published post or page is unpublished.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'post_trashed' => array(
				'category' => 'Post', 'label' => __( 'Post trashed', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a post or page is moved to the trash.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'post_deletion' => array(
				'category' => 'Post', 'label' => __( 'Post deletion', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a post or page is permanently deleted.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'high',
			),
			'post_category_update' => array(
				'category' => 'Post', 'label' => __( 'Post category update', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when post category assignments change.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'low',
			),
			'post_tag_update' => array(
				'category' => 'Post', 'label' => __( 'Post tag update', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when post tag assignments change.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'low',
			),
			'general_settings_update' => array(
				'category' => 'Setting', 'label' => __( 'General settings update', 'aegisify-audit-agent' ), 'remarks' => __( 'Records updates to core general site settings.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'writing_settings_update' => array(
				'category' => 'Setting', 'label' => __( 'Writing settings update', 'aegisify-audit-agent' ), 'remarks' => __( 'Records updates to core writing settings.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'reading_settings_update' => array(
				'category' => 'Setting', 'label' => __( 'Reading settings update', 'aegisify-audit-agent' ), 'remarks' => __( 'Records updates to core reading settings.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'discussion_settings_update' => array(
				'category' => 'Setting', 'label' => __( 'Discussion settings update', 'aegisify-audit-agent' ), 'remarks' => __( 'Records updates to core discussion settings.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'media_settings_update' => array(
				'category' => 'Setting', 'label' => __( 'Media settings update', 'aegisify-audit-agent' ), 'remarks' => __( 'Records updates to core media settings.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'permalink_settings_update' => array(
				'category' => 'Setting', 'label' => __( 'Permalink settings update', 'aegisify-audit-agent' ), 'remarks' => __( 'Records updates to permalink and URL structure settings.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'high',
			),
			'privacy_settings_update' => array(
				'category' => 'Setting', 'label' => __( 'Privacy settings update', 'aegisify-audit-agent' ), 'remarks' => __( 'Records updates to the privacy policy page setting.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'tag_creation' => array(
				'category' => 'Tag', 'label' => __( 'Tag creation', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a new tag is created.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'low',
			),
			'tag_update' => array(
				'category' => 'Tag', 'label' => __( 'Tag update', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when an existing tag is edited.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'low',
			),
			'tag_deletion' => array(
				'category' => 'Tag', 'label' => __( 'Tag deletion', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a tag is permanently deleted.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'theme_installation' => array(
				'category' => 'Theme', 'label' => __( 'Theme installation', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when one or more themes are installed.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'theme_update' => array(
				'category' => 'Theme', 'label' => __( 'Theme update', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when one or more themes are updated.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'theme_switch' => array(
				'category' => 'Theme', 'label' => __( 'Theme switch', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when the active theme is switched.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'theme_deletion' => array(
				'category' => 'Theme', 'label' => __( 'Theme deletion', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a theme is deleted.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'high',
			),
			'user_registration' => array(
				'category' => 'User', 'label' => __( 'Registration', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a new user account is created or self-registered.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'user_update' => array(
				'category' => 'User', 'label' => __( 'User update', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a user account profile is updated.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'user_login' => array(
				'category' => 'User', 'label' => __( 'Login', 'aegisify-audit-agent' ), 'remarks' => __( 'Records successful user logins.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'low',
			),
			'user_login_failed' => array(
				'category' => 'User', 'label' => __( 'Login failed', 'aegisify-audit-agent' ), 'remarks' => __( 'Records failed user login attempts.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'high',
			),
			'user_logout' => array(
				'category' => 'User', 'label' => __( 'Logout', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a user logs out.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'low',
			),
			'user_password_reset' => array(
				'category' => 'User', 'label' => __( 'Password reset', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a password reset is requested.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'high',
			),
			'user_deletion' => array(
				'category' => 'User', 'label' => __( 'User deletion', 'aegisify-audit-agent' ), 'remarks' => __( 'Records when a user account is deleted.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'high',
			),
			'sensor_update' => array(
				'category' => 'Aegisify Audit', 'label' => __( 'Sensor update', 'aegisify-audit-agent' ), 'remarks' => __( 'Records sensor enable, disable, and severity changes.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
			'settings_update' => array(
				'category' => 'Aegisify Audit', 'label' => __( 'Settings update', 'aegisify-audit-agent' ), 'remarks' => __( 'Records updates to Aegisify Audit Agent settings.', 'aegisify-audit-agent' ), 'default_enabled' => 1, 'default_severity' => 'medium',
			),
		);
	}

	public static function get_sensor_definitions_for_display() {
		$definitions = self::get_sensor_definitions();
		$category_order = array(
			'User'           => 10,
			'Setting'        => 20,
			'Plugin'         => 30,
			'Theme'          => 40,
			'Core'           => 50,
			'File'           => 60,
			'Post'           => 70,
			'Category'       => 80,
			'Tag'            => 90,
			'Comment'        => 100,
			'Media'          => 110,
			'Menu'           => 120,
			'Option'         => 130,
			'Aegisify Audit' => 140,
		);

		uasort(
			$definitions,
			static function ( $left, $right ) use ( $category_order ) {
				$left_category  = isset( $left['category'] ) ? (string) $left['category'] : '';
				$right_category = isset( $right['category'] ) ? (string) $right['category'] : '';
				$left_rank      = isset( $category_order[ $left_category ] ) ? (int) $category_order[ $left_category ] : 999;
				$right_rank     = isset( $category_order[ $right_category ] ) ? (int) $category_order[ $right_category ] : 999;

				if ( $left_rank !== $right_rank ) {
					return $left_rank <=> $right_rank;
				}

				$left_label  = isset( $left['label'] ) ? wp_strip_all_tags( (string) $left['label'] ) : '';
				$right_label = isset( $right['label'] ) ? wp_strip_all_tags( (string) $right['label'] ) : '';

				return strcasecmp( $left_label, $right_label );
			}
		);

		return $definitions;
	}

	public static function get_default_settings() {
		$settings = array(
			'sensors'    => array(),
			'updated_at' => current_time( 'mysql' ),
		);
		foreach ( self::get_sensor_definitions() as $sensor_key => $definition ) {
			$settings['sensors'][ $sensor_key ] = array(
				'enabled'  => ! empty( $definition['default_enabled'] ) ? 1 : 0,
				'severity' => self::sanitize_severity_key( isset( $definition['default_severity'] ) ? $definition['default_severity'] : 'low' ),
			);
		}

		return $settings;
	}

	public static function get_settings() {
		if ( null !== self::$settings_cache && is_array( self::$settings_cache ) ) {
			return self::$settings_cache;
		}

		self::ensure_storage();
		$stored   = get_option( self::SETTINGS_OPTION, array() );
		$defaults = self::get_default_settings();
		if ( ! is_array( $stored ) ) {
			$stored = array();
		}

		$settings = wp_parse_args( $stored, $defaults );
		$settings['sensors'] = is_array( $settings['sensors'] ) ? $settings['sensors'] : array();
		foreach ( $defaults['sensors'] as $sensor_key => $sensor_defaults ) {
			$current = isset( $settings['sensors'][ $sensor_key ] ) && is_array( $settings['sensors'][ $sensor_key ] ) ? $settings['sensors'][ $sensor_key ] : array();
			$settings['sensors'][ $sensor_key ] = array(
				'enabled'  => empty( $current['enabled'] ) ? 0 : 1,
				'severity' => self::sanitize_severity_key( isset( $current['severity'] ) ? $current['severity'] : $sensor_defaults['severity'] ),
			);
		}

		self::$settings_cache = $settings;
		return $settings;
	}

	public static function save_settings( array $raw_settings ) {
		$defaults = self::get_default_settings();
		$settings = array(
			'sensors'    => array(),
			'updated_at' => current_time( 'mysql' ),
		);
		$raw_sensors = isset( $raw_settings['sensors'] ) && is_array( $raw_settings['sensors'] ) ? $raw_settings['sensors'] : array();
		foreach ( $defaults['sensors'] as $sensor_key => $sensor_defaults ) {
			$current = isset( $raw_sensors[ $sensor_key ] ) && is_array( $raw_sensors[ $sensor_key ] ) ? $raw_sensors[ $sensor_key ] : array();
			$settings['sensors'][ $sensor_key ] = array(
				'enabled'  => empty( $current['enabled'] ) ? 0 : 1,
				'severity' => self::sanitize_severity_key( isset( $current['severity'] ) ? $current['severity'] : $sensor_defaults['severity'] ),
			);
		}
		update_option( self::SETTINGS_OPTION, $settings, false );
		self::$settings_cache = $settings;
		return $settings;
	}

	public static function get_events( $limit = 100 ) {
		self::ensure_storage();
		$events = get_option( self::EVENTS_OPTION, array() );
		if ( ! is_array( $events ) ) {
			$events = array();
		}

		if ( ! empty( self::$pending_events ) ) {
			$events = array_merge( self::$pending_events, $events );
			if ( count( $events ) > self::MAX_EVENTS ) {
				$events = array_slice( $events, 0, self::MAX_EVENTS );
			}
		}

		$limit = max( 1, absint( $limit ) );
		return array_slice( $events, 0, $limit );
	}

	public static function build_rest_payload( $limit = 200, $since_id = 0 ) {
		$limit    = max( 1, min( 500, absint( $limit ) ) );
		$since_id = max( 0, absint( $since_id ) );
		$events   = self::get_events( 500 );

		if ( $since_id > 0 ) {
			$events = array_values(
				array_filter(
					$events,
					function ( $event ) use ( $since_id ) {
						return ! empty( $event['id'] ) && absint( $event['id'] ) > $since_id;
					}
				)
			);
		}

		$events        = array_slice( $events, 0, $limit );
		$last_event_id = self::get_current_last_id();

		return array(
			'generated_at'  => current_time( 'mysql' ),
			'last_event_id' => $last_event_id,
			'event_count'   => count( $events ),
			'sensor_count'  => count( self::get_sensor_definitions() ),
			'sensors'       => self::get_sensor_export(),
			'events'        => $events,
		);
	}

	public static function get_sensor_export() {
		$definitions = self::get_sensor_definitions();
		$settings    = self::get_settings();
		$export      = array();
		foreach ( $definitions as $sensor_key => $definition ) {
			$sensor_settings = isset( $settings['sensors'][ $sensor_key ] ) ? $settings['sensors'][ $sensor_key ] : array();
			$description = isset( $definition['remarks'] ) ? (string) $definition['remarks'] : '';
			$export[] = array(
				'key'         => $sensor_key,
				'category'    => isset( $definition['category'] ) ? (string) $definition['category'] : '',
				'label'       => isset( $definition['label'] ) ? (string) $definition['label'] : '',
				'remarks'     => $description,
				'description' => $description,
				'enabled'     => ! empty( $sensor_settings['enabled'] ),
				'severity'    => self::format_severity_label( isset( $sensor_settings['severity'] ) ? $sensor_settings['severity'] : ( isset( $definition['default_severity'] ) ? $definition['default_severity'] : 'low' ) ),
			);
		}
		return $export;
	}

	public static function log_event( $sensor_key, $summary, array $details = array(), array $args = array() ) {
		if ( self::$request_event_count >= self::MAX_EVENTS_PER_REQUEST ) {
			return false;
		}

		$definitions = self::get_sensor_definitions();
		if ( empty( $definitions[ $sensor_key ] ) ) {
			return false;
		}

		$settings        = self::get_settings();
		$sensor_settings = isset( $settings['sensors'][ $sensor_key ] ) ? $settings['sensors'][ $sensor_key ] : array();
		$force_log       = ! empty( $args['force'] );
		if ( ! $force_log && empty( $sensor_settings['enabled'] ) ) {
			return false;
		}

		$severity_key = self::sanitize_severity_key( isset( $sensor_settings['severity'] ) ? $sensor_settings['severity'] : ( isset( $definitions[ $sensor_key ]['default_severity'] ) ? $definitions[ $sensor_key ]['default_severity'] : 'low' ) );

		$event = array(
			'id'           => self::next_event_id(),
			'recorded_at'  => current_time( 'mysql' ),
			'category'     => isset( $definitions[ $sensor_key ]['category'] ) ? (string) $definitions[ $sensor_key ]['category'] : '',
			'sensor_key'   => $sensor_key,
			'sensor_label' => isset( $definitions[ $sensor_key ]['label'] ) ? (string) $definitions[ $sensor_key ]['label'] : '',
			'severity_key' => $severity_key,
			'severity'     => self::format_severity_label( $severity_key ),
			'summary'      => sanitize_text_field( (string) $summary ),
			'actor'        => self::detect_actor( $args ),
			'details'      => self::sanitize_context( $details ),
		);

		self::$pending_events[] = $event;
		self::$request_event_count++;
		return $event;
	}

	protected static function get_current_last_id() {
		if ( null === self::$pending_last_id ) {
			self::$pending_last_id = absint( get_option( self::LAST_ID_OPTION, 0 ) );
		}

		return self::$pending_last_id;
	}

	protected static function next_event_id() {
		$current = self::get_current_last_id() + 1;
		self::$pending_last_id = $current;
		return $current;
	}

	public static function flush_pending_events() {
		if ( empty( self::$pending_events ) ) {
			if ( null !== self::$pending_last_id ) {
				update_option( self::LAST_ID_OPTION, absint( self::$pending_last_id ), false );
			}
			return;
		}

		self::ensure_storage();
		$events = get_option( self::EVENTS_OPTION, array() );
		if ( ! is_array( $events ) ) {
			$events = array();
		}

		$events = array_merge( self::$pending_events, $events );
		if ( count( $events ) > self::MAX_EVENTS ) {
			$events = array_slice( $events, 0, self::MAX_EVENTS );
		}

		update_option( self::EVENTS_OPTION, $events, false );
		if ( null !== self::$pending_last_id ) {
			update_option( self::LAST_ID_OPTION, absint( self::$pending_last_id ), false );
		}

		self::$pending_events = array();
	}

	protected static function sanitize_severity_key( $severity ) {
		$severity = sanitize_key( (string) $severity );
		$allowed  = array_keys( self::get_severity_levels() );
		return in_array( $severity, $allowed, true ) ? $severity : 'low';
	}

	protected static function format_severity_label( $severity ) {
		$levels = self::get_severity_levels();
		$key    = self::sanitize_severity_key( $severity );
		return isset( $levels[ $key ] ) ? $levels[ $key ] : ucfirst( $key );
	}

	protected static function detect_actor( array $args ) {
		$actor = array(
			'id'    => 0,
			'label' => __( 'System', 'aegisify-audit-agent' ),
			'roles' => array(),
		);

		if ( isset( $args['actor_id'] ) ) {
			$user = get_userdata( absint( $args['actor_id'] ) );
			if ( $user instanceof WP_User ) {
				$actor['id']    = (int) $user->ID;
				$actor['label'] = sanitize_text_field( (string) $user->user_login );
				$actor['roles'] = array_values( array_map( 'sanitize_key', (array) $user->roles ) );
			}
		}

		if ( 0 === $actor['id'] && is_user_logged_in() ) {
			$current_user = wp_get_current_user();
			if ( $current_user instanceof WP_User && ! empty( $current_user->ID ) ) {
				$actor['id']    = (int) $current_user->ID;
				$actor['label'] = sanitize_text_field( (string) $current_user->user_login );
				$actor['roles'] = array_values( array_map( 'sanitize_key', (array) $current_user->roles ) );
			}
		}

		if ( ! empty( $args['actor_label'] ) ) {
			$actor['label'] = sanitize_text_field( (string) $args['actor_label'] );
		}

		return $actor;
	}

	protected static function sanitize_context( $value, $depth = 0 ) {
		if ( $depth > 4 ) {
			return null;
		}
		if ( is_bool( $value ) || is_int( $value ) || is_float( $value ) || null === $value ) {
			return $value;
		}
		if ( is_string( $value ) ) {
			$trimmed = sanitize_text_field( $value );
			return strlen( $trimmed ) > 500 ? substr( $trimmed, 0, 500 ) . '…' : $trimmed;
		}
		if ( is_array( $value ) ) {
			$clean = array();
			foreach ( $value as $key => $item ) {
				$clean_key = is_string( $key ) ? sanitize_key( $key ) : absint( $key );
				$clean[ $clean_key ] = self::sanitize_context( $item, $depth + 1 );
			}
			return $clean;
		}
		if ( $value instanceof WP_Post ) {
			return array(
				'id'    => (int) $value->ID,
				'type'  => sanitize_key( (string) $value->post_type ),
				'title' => sanitize_text_field( (string) $value->post_title ),
			);
		}
		if ( $value instanceof WP_User ) {
			return array(
				'id'    => (int) $value->ID,
				'login' => sanitize_text_field( (string) $value->user_login ),
				'roles' => array_values( array_map( 'sanitize_key', (array) $value->roles ) ),
			);
		}
		if ( $value instanceof WP_Comment ) {
			return array(
				'id'      => (int) $value->comment_ID,
				'post_id' => (int) $value->comment_post_ID,
				'status'  => sanitize_key( (string) $value->comment_approved ),
			);
		}
		if ( is_object( $value ) ) {
			return self::sanitize_context( get_object_vars( $value ), $depth + 1 );
		}
		return sanitize_text_field( (string) $value );
	}

	protected static function mark_request_event_seen( $key ) {
		$key = sanitize_key( (string) $key );
		if ( isset( self::$request_dedupe[ $key ] ) ) {
			return true;
		}
		self::$request_dedupe[ $key ] = true;
		return false;
	}

	protected static function is_agent_rest_request_context() {
		if ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) {
			return false;
		}

		$route = isset( $_GET['rest_route'] ) ? sanitize_text_field( wp_unslash( $_GET['rest_route'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only request classification.
		if ( '' !== $route && 0 === strpos( $route, '/aegisify-agent/v1/' ) ) {
			return true;
		}

		$request_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Read-only request classification.
		if ( '' !== $request_uri && false !== strpos( $request_uri, '/wp-json/aegisify-agent/v1/' ) ) {
			return true;
		}

		return false;
	}

	protected static function is_mutating_request_context() {
		if ( self::is_agent_rest_request_context() ) {
			return false;
		}

		if ( wp_doing_ajax() || wp_doing_cron() ) {
			return true;
		}

		if ( defined( 'WP_CLI' ) && WP_CLI ) {
			return true;
		}

		$method = isset( $_SERVER['REQUEST_METHOD'] ) ? strtoupper( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Normalized request method only.
		if ( in_array( $method, array( 'POST', 'PUT', 'PATCH', 'DELETE' ), true ) ) {
			return true;
		}

		if ( ! is_admin() ) {
			return false;
		}

		$action     = isset( $_REQUEST['action'] ) ? sanitize_key( wp_unslash( $_REQUEST['action'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only request classification.
		$action_two = isset( $_REQUEST['action2'] ) ? sanitize_key( wp_unslash( $_REQUEST['action2'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only request classification.
		$has_nonce  = isset( $_REQUEST['_wpnonce'] ) || isset( $_REQUEST['_ajax_nonce'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only request classification.

		return $has_nonce && ( '' !== $action || '' !== $action_two );
	}

	protected static function is_sensor_enabled( $sensor_key ) {
		$settings = self::get_settings();
		return ! empty( $settings['sensors'][ $sensor_key ]['enabled'] );
	}

	protected static function is_supported_post( $post ) {
		if ( ! $post instanceof WP_Post ) {
			return false;
		}
		if ( in_array( $post->post_type, array( 'attachment', 'revision', 'nav_menu_item', 'custom_css', 'customize_changeset' ), true ) ) {
			return false;
		}
		return true;
	}

	protected static function get_core_option_keys() {
		return array(
			'active_plugins', 'admin_email', 'blog_charset', 'blog_public', 'blogdescription', 'blogname', 'category_base', 'comment_max_links',
			'comment_moderation', 'comment_previously_approved', 'comment_registration', 'comments_notify', 'date_format', 'default_category',
			'default_comment_status', 'default_email_category', 'default_link_category', 'default_ping_status', 'default_pingback_flag',
			'default_post_format', 'default_role', 'default_comment_status', 'finished_splitting_shared_terms', 'gmt_offset', 'home',
			'illegal_names', 'links_recently_updated_prepend', 'mailserver_login', 'mailserver_pass', 'mailserver_port', 'mailserver_url',
			'medium_crop', 'medium_size_h', 'medium_size_w', 'moderation_keys', 'moderation_notify', 'page_comments', 'page_for_posts',
			'page_on_front', 'permalink_structure', 'posts_per_page', 'posts_per_rss', 'privacy_policy_page', 'require_name_email',
			'rewrite_rules', 'rss_excerpt_length', 'rss_use_excerpt', 'show_avatars', 'show_on_front', 'site_icon', 'siteurl', 'start_of_week',
			'tag_base', 'template', 'thread_comments', 'thread_comments_depth', 'thumbnail_crop', 'thumbnail_size_h', 'thumbnail_size_w',
			'time_format', 'timezone_string', 'use_balanceTags', 'use_smilies', 'users_can_register', 'uploads_use_yearmonth_folders',
			'upload_path', 'upload_url_path', 'WPLANG', 'wp_attachment_pages_enabled', 'wp_page_for_privacy_policy', 'stylesheet'
		);
	}

	protected static function get_settings_sensor_by_option( $option_name ) {
		$map = array(
			'general_settings_update' => array( 'blogname', 'blogdescription', 'siteurl', 'home', 'admin_email', 'users_can_register', 'default_role', 'timezone_string', 'gmt_offset', 'start_of_week', 'date_format', 'time_format', 'WPLANG' ),
			'writing_settings_update' => array( 'default_category', 'default_post_format', 'default_email_category', 'mailserver_url', 'mailserver_login', 'mailserver_pass', 'mailserver_port', 'use_smilies', 'default_link_category', 'default_post_edit_rows', 'ping_sites' ),
			'reading_settings_update' => array( 'show_on_front', 'page_on_front', 'page_for_posts', 'posts_per_page', 'posts_per_rss', 'rss_use_excerpt', 'blog_public' ),
			'discussion_settings_update' => array( 'default_ping_status', 'default_comment_status', 'require_name_email', 'comment_registration', 'close_comments_for_old_posts', 'close_comments_days_old', 'thread_comments', 'thread_comments_depth', 'page_comments', 'comments_per_page', 'default_comments_page', 'comment_order', 'comments_notify', 'moderation_notify', 'comment_moderation', 'comment_max_links', 'moderation_keys', 'blacklist_keys', 'show_avatars', 'avatar_default', 'avatar_rating' ),
			'media_settings_update' => array( 'thumbnail_size_w', 'thumbnail_size_h', 'thumbnail_crop', 'medium_size_w', 'medium_size_h', 'large_size_w', 'large_size_h', 'uploads_use_yearmonth_folders', 'upload_path', 'upload_url_path' ),
			'permalink_settings_update' => array( 'permalink_structure', 'category_base', 'tag_base', 'rewrite_rules' ),
			'privacy_settings_update' => array( 'wp_page_for_privacy_policy', 'privacy_policy_page' ),
		);
		foreach ( $map as $sensor_key => $options ) {
			if ( in_array( $option_name, $options, true ) ) {
				return $sensor_key;
			}
		}
		return '';
	}

	protected static function should_ignore_option( $option_name ) {
		$option_name = (string) $option_name;
		if ( '' === $option_name ) {
			return true;
		}
		if ( in_array( $option_name, array( 'cron', self::EVENTS_OPTION, self::SETTINGS_OPTION, self::LAST_ID_OPTION, ASA_Telemetry::POLICY_OPTION, ASA_Logger::OPTION_KEY, ASA_Logger::RETENTION_OPTION_KEY ), true ) ) {
			return true;
		}
		return 0 === strpos( $option_name, '_transient_' ) || 0 === strpos( $option_name, '_site_transient_' ) || 0 === strpos( $option_name, 'asa_' );
	}

	protected static function resolve_file_editor_target() {
		// phpcs:disable WordPress.Security.NonceVerification.Missing -- WordPress core file-editor AJAX nonce is verified against the plugin/theme target before this activity snapshot records anything.
		$action = isset( $_POST['action'] ) ? sanitize_key( wp_unslash( $_POST['action'] ) ) : '';
		if ( 'edit-theme-plugin-file' !== $action ) {
			return array();
		}

		$file = isset( $_POST['file'] ) ? wp_normalize_path( sanitize_text_field( wp_unslash( $_POST['file'] ) ) ) : '';
		if ( '' === $file ) {
			return array();
		}

		$nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
		if ( '' === $nonce ) {
			return array();
		}

		if ( isset( $_POST['plugin'] ) ) {
			$plugin = wp_normalize_path( sanitize_text_field( wp_unslash( $_POST['plugin'] ) ) );
			if ( ! current_user_can( 'edit_plugins' ) || ! wp_verify_nonce( $nonce, 'edit-plugin_' . $file ) ) {
				return array();
			}

			$base = wp_normalize_path( WP_PLUGIN_DIR );
			$path = wp_normalize_path( trailingslashit( $base ) . ltrim( $file, '/' ) );
			return self::validate_scoped_file_path( $path, $base ) ? array(
				'sensor' => 'plugin_file_edit',
				'path'   => $path,
				'label'  => $plugin,
			) : array();
		}

		if ( isset( $_POST['theme'] ) ) {
			$theme_stylesheet = sanitize_text_field( wp_unslash( $_POST['theme'] ) );
			if ( ! current_user_can( 'edit_themes' ) || ! wp_verify_nonce( $nonce, 'edit-theme_' . $theme_stylesheet . '_' . $file ) ) {
				return array();
			}

			$theme = wp_get_theme( $theme_stylesheet );
			if ( ! $theme instanceof WP_Theme || ! $theme->exists() ) {
				return array();
			}
			$base = wp_normalize_path( $theme->get_stylesheet_directory() );
			$path = wp_normalize_path( trailingslashit( $base ) . ltrim( $file, '/' ) );
			return self::validate_scoped_file_path( $path, $base ) ? array(
				'sensor' => 'theme_file_edit',
				'path'   => $path,
				'label'  => $theme->get_stylesheet(),
			) : array();
		}

		// phpcs:enable WordPress.Security.NonceVerification.Missing
		return array();
	}

	protected static function validate_scoped_file_path( $path, $base ) {
		$path = wp_normalize_path( (string) $path );
		$base = wp_normalize_path( (string) $base );
		if ( '' === $path || '' === $base ) {
			return false;
		}
		return 0 === strpos( $path, trailingslashit( $base ) ) || $path === $base;
	}

	public function capture_file_editor_request() {
		$target = self::resolve_file_editor_target();
		if ( empty( $target['path'] ) ) {
			return;
		}
		$before_hash = ( file_exists( $target['path'] ) && is_readable( $target['path'] ) ) ? @md5_file( $target['path'] ) : false; // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- Safe checksum probe during file editor request shutdown tracking.
		self::$file_edit_watch = array(
			'sensor'      => $target['sensor'],
			'path'        => $target['path'],
			'label'       => $target['label'],
			'before_hash' => $before_hash,
		);
		add_action( 'shutdown', array( $this, 'maybe_log_file_editor_change' ), 9999 );
	}

	public function maybe_log_file_editor_change() {
		if ( empty( self::$file_edit_watch['path'] ) || ! file_exists( self::$file_edit_watch['path'] ) ) {
			return;
		}
		$after_hash = @md5_file( self::$file_edit_watch['path'] ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- Safe checksum probe after file editor request finishes.
		if ( false === $after_hash || (string) $after_hash === (string) self::$file_edit_watch['before_hash'] ) {
			return;
		}
		self::log_event(
			self::$file_edit_watch['sensor'],
			sprintf(
				/* translators: 1: plugin or theme label, 2: edited relative file path. */
				__( 'A file editor change was saved for %1$s: %2$s', 'aegisify-audit-agent' ),
				(string) self::$file_edit_watch['label'],
				basename( (string) self::$file_edit_watch['path'] )
			),
			array(
				'file' => wp_normalize_path( (string) self::$file_edit_watch['path'] ),
			),
			array()
		);
	}

	public function handle_created_term( $term_id, $tt_id, $taxonomy ) {
		unset( $tt_id );
		$term = get_term( $term_id, $taxonomy );
		if ( 'category' === $taxonomy ) {
			/* translators: Dynamic values are inserted into the translated message. */
			self::log_event( 'category_creation', sprintf( __( 'Category created: %s', 'aegisify-audit-agent' ), $term && ! is_wp_error( $term ) ? $term->name : '#' . absint( $term_id ) ), array( 'term_id' => absint( $term_id ), 'taxonomy' => sanitize_key( $taxonomy ) ) );
		} elseif ( 'post_tag' === $taxonomy ) {
			/* translators: Dynamic values are inserted into the translated message. */
			self::log_event( 'tag_creation', sprintf( __( 'Tag created: %s', 'aegisify-audit-agent' ), $term && ! is_wp_error( $term ) ? $term->name : '#' . absint( $term_id ) ), array( 'term_id' => absint( $term_id ), 'taxonomy' => sanitize_key( $taxonomy ) ) );
		}
	}

	public function handle_edited_term( $term_id, $tt_id, $taxonomy ) {
		unset( $tt_id );
		$term = get_term( $term_id, $taxonomy );
		if ( 'category' === $taxonomy ) {
			/* translators: Dynamic values are inserted into the translated message. */
			self::log_event( 'category_update', sprintf( __( 'Category updated: %s', 'aegisify-audit-agent' ), $term && ! is_wp_error( $term ) ? $term->name : '#' . absint( $term_id ) ), array( 'term_id' => absint( $term_id ), 'taxonomy' => sanitize_key( $taxonomy ) ) );
		} elseif ( 'post_tag' === $taxonomy ) {
			/* translators: Dynamic values are inserted into the translated message. */
			self::log_event( 'tag_update', sprintf( __( 'Tag updated: %s', 'aegisify-audit-agent' ), $term && ! is_wp_error( $term ) ? $term->name : '#' . absint( $term_id ) ), array( 'term_id' => absint( $term_id ), 'taxonomy' => sanitize_key( $taxonomy ) ) );
		}
	}

	public function handle_delete_term( $term_id, $tt_id, $taxonomy, $deleted_term, $object_ids ) {
		unset( $tt_id, $object_ids );
		$name = ( $deleted_term instanceof WP_Term ) ? $deleted_term->name : '#' . absint( $term_id );
		if ( 'category' === $taxonomy ) {
			/* translators: Dynamic values are inserted into the translated message. */
			self::log_event( 'category_deletion', sprintf( __( 'Category deleted: %s', 'aegisify-audit-agent' ), $name ), array( 'term_id' => absint( $term_id ), 'taxonomy' => sanitize_key( $taxonomy ) ) );
		} elseif ( 'post_tag' === $taxonomy ) {
			/* translators: Dynamic values are inserted into the translated message. */
			self::log_event( 'tag_deletion', sprintf( __( 'Tag deleted: %s', 'aegisify-audit-agent' ), $name ), array( 'term_id' => absint( $term_id ), 'taxonomy' => sanitize_key( $taxonomy ) ) );
		}
	}

	public function handle_comment_creation( $comment_id, $comment ) {
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'comment_creation', sprintf( __( 'Comment created on post #%d', 'aegisify-audit-agent' ), $comment instanceof WP_Comment ? (int) $comment->comment_post_ID : 0 ), array( 'comment_id' => absint( $comment_id ), 'post_id' => $comment instanceof WP_Comment ? (int) $comment->comment_post_ID : 0 ) );
	}

	public function handle_comment_update( $comment_id ) {
		$comment = get_comment( $comment_id );
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'comment_update', sprintf( __( 'Comment updated on post #%d', 'aegisify-audit-agent' ), $comment instanceof WP_Comment ? (int) $comment->comment_post_ID : 0 ), array( 'comment_id' => absint( $comment_id ), 'post_id' => $comment instanceof WP_Comment ? (int) $comment->comment_post_ID : 0 ) );
	}

	public function handle_comment_status_transition( $comment_id, $comment_status ) {
		$comment_status = sanitize_key( (string) $comment_status );
		$comment        = get_comment( $comment_id );
		$details        = array( 'comment_id' => absint( $comment_id ), 'status' => $comment_status, 'post_id' => $comment instanceof WP_Comment ? (int) $comment->comment_post_ID : 0 );
		if ( in_array( $comment_status, array( 'approve', 'approved', '1' ), true ) ) {
			self::log_event( 'comment_approved', __( 'Comment approved.', 'aegisify-audit-agent' ), $details );
		} elseif ( in_array( $comment_status, array( 'hold', 'unapprove', 'unapproved', '0' ), true ) ) {
			self::log_event( 'comment_unapproved', __( 'Comment put on hold.', 'aegisify-audit-agent' ), $details );
		}
	}

	public function handle_comment_trashed( $comment_id ) {
		self::log_event( 'comment_trashed', __( 'Comment moved to trash.', 'aegisify-audit-agent' ), array( 'comment_id' => absint( $comment_id ) ) );
	}

	public function handle_comment_restored( $comment_id ) {
		self::log_event( 'comment_restored', __( 'Comment restored from trash.', 'aegisify-audit-agent' ), array( 'comment_id' => absint( $comment_id ) ) );
	}

	public function handle_comment_spammed( $comment_id ) {
		self::log_event( 'comment_spammed', __( 'Comment marked as spam.', 'aegisify-audit-agent' ), array( 'comment_id' => absint( $comment_id ) ) );
	}

	public function handle_comment_deletion( $comment_id, $comment ) {
		self::log_event( 'comment_deletion', __( 'Comment permanently deleted.', 'aegisify-audit-agent' ), array( 'comment_id' => absint( $comment_id ), 'post_id' => $comment instanceof WP_Comment ? (int) $comment->comment_post_ID : 0 ) );
	}

	public function handle_media_creation( $attachment_id ) {
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'media_creation', sprintf( __( 'Media item created: #%d', 'aegisify-audit-agent' ), absint( $attachment_id ) ), array( 'attachment_id' => absint( $attachment_id ) ) );
	}

	public function handle_media_update( $attachment_id ) {
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'media_update', sprintf( __( 'Media item updated: #%d', 'aegisify-audit-agent' ), absint( $attachment_id ) ), array( 'attachment_id' => absint( $attachment_id ) ) );
	}

	public function handle_media_deletion( $attachment_id ) {
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'media_deletion', sprintf( __( 'Media item deleted: #%d', 'aegisify-audit-agent' ), absint( $attachment_id ) ), array( 'attachment_id' => absint( $attachment_id ) ) );
	}

	public function handle_menu_creation( $menu_id, $menu_data ) {
		$menu_name = is_array( $menu_data ) && ! empty( $menu_data['menu-name'] ) ? sanitize_text_field( wp_unslash( $menu_data['menu-name'] ) ) : '#' . absint( $menu_id );
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'menu_creation', sprintf( __( 'Menu created: %s', 'aegisify-audit-agent' ), $menu_name ), array( 'menu_id' => absint( $menu_id ) ) );
	}

	public function handle_menu_update( $menu_id, $menu_data ) {
		$menu_name = is_array( $menu_data ) && ! empty( $menu_data['menu-name'] ) ? sanitize_text_field( wp_unslash( $menu_data['menu-name'] ) ) : '#' . absint( $menu_id );
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'menu_update', sprintf( __( 'Menu updated: %s', 'aegisify-audit-agent' ), $menu_name ), array( 'menu_id' => absint( $menu_id ) ) );
	}

	public function handle_menu_deletion( $menu_id ) {
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'menu_deletion', sprintf( __( 'Menu deleted: #%d', 'aegisify-audit-agent' ), absint( $menu_id ) ), array( 'menu_id' => absint( $menu_id ) ) );
	}

	public function handle_option_added( $option, $value ) {
		if ( ! self::is_mutating_request_context() || self::should_ignore_option( $option ) || ! self::is_sensor_enabled( 'option_creation' ) ) {
			return;
		}
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'option_creation', sprintf( __( 'Option created: %s', 'aegisify-audit-agent' ), sanitize_key( (string) $option ) ), array( 'option' => sanitize_key( (string) $option ), 'value_type' => gettype( $value ) ) );
	}

	public function handle_option_updated( $option, $old_value, $value ) {
		unset( $old_value );
		$option = (string) $option;
		if ( ! self::is_mutating_request_context() || self::should_ignore_option( $option ) ) {
			return;
		}

		$settings_sensor        = self::get_settings_sensor_by_option( $option );
		$core_sensor            = in_array( $option, self::get_core_option_keys(), true ) ? 'option_update_wp_core' : 'option_update_non_wp_core';
		$settings_sensor_active = '' !== $settings_sensor && self::is_sensor_enabled( $settings_sensor );
		$core_sensor_active     = self::is_sensor_enabled( $core_sensor );

		if ( ! $settings_sensor_active && ! $core_sensor_active ) {
			return;
		}

		if ( $settings_sensor_active && ! self::mark_request_event_seen( $settings_sensor ) ) {
			/* translators: Dynamic values are inserted into the translated message. */
			self::log_event( $settings_sensor, sprintf( __( 'WordPress settings updated through %s.', 'aegisify-audit-agent' ), str_replace( '_', ' ', $settings_sensor ) ), array( 'option' => sanitize_key( $option ) ) );
		}

		if ( $core_sensor_active ) {
			/* translators: Dynamic values are inserted into the translated message. */
			self::log_event( $core_sensor, sprintf( __( 'Option updated: %s', 'aegisify-audit-agent' ), sanitize_key( $option ) ), array( 'option' => sanitize_key( $option ), 'value_type' => gettype( $value ) ) );
		}
	}

	public function handle_option_deleted( $option, $value = null ) {
		if ( ! self::is_mutating_request_context() || self::should_ignore_option( $option ) || ! self::is_sensor_enabled( 'option_deletion' ) ) {
			return;
		}
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'option_deletion', sprintf( __( 'Option deleted: %s', 'aegisify-audit-agent' ), sanitize_key( (string) $option ) ), array( 'option' => sanitize_key( (string) $option ), 'value_type' => gettype( $value ) ) );
	}

	public function handle_plugin_activation( $plugin, $network_wide ) {
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'plugin_activation', sprintf( __( 'Plugin activated: %s', 'aegisify-audit-agent' ), sanitize_text_field( (string) $plugin ) ), array( 'plugin' => sanitize_text_field( (string) $plugin ), 'network_wide' => ! empty( $network_wide ) ) );
	}

	public function handle_plugin_deactivation( $plugin, $network_wide ) {
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'plugin_deactivation', sprintf( __( 'Plugin deactivated: %s', 'aegisify-audit-agent' ), sanitize_text_field( (string) $plugin ) ), array( 'plugin' => sanitize_text_field( (string) $plugin ), 'network_wide' => ! empty( $network_wide ) ) );
	}

	public function handle_plugin_deletion( $plugin_file, $deleted ) {
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'plugin_deletion', sprintf( __( 'Plugin deleted: %s', 'aegisify-audit-agent' ), sanitize_text_field( (string) $plugin_file ) ), array( 'plugin' => sanitize_text_field( (string) $plugin_file ), 'deleted' => ! empty( $deleted ) ) );
	}

	public function handle_upgrader_process_complete( $upgrader, $options ) {
		unset( $upgrader );
		if ( ! is_array( $options ) ) {
			return;
		}
		$type   = isset( $options['type'] ) ? sanitize_key( (string) $options['type'] ) : '';
		$action = isset( $options['action'] ) ? sanitize_key( (string) $options['action'] ) : '';

		if ( 'core' === $type && 'update' === $action ) {
			self::log_event( 'core_update', __( 'WordPress core was updated.', 'aegisify-audit-agent' ), array( 'type' => $type, 'action' => $action ) );
			return;
		}

		if ( 'plugin' === $type ) {
			$plugins = ! empty( $options['plugins'] ) && is_array( $options['plugins'] ) ? array_map( 'sanitize_text_field', $options['plugins'] ) : array();
			if ( 'install' === $action ) {
				self::log_event( 'plugin_installation', __( 'One or more plugins were installed.', 'aegisify-audit-agent' ), array( 'plugins' => $plugins, 'action' => $action ) );
			} elseif ( 'update' === $action ) {
				self::log_event( 'plugin_update', __( 'One or more plugins were updated.', 'aegisify-audit-agent' ), array( 'plugins' => $plugins, 'action' => $action ) );
			}
			return;
		}

		if ( 'theme' === $type ) {
			$themes = ! empty( $options['themes'] ) && is_array( $options['themes'] ) ? array_map( 'sanitize_text_field', $options['themes'] ) : array();
			if ( 'install' === $action ) {
				self::log_event( 'theme_installation', __( 'One or more themes were installed.', 'aegisify-audit-agent' ), array( 'themes' => $themes, 'action' => $action ) );
			} elseif ( 'update' === $action ) {
				self::log_event( 'theme_update', __( 'One or more themes were updated.', 'aegisify-audit-agent' ), array( 'themes' => $themes, 'action' => $action ) );
			}
		}
	}

	public function handle_post_save( $post_id, $post, $update ) {
		if ( ! self::is_supported_post( $post ) || wp_is_post_revision( $post_id ) || ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) ) {
			return;
		}
		$details = array( 'post_id' => absint( $post_id ), 'post_type' => sanitize_key( (string) $post->post_type ), 'post_title' => sanitize_text_field( (string) $post->post_title ) );
		if ( $update ) {
			/* translators: Dynamic values are inserted into the translated message. */
			self::log_event( 'post_update', sprintf( __( 'Post updated: %s', 'aegisify-audit-agent' ), $post->post_title ), $details );
		} else {
			/* translators: Dynamic values are inserted into the translated message. */
			self::log_event( 'post_creation', sprintf( __( 'Post created: %s', 'aegisify-audit-agent' ), $post->post_title ), $details );
		}
	}

	public function handle_post_status_transition( $new_status, $old_status, $post ) {
		if ( ! self::is_supported_post( $post ) || 'auto-draft' === $new_status ) {
			return;
		}
		$details = array( 'post_id' => (int) $post->ID, 'post_type' => sanitize_key( (string) $post->post_type ), 'from' => sanitize_key( (string) $old_status ), 'to' => sanitize_key( (string) $new_status ) );
		if ( 'publish' === $new_status && 'publish' !== $old_status ) {
			/* translators: Dynamic values are inserted into the translated message. */
			self::log_event( 'post_published', sprintf( __( 'Post published: %s', 'aegisify-audit-agent' ), $post->post_title ), $details );
		} elseif ( 'publish' === $old_status && 'publish' !== $new_status ) {
			/* translators: Dynamic values are inserted into the translated message. */
			self::log_event( 'post_unpublished', sprintf( __( 'Post unpublished: %s', 'aegisify-audit-agent' ), $post->post_title ), $details );
		}
	}

	public function handle_post_trashed( $post_id ) {
		$post = get_post( $post_id );
		if ( ! self::is_supported_post( $post ) ) {
			return;
		}
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'post_trashed', sprintf( __( 'Post moved to trash: %s', 'aegisify-audit-agent' ), $post->post_title ), array( 'post_id' => absint( $post_id ), 'post_type' => sanitize_key( (string) $post->post_type ) ) );
	}

	public function handle_post_deletion( $post_id ) {
		$post = get_post( $post_id );
		if ( ! self::is_supported_post( $post ) ) {
			return;
		}
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'post_deletion', sprintf( __( 'Post permanently deleted: %s', 'aegisify-audit-agent' ), $post->post_title ), array( 'post_id' => absint( $post_id ), 'post_type' => sanitize_key( (string) $post->post_type ) ) );
	}

	public function handle_set_object_terms( $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids ) {
		unset( $tt_ids, $append, $old_tt_ids );
		$post = get_post( $object_id );
		if ( ! self::is_supported_post( $post ) ) {
			return;
		}
		if ( 'category' === $taxonomy ) {
			/* translators: Dynamic values are inserted into the translated message. */
			self::log_event( 'post_category_update', sprintf( __( 'Post category assignments updated: %s', 'aegisify-audit-agent' ), $post->post_title ), array( 'post_id' => absint( $object_id ), 'terms' => array_map( 'absint', (array) $terms ) ) );
		} elseif ( 'post_tag' === $taxonomy ) {
			/* translators: Dynamic values are inserted into the translated message. */
			self::log_event( 'post_tag_update', sprintf( __( 'Post tag assignments updated: %s', 'aegisify-audit-agent' ), $post->post_title ), array( 'post_id' => absint( $object_id ), 'terms' => array_map( 'absint', (array) $terms ) ) );
		}
	}

	public function handle_theme_switch( $new_name, $new_theme, $old_theme ) {
		$new_label = $new_theme instanceof WP_Theme ? $new_theme->get_stylesheet() : sanitize_text_field( (string) $new_name );
		$old_label = $old_theme instanceof WP_Theme ? $old_theme->get_stylesheet() : '';
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'theme_switch', sprintf( __( 'Theme switched to: %s', 'aegisify-audit-agent' ), $new_label ), array( 'new_theme' => $new_label, 'old_theme' => $old_label ) );
	}

	public function handle_theme_deletion( $stylesheet, $deleted ) {
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'theme_deletion', sprintf( __( 'Theme deleted: %s', 'aegisify-audit-agent' ), sanitize_text_field( (string) $stylesheet ) ), array( 'theme' => sanitize_text_field( (string) $stylesheet ), 'deleted' => ! empty( $deleted ) ) );
	}

	public function handle_user_registration( $user_id ) {
		$user = get_userdata( $user_id );
		$details = array(
			'target_user_id'    => absint( $user_id ),
			'target_user_login' => $user instanceof WP_User ? sanitize_text_field( (string) $user->user_login ) : '',
		);
		$args = is_user_logged_in() ? array() : array( 'actor_label' => $user instanceof WP_User ? sanitize_text_field( (string) $user->user_login ) : '#' . absint( $user_id ) );
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'user_registration', sprintf( __( 'User registered: %s', 'aegisify-audit-agent' ), $user instanceof WP_User ? $user->user_login : '#' . absint( $user_id ) ), $details, $args );
	}

	public function handle_user_update( $user_id, $old_user_data, $userdata ) {
		unset( $old_user_data );
		$target_user_label = ! empty( $userdata['user_login'] ) ? sanitize_text_field( (string) $userdata['user_login'] ) : '';
		$details = array(
			'target_user_id'    => absint( $user_id ),
			'target_user_login' => $target_user_label,
		);
		$args = is_user_logged_in() ? array() : array( 'actor_label' => '' !== $target_user_label ? $target_user_label : '#' . absint( $user_id ) );
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'user_update', sprintf( __( 'User updated: %s', 'aegisify-audit-agent' ), '' !== $target_user_label ? $target_user_label : '#' . absint( $user_id ) ), $details, $args );
	}

	public function handle_user_login( $user_login, $user ) {
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'user_login', sprintf( __( 'User logged in: %s', 'aegisify-audit-agent' ), sanitize_text_field( (string) $user_login ) ), array( 'user_id' => $user instanceof WP_User ? (int) $user->ID : 0 ), array( 'actor_id' => $user instanceof WP_User ? (int) $user->ID : 0 ) );
	}

	public function handle_user_login_failed( $username, $error ) {
		$details = array(
			'username'   => sanitize_text_field( (string) $username ),
			'error_code' => $error instanceof WP_Error ? sanitize_key( (string) $error->get_error_code() ) : '',
		);
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'user_login_failed', sprintf( __( 'Login failed for: %s', 'aegisify-audit-agent' ), sanitize_text_field( (string) $username ) ), $details, array( 'actor_label' => sanitize_text_field( (string) $username ) ) );
	}

	public function handle_user_logout() {
		$current_user = wp_get_current_user();
		self::log_event( 'user_logout', __( 'User logged out.', 'aegisify-audit-agent' ), array(), array( 'actor_id' => $current_user instanceof WP_User ? (int) $current_user->ID : 0 ) );
	}

	public function handle_password_reset_requested( $user_login ) {
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'user_password_reset', sprintf( __( 'Password reset requested for: %s', 'aegisify-audit-agent' ), sanitize_text_field( (string) $user_login ) ), array( 'user_login' => sanitize_text_field( (string) $user_login ) ), array( 'actor_label' => sanitize_text_field( (string) $user_login ) ) );
	}

	public function handle_user_deletion( $user_id, $reassign, $user ) {
		unset( $reassign );
		$details = array(
			'target_user_id'    => absint( $user_id ),
			'target_user_login' => $user instanceof WP_User ? sanitize_text_field( (string) $user->user_login ) : '',
		);
		$args = is_user_logged_in() ? array() : array( 'actor_label' => $user instanceof WP_User ? sanitize_text_field( (string) $user->user_login ) : '#' . absint( $user_id ) );
		/* translators: Dynamic values are inserted into the translated message. */
		self::log_event( 'user_deletion', sprintf( __( 'User deleted: %s', 'aegisify-audit-agent' ), $user instanceof WP_User ? $user->user_login : '#' . absint( $user_id ) ), $details, $args );
	}
}
