<?php

/**
 * Plugin Name: Disable Comments
 * Plugin URI: https://wordpress.org/plugins/disable-comments/
 * Description: Allows administrators to globally disable comments on their site. Comments can be disabled according to post type. You could bulk delete comments using Tools.
 * Version: 2.7.0
 * Author: WPDeveloper
 * Author URI: https://wpdeveloper.com
 * License: GPL-3.0+
 * License URI: https://www.gnu.org/licenses/gpl-3.0.html
 * Text Domain: disable-comments
 * Domain Path: /languages/
 *
 * @package Disable_Comments
 */

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

class Disable_Comments {
	const DB_VERSION         = 8;
	private static $instance = null;
	private $options;
	public  $networkactive;
	public  $tracker;
	public  $is_CLI;
	public  $sitewide_settings;
	public  $setup_notice_flag;
	private $modified_types = array();

	public static function get_instance() {
		if (is_null(self::$instance)) {
			self::$instance = new self;
		}
		return self::$instance;
	}

	function __construct() {
		define('DC_VERSION', '2.7.0');
		define('DC_PLUGIN_SLUG', 'disable_comments_settings');
		define('DC_PLUGIN_ROOT_PATH', dirname(__FILE__));
		define('DC_PLUGIN_VIEWS_PATH', DC_PLUGIN_ROOT_PATH . '/views/');
		define('DC_PLUGIN_ROOT_URI', plugins_url("/", __FILE__));
		define('DC_ASSETS_URI', DC_PLUGIN_ROOT_URI . 'assets/');

		// save settings
		add_action('wp_ajax_disable_comments_save_settings', array($this, 'disable_comments_settings'));
		add_action('wp_ajax_disable_comments_delete_comments', array($this, 'delete_comments_settings'));
		add_action('wp_ajax_get_sub_sites', array($this, 'get_sub_sites'));

		// Including cli.php
		if (defined('WP_CLI') && WP_CLI) {
			add_action('init', array($this, 'enable_cli'), 9999);
		}

		// are we network activated?
		$this->networkactive = (is_multisite() && array_key_exists(plugin_basename(__FILE__), (array) get_site_option('active_sitewide_plugins')));
		$this->is_CLI = defined('WP_CLI') && WP_CLI;

		$this->sitewide_settings = get_site_option('disable_comments_sitewide_settings', false);
		// Load options.
		// Uses is_network_admin_ajax_context() (routing hint, not capability
		// check) because current_user_can() is unavailable during plugin
		// construction — pluggable.php hasn't loaded yet. This only controls
		// which options table is READ (site vs blog) — writes are always
		// gated by capability checks in the AJAX handlers and settings_page().
		if ($this->networkactive && ($this->is_network_admin_ajax_context() || $this->sitewide_settings !== '1')) {
			$this->options = get_site_option('disable_comments_options', array());
			$this->options['disabled_sites'] = $this->get_disabled_sites();

			$blog_id = get_current_blog_id();
			if (
				!$this->is_network_admin_ajax_context() && (
					empty($this->options['disabled_sites']) ||
					// if site disabled
					empty($this->options['disabled_sites']["site_$blog_id"])
				)
			) {
				$this->options = [
					'remove_everywhere' => false,
					'disabled_post_types' => array(),
					'extra_post_types' => array(),
					'disabled_sites' => array(),
					'remove_xmlrpc_comments' => 0,
					'remove_rest_API_comments' => 0,
					'show_existing_comments' => false,
					'allowed_comment_types' => array(),
					'settings_saved' => true,
					'db_version' => $this->options['db_version']
				];
			}
		} else {
			$this->options = get_option('disable_comments_options', array());
			$not_configured = empty($this->options) || empty($this->options['settings_saved']);

			if (is_multisite() && $not_configured && $this->sitewide_settings == '1') {
				$this->options = get_site_option('disable_comments_options', array());
				$this->options['is_network_options'] = true;
			}
		}


		// If it looks like first run, check compat.
		if (empty($this->options)) {
			$this->check_compatibility();
		}

		$this->options['sitewide_settings'] = ($this->sitewide_settings == '1');

		// Upgrade DB if necessary.
		$this->check_db_upgrades();
		$this->check_upgrades();

		add_action('plugins_loaded', [$this, 'init_filters']);
		add_action('wp_loaded', [$this, 'start_plugin_usage_tracking']);

		// Add Site Health integration
		add_filter('debug_information', array($this, 'add_site_health_info'));
	}

	/**
	 * Routing hint: is this request from the network-admin screen?
	 *
	 * During AJAX, WP's is_network_admin() is always false, so the JS
	 * appends ?is_network_admin=1 to ajaxurl (value set server-side in
	 * admin_enqueue_scripts via is_network_admin()). The GET param is
	 * client-supplied and therefore forgeable — never use this method
	 * alone for authorization. Always pair with can_network_admin_ajax_context()
	 * or an explicit current_user_can() check.
	 */
	private function is_network_admin_ajax_context() {
		if (!$this->networkactive) {
			return false;
		}
		if (is_network_admin()) {
			return true;
		}
		if (defined('DOING_AJAX') && DOING_AJAX && is_multisite() && isset($_GET['is_network_admin'])) {
			$param = sanitize_text_field(wp_unslash($_GET['is_network_admin']));
			return $param === '1';
		}
		return false;
	}

	/**
	 * Capability-gated network-admin context check.
	 *
	 * Returns true only when the request appears to come from the
	 * network-admin screen AND the current user holds
	 * manage_network_plugins. Safe for authorization decisions.
	 */
	private function can_network_admin_ajax_context() {
		if ($this->is_network_admin_ajax_context() && current_user_can('manage_network_plugins')) {
			return true;
		}

		return false;
	}

	/**
	 * Enable CLI
	 * @since 2.0.0
	 */
	public function enable_cli() {
		require_once DC_PLUGIN_ROOT_PATH . "/includes/cli.php";
		new Disable_Comment_Command($this);
	}

	public function admin_notice() {
		if ($this->tracker instanceof DisableComments_Plugin_Tracker) {
			if (isset($this->setup_notice_flag) && $this->setup_notice_flag === true) {
				return;
			}
			$current_screen = get_current_screen()->id;
			$has_caps = $this->networkactive && is_network_admin() ? current_user_can('manage_network_plugins') : current_user_can('manage_options');
			// if( ! in_array( $current_screen, ['settings_page_disable_comments_settings', 'settings_page_disable_comments_settings-network']) && $has_caps ) {
			if ($has_caps && in_array($current_screen, ['dashboard-network', 'dashboard'])) {
				$this->tracker->notice();
			}
		}
	}

	public function start_plugin_usage_tracking() {
		if ($this->networkactive && !$this->options['sitewide_settings']) {
			$this->tracker = null;
			return;
		}
		if (!class_exists('DisableComments_Plugin_Tracker')) {
			include_once(DC_PLUGIN_ROOT_PATH . '/includes/class-plugin-usage-tracker.php');
		}
		$tracker = $this->tracker = DisableComments_Plugin_Tracker::get_instance(__FILE__, [
			'opt_in' => true,
			'goodbye_form' => true,
			'item_id' => 'b0112c9030af6ba53de4'
		]);
		$tracker->set_notice_options(array(
			'notice' => __('Want to help make Disable Comments even better?', 'disable-comments'),
			'extra_notice' => __('We collect non-sensitive diagnostic data and plugin usage information. Your site URL, WordPress & PHP version, plugins & themes and email address to send you the discount coupon. This data lets us make sure this plugin always stays compatible with the most popular plugins and themes. No spam, I promise.', 'disable-comments'),
		));
		$tracker->init();
	}

	private function check_compatibility() {
		if (version_compare($GLOBALS['wp_version'], '4.7', '<')) {
			require_once(ABSPATH . 'wp-admin/includes/plugin.php');
			deactivate_plugins(__FILE__);

			// @phpcs:ignore WordPress.Security.NonceVerification.Recommended
			if (isset($_GET['action']) && ($_GET['action'] == 'activate' || $_GET['action'] == 'error_scrape')) {
				// translators: %s: WordPress version no.
				exit(sprintf(esc_html__('Disable Comments requires WordPress version %s or greater.', 'disable-comments'), '4.7'));
			}
		}
	}

	private function check_db_upgrades() {
		$old_ver = isset($this->options['db_version']) ? $this->options['db_version'] : 0;
		if ($old_ver < self::DB_VERSION) {
			if ($old_ver < 2) {
				// upgrade options from version 0.2.1 or earlier to 0.3.
				$this->options['disabled_post_types'] = get_option('disable_comments_post_types', array());
				delete_option('disable_comments_post_types');
			}
			if ($old_ver < 5) {
				// simple is beautiful - remove multiple settings in favour of one.
				$this->options['remove_everywhere'] = isset($this->options['remove_admin_menu_comments']) ? $this->options['remove_admin_menu_comments'] : false;
				foreach (array('remove_admin_menu_comments', 'remove_admin_bar_comments', 'remove_recent_comments', 'remove_discussion', 'remove_rc_widget') as $v) {
					unset($this->options[$v]);
				}
			}
			if ($old_ver < 7 && function_exists('get_sites')) {
				$this->options['disabled_sites'] = [];
				$dc_options = get_site_option('disable_comments_options', array());

				foreach (get_sites(['number' => 0, 'fields' => 'ids']) as $blog_id) {
					if (isset($dc_options['disabled_sites'])) {
						$this->options['disabled_sites']["site_$blog_id"] = in_array($blog_id, $dc_options['disabled_sites']);
					} else {
						$this->options['disabled_sites']["site_$blog_id"] = true;
					}
				}
				$this->options['disabled_sites'] = $this->get_disabled_sites();
			}

			if ($old_ver < 8) {
				// Add new show_existing_comments option with default value false
				// This maintains backward compatibility - existing behavior is preserved
				$this->options['show_existing_comments'] = false;
			}

			foreach (array('remove_everywhere', 'extra_post_types', 'show_existing_comments') as $v) {
				if (!isset($this->options[$v])) {
					$this->options[$v] = false;
				}
			}

			$this->options['db_version'] = self::DB_VERSION;
			$this->update_options($this->networkactive);
		}
	}

	public function check_upgrades() {
		$dc_version = get_option('disable_comment_version');
		if (version_compare($dc_version, '2.3.1', '<')) {
			if ($this->is_remove_everywhere()) {
				update_option('show_avatars', true);
			}
		}
		if (!$dc_version || $dc_version != DC_VERSION) {
			update_option('disable_comment_version', DC_VERSION);
		}
	}

	private function update_options($is_network_ctx = false) {
		if ($this->networkactive && $is_network_ctx) {
			update_site_option('disable_comments_options', $this->options);
		} else {
			update_option('disable_comments_options', $this->options);
		}
	}

	public function get_disabled_sites($default = false) {
		$disabled_sites = ['all' => true];
		foreach (get_sites(['number' => 0, 'fields' => 'ids']) as $blog_id) {
			$disabled_sites["site_{$blog_id}"] = true;
		}
		if ($default) {
			return $disabled_sites;
		}

		$this->options['disabled_sites'] = isset($this->options['disabled_sites']) ? $this->options['disabled_sites'] : [];
		$this->options['disabled_sites'] = wp_parse_args($this->options['disabled_sites'], $disabled_sites);
		$disabled_sites = $this->options['disabled_sites'];
		unset($disabled_sites['all']);
		if (in_array(false, $disabled_sites)) {
			$this->options['disabled_sites']['all'] = false;
		} else {
			$this->options['disabled_sites']['all'] = true;
		}
		return $this->options['disabled_sites'];
	}

	// public function get_disabled_count(){
	// 	$disabled_sites = isset($this->options['disabled_sites']) ? $this->options['disabled_sites'] : [];
	// 	unset($disabled_sites['all']);
	// 	return array_sum($disabled_sites);
	// }

	/**
	 * Get an array of disabled post type.
	 */
	public function get_disabled_post_types() {
		$types = $this->options['disabled_post_types'];
		// Not all extra_post_types might be registered on this particular site.
		if ($this->networkactive && !empty($this->options['extra_post_types'])) {
			foreach ((array) $this->options['extra_post_types'] as $extra) {
				if (post_type_exists($extra)) {
					$types[] = $extra;
				}
			}
		}
		return $types;
	}

	/**
	 * Check whether comments have been disabled on a given post type.
	 */
	private function is_exclude_by_role() {
		if (!empty($this->options['enable_exclude_by_role']) && !empty($this->options['exclude_by_role'])) {
			if (is_user_logged_in()) {
				$user = wp_get_current_user();
				$roles = (array) $user->roles;
				$diff = array_intersect($this->options['exclude_by_role'], $roles);
				if (count($diff) || (in_array("administrator", $this->options['exclude_by_role']) && is_super_admin())) {
					return true;
				}
			} else if (in_array('logged-out-users', $this->options['exclude_by_role'])) {
				return true;
			}
		}
		return false;
	}
	private function is_remove_everywhere() {
		if ($this->is_exclude_by_role()) {
			return false;
		}
		if (isset($this->options['remove_everywhere'])) {
			return $this->options['remove_everywhere'];
		}
		return false;
	}

	/**
	 * Check whether comments have been disabled on a given post type.
	 */
	private function is_post_type_disabled($type) {
		if ($this->is_exclude_by_role()) {
			return false;
		}
		return $type && in_array($type, $this->get_disabled_post_types());
	}

	public function init_filters() {
		// These need to happen now.
		if ($this->is_remove_everywhere()) {
			add_action('widgets_init', array($this, 'disable_rc_widget'));
			add_filter('wp_headers', array($this, 'filter_wp_headers'));
			add_action('template_redirect', array($this, 'filter_query'), 9);   // before redirect_canonical.

			// Admin bar filtering has to happen here since WP 3.6.
			add_action('template_redirect', array($this, 'filter_admin_bar'));
			add_action('admin_init', array($this, 'filter_admin_bar'));

			// Disable Comments REST API Endpoint (but allow notes)
			add_filter('rest_endpoints', array($this, 'filter_rest_endpoints'));
			add_filter('rest_pre_dispatch', array($this, 'filter_rest_comment_dispatch'), 10, 3);
			add_filter('rest_comment_query', array($this, 'filter_rest_comment_query'), 10, 2);
		}

		// remove create comment via xmlrpc
		if (isset($this->options['remove_xmlrpc_comments']) && intval($this->options['remove_xmlrpc_comments']) === 1) {
			add_filter('xmlrpc_methods', array($this, 'disable_xmlrc_comments'));
		}
		// rest API Comment Block (but allow notes)
		if (isset($this->options['remove_rest_API_comments']) && intval($this->options['remove_rest_API_comments']) === 1) {
			add_filter('rest_endpoints', array($this, 'filter_rest_endpoints'));
			add_filter('rest_pre_insert_comment', array($this, 'disable_rest_API_comments'), 10, 2);
			add_filter('rest_pre_dispatch', array($this, 'filter_rest_comment_dispatch'), 10, 3);
			add_filter('rest_comment_query', array($this, 'filter_rest_comment_query'), 10, 2);
		}

		// These can happen later.
		add_action('wp_loaded', array($this, 'init_wploaded_filters'));
		// Disable "Latest comments" block in Gutenberg.
		add_action('enqueue_block_editor_assets', array($this, 'filter_gutenberg_blocks'));
		// settings page assets
		add_action('admin_enqueue_scripts', array($this, 'settings_page_assets'));

		if (!$this->networkactive || $this->options['sitewide_settings']) {
			add_filter('comment_status_links', function ($status_links) {
				$status_links['disable_comments'] = sprintf("<a href='" . $this->settings_page_url() . "'>%s</a>", __("Disable Comments", 'disable-comments'));
				return $status_links;
			});
		}
	}

	public function init_wploaded_filters() {
		$disabled_post_types = $this->get_disabled_post_types();
		if (!empty($disabled_post_types) && !$this->is_exclude_by_role()) {
			foreach ($disabled_post_types as $type) {
				// we need to know what native support was for later.
				if (post_type_supports($type, 'comments')) {
					$this->modified_types[] = $type;
					// Keep comments support if show_existing_comments is enabled
					// or if there are allowed comment types that need to be displayed
					if (empty($this->options['show_existing_comments']) && !$this->has_allowed_comment_types()) {
						remove_post_type_support($type, 'comments');
					}
					remove_post_type_support($type, 'trackbacks');
				}
			}
		} elseif (is_admin() && !$this->is_configured()) {
			/**
			 * It is possible that $disabled_post_types is empty if other
			 * plugins have disabled comments. Hence we also check for
			 * remove_everywhere. If you still get a warning you probably
			 * shouldn't be using this plugin.
			 */
			add_action('all_admin_notices', array($this, 'setup_notice'));
		}

		if ($this->is_remove_everywhere() || (!empty($disabled_post_types) && !$this->is_exclude_by_role())) {
			add_filter('comments_array', array($this, 'filter_existing_comments'), 20, 2);
			add_filter('comments_open', array($this, 'filter_comment_status'), 20, 2);
			add_filter('pings_open', array($this, 'filter_comment_status'), 20, 2);
			add_filter('get_comments_number', array($this, 'filter_comments_number'), 20, 2);
		}

		// Filters for the admin only.
		if (is_admin()) {
			add_action('all_admin_notices', array($this, 'admin_notice'));
			if ($this->networkactive && is_network_admin()) {
				add_action('network_admin_menu', array($this, 'settings_menu'));
				add_action('network_admin_menu', array($this, 'tools_menu'));
				add_filter('network_admin_plugin_action_links', array($this, 'plugin_actions_links'), 10, 2);
			} elseif (!$this->networkactive || $this->options['sitewide_settings']) {
				add_action('admin_menu', array($this, 'settings_menu'));
				add_action('admin_menu', array($this, 'tools_menu'));
				add_filter('plugin_action_links', array($this, 'plugin_actions_links'), 10, 2);
				if (is_multisite()) {    // We're on a multisite setup, but the plugin isn't network activated.
					register_deactivation_hook(__FILE__, array($this, 'single_site_deactivate'));
				}
			}
			add_action('admin_notices', array($this, 'discussion_notice'));
			add_filter('plugin_row_meta', array($this, 'set_plugin_meta'), 10, 2);

			if ($this->is_remove_everywhere()) {
				add_action('admin_menu', array($this, 'filter_admin_menu'), 9999);  // do this as late as possible.
				add_action('admin_print_styles-index.php', array($this, 'admin_css'));
				add_action('admin_print_styles-profile.php', array($this, 'admin_css'));
				add_action('wp_dashboard_setup', array($this, 'filter_dashboard'));
				add_filter('pre_option_default_pingback_flag', '__return_zero');
			}
		}
		// Filters for front end only.
		else {
			add_action('template_redirect', array($this, 'check_comment_template'));

			if ($this->is_remove_everywhere()) {
				add_filter('feed_links_show_comments_feed', '__return_false');
			}
		}
	}

	// public function get_option( $key, $default = false ){
	// 	return $this->networkactive ? get_site_option( $key, $default ) : get_option( $key, $default );
	// }
	// public function update_option( $option, $value ){
	// 	return $this->networkactive ? update_site_option( $option, $value ) : update_option( $option, $value );
	// }
	// public function delete_option( $option ){
	// 	return $this->networkactive ? delete_site_option( $option ) : delete_option( $option );
	// }

	/**
	 * Replace the theme's comment template with a blank one.
	 * To prevent this, define DISABLE_COMMENTS_REMOVE_COMMENTS_TEMPLATE
	 * and set it to True
	 */
	public function check_comment_template() {
		if (is_singular() && ($this->is_remove_everywhere() || $this->is_post_type_disabled(get_post_type()))) {
			if (!defined('DISABLE_COMMENTS_REMOVE_COMMENTS_TEMPLATE') || DISABLE_COMMENTS_REMOVE_COMMENTS_TEMPLATE == true) {
				// Kill the comments template unless:
				// - show_existing_comments is enabled, OR
				// - there are allowed comment types that need to be displayed
				if (empty($this->options['show_existing_comments']) && !$this->has_allowed_comment_types()) {
					add_filter('comments_template', array($this, 'dummy_comments_template'), 20);
				}
			}
			// Remove comment-reply script for themes that include it indiscriminately.
			wp_deregister_script('comment-reply');
			// feed_links_extra inserts a comments RSS link.
			remove_action('wp_head', 'feed_links_extra', 3);
		}
	}

	public function dummy_comments_template() {
		return dirname(__FILE__) . '/views/comments.php';
	}

	public function is_xmlrpc_rest() {
		// remove create comment via xmlrpc
		if (isset($this->options['remove_xmlrpc_comments']) && intval($this->options['remove_xmlrpc_comments']) === 1) {
			return true;
		}
		// rest API Comment Block
		if (isset($this->options['remove_rest_API_comments']) && intval($this->options['remove_rest_API_comments']) === 1) {
			return true;
		}
		return false;
	}

	/**
	 * Remove the X-Pingback HTTP header
	 */
	public function filter_wp_headers($headers) {
		unset($headers['X-Pingback']);
		return $headers;
	}

	/**
	 * remove method wp.newComment
	 */
	public function disable_xmlrc_comments($methods) {
		unset($methods['wp.newComment']);
		return $methods;
	}

	public function disable_rest_API_comments($prepared_comment, $request) {
		// Allow comment types in the allowlist (e.g., WordPress 6.9+ block notes)
		if ($this->is_allowed_comment_type_request($request)) {
			return $prepared_comment;
		}
		return;
	}

	/**
	 * Get the list of allowed comment types from settings
	 *
	 * @return array Array of allowed comment types
	 */
	private function get_allowed_comment_types() {
		if (!isset($this->options['allowed_comment_types']) || !is_array($this->options['allowed_comment_types'])) {
			return array(); // Default: all special comment types disabled
		}
		return $this->options['allowed_comment_types'];
	}

	/**
	 * Check if any comment types are enabled in the allowlist
	 *
	 * @return bool True if there are allowed comment types, false otherwise
	 */
	private function has_allowed_comment_types() {
		$allowed_types = $this->get_allowed_comment_types();
		return !empty($allowed_types);
	}

	/**
	 * Check if a specific comment type is allowed (enabled in the allowlist)
	 *
	 * @param string $comment_type The comment type to check
	 * @return bool True if the comment type is allowed, false otherwise
	 */
	private function is_comment_type_allowed($comment_type) {
		$allowed_types = $this->get_allowed_comment_types();
		return in_array($comment_type, $allowed_types, true);
	}

	/**
	 * Get available comment type options for the "Enable Certain Comment Types" UI
	 *
	 * This function returns a list of known special comment types that users can enable,
	 * regardless of whether any comments of those types currently exist in the database.
	 *
	 * IMPORTANT: WordPress does not provide a formal API for registering or retrieving
	 * comment types (unlike post types with get_post_types()). Comment types are simply
	 * arbitrary string values stored in the wp_comments table. Therefore, we maintain
	 * a curated list of known special comment types that plugins commonly use.
	 *
	 * This function returns only predefined known types plus any types added via the
	 * 'disable_comments_known_comment_types' filter hook.
	 *
	 * @return array Associative array of comment_type => label
	 */
	public function get_available_comment_type_options() {
		// Predefined known special comment types with descriptive labels
		// These are shown even if no comments of these types exist yet in the database
		//
		// Note: WordPress does not have a formal comment type registration API,
		// so this list is maintained manually based on common plugin usage.
		$known_types = array(
			'note' => __('Notes - WordPress 6.9+ (note)', 'disable-comments'),
		);

		/**
		 * Filter the list of known comment types shown in the "Enable Certain Comment Types" UI
		 *
		 * Plugins can add their own comment types to this list so users can enable them
		 * even before any comments of those types exist in the database.
		 *
		 * Example:
		 *   add_filter( 'disable_comments_known_comment_types', function( $types ) {
		 *       $types['my_custom_type'] = __( 'My Custom Comment Type', 'my-plugin' );
		 *       return $types;
		 *   } );
		 *
		 * @param array $known_types Associative array of comment_type => label
		 */
		return apply_filters('disable_comments_known_comment_types', $known_types);
	}

	/**
	 * Check if a REST API request is for an allowed comment type
	 *
	 * @param WP_REST_Request $request The REST API request object
	 * @return bool True if the request is for an allowed comment type, false otherwise
	 */
	private function is_allowed_comment_type_request($request = null) {
		$comment_type = null;

		// Check if we have a request object
		if (!$request) {
			// Check global $_REQUEST for type parameter
			if (isset($_REQUEST['type'])) {
				$comment_type = sanitize_text_field(wp_unslash($_REQUEST['type']));
			}
			// Check if we're in a REST API context
			elseif (defined('REST_REQUEST') && REST_REQUEST) {
				global $wp;
				if (isset($wp->query_vars['type'])) {
					$comment_type = sanitize_text_field($wp->query_vars['type']);
				}
			}
		} else {
			// Check the request object for type parameter
			$type = $request->get_param('type');
			if ($type) {
				$comment_type = $type;
			}

			// Check the request body for type parameter (for POST requests)
			if (!$comment_type) {
				$body = $request->get_body_params();
				if (isset($body['type'])) {
					$comment_type = $body['type'];
				}
			}

			// Check JSON body for type parameter
			if (!$comment_type) {
				$json = $request->get_json_params();
				if (isset($json['type'])) {
					$comment_type = $json['type'];
				}
			}

			// For UPDATE requests (PUT/PATCH), check if the existing comment is an allowed type
			// WordPress doesn't send the type parameter when updating, only the ID and content
			if (!$comment_type) {
				$comment_id = $request->get_param('id');
				if ($comment_id) {
					$comment = get_comment($comment_id);
					if ($comment && isset($comment->comment_type)) {
						$comment_type = $comment->comment_type;
					}
				}
			}

			// For DELETE requests, extract comment ID from the route path
			// The comment ID is only in the URL (e.g., /wp/v2/comments/123), not in request params
			if (!$comment_type && $request->is_method('DELETE')) {
				$route_parts = explode('/', $request->get_route());
				$comment_id = end($route_parts);

				// Ensure we have a numeric comment ID
				if (is_numeric($comment_id)) {
					$comment = get_comment((int) $comment_id);
					if ($comment && isset($comment->comment_type)) {
						$comment_type = $comment->comment_type;
					}
				}
			}
		}

		// Check if the comment type is in the allowlist
		if ($comment_type && $this->is_comment_type_allowed($comment_type)) {
			return true;
		}

		return false;
	}

	/**
	 * Issue a 403 for all comment feed requests.
	 */
	public function filter_query() {
		if (is_comment_feed()) {
			wp_die(esc_html__('Comments are closed.', 'disable-comments'), '', array('response' => 403));
		}
	}

	/**
	 * Remove comment links from the admin bar.
	 */
	public function filter_admin_bar() {
		if (is_admin_bar_showing()) {
			// Remove comments links from admin bar.
			remove_action('admin_bar_menu', 'wp_admin_bar_comments_menu', 60);
			if (is_multisite()) {
				add_action('admin_bar_menu', array($this, 'remove_network_comment_links'), 500);
			}
		}
	}

	/**
	 * Remove the comments endpoint for the REST API
	 * But allow WordPress 6.9+ block notes (type=note) to work
	 */
	public function filter_rest_endpoints($endpoints) {
		// Don't remove endpoints entirely - instead we'll use permission callbacks
		// and other filters to block regular comments while allowing notes

		// We still need to add a filter to block non-note requests
		// This is handled by rest_pre_dispatch filter added in init_filters

		return $endpoints;
	}

	/**
	 * Filter REST API comment requests to block comments except allowed types
	 *
	 * @param mixed $result Response to replace the requested version with
	 * @param WP_REST_Server $server Server instance
	 * @param WP_REST_Request $request Request used to generate the response
	 * @return mixed
	 */
	public function filter_rest_comment_dispatch($result, $server, $request) {
		// Only filter comment-related routes
		$route = $request->get_route();
		if (strpos($route, '/wp/v2/comments') === false) {
			return $result;
		}

		// Allow requests for comment types in the allowlist to pass through
		if ($this->is_allowed_comment_type_request($request)) {
			return $result;
		}

		// Block all other comment requests
		return new WP_Error(
			'rest_comment_disabled',
			__('Comments are disabled.', 'disable-comments'),
			array('status' => 403)
		);
	}

	/**
	 * Filter comment queries in REST API to allow only allowed comment types
	 *
	 * @param array $prepared_args Array of arguments for WP_Comment_Query
	 * @param WP_REST_Request $request The REST API request
	 * @return array
	 */
	public function filter_rest_comment_query($prepared_args, $request) {
		// If this is a request for an allowed comment type, allow it
		if ($this->is_allowed_comment_type_request($request)) {
			return $prepared_args;
		}

		// For non-allowed requests, return empty results
		// by setting an impossible condition
		$prepared_args['comment__in'] = array(0);

		return $prepared_args;
	}

	/**
	 * Determines if scripts should be enqueued
	 */
	public function filter_gutenberg_blocks($hook) {
		global $post;
		if ($this->is_remove_everywhere() || (isset($post->post_type) && $this->is_post_type_disabled($post->post_type))) {
			return $this->disable_comments_script();
		}
	}

	/**
	 * Enqueues scripts
	 */
	public function disable_comments_script() {
		wp_enqueue_script('disable-comments-gutenberg', plugin_dir_url(__FILE__) . 'assets/js/disable-comments.js', array(), DC_VERSION, true);
	}

	/**
	 * Enqueues Scripts for Settings Page
	 */
	public function settings_page_assets($hook_suffix) {
		if (
			$hook_suffix === 'settings_page_' . DC_PLUGIN_SLUG ||
			$hook_suffix === 'options-general_' . DC_PLUGIN_SLUG
		) {
			// css
			wp_enqueue_style('sweetalert2', DC_ASSETS_URI . 'css/sweetalert2.min.css', [], DC_VERSION);
			// wp_enqueue_style('pagination',  DC_ASSETS_URI . 'css/pagination.css', [], false);
			wp_enqueue_style('disable-comments-style', DC_ASSETS_URI . 'css/style.css', [], DC_VERSION);
			wp_enqueue_style('select2', DC_ASSETS_URI . 'css/select2.min.css', [], DC_VERSION);
			// js
			wp_enqueue_script('sweetalert2', DC_ASSETS_URI . 'js/sweetalert2.all.min.js', array('jquery'), DC_VERSION, true);
			wp_enqueue_script('pagination', DC_ASSETS_URI . 'js/pagination.min.js', array('jquery'), DC_VERSION, true);
			wp_enqueue_script('select2', DC_ASSETS_URI . 'js/select2.min.js', array('jquery'), DC_VERSION, true);
			wp_enqueue_script('disable-comments-scripts', DC_ASSETS_URI . 'js/disable-comments-settings-scripts.js', array('jquery', 'select2', 'pagination', 'sweetalert2', 'wp-i18n'), DC_VERSION, true);
			wp_localize_script(
				'disable-comments-scripts',
				'disableCommentsObj',
				array(
					'save_action'      => 'disable_comments_save_settings',
					'delete_action'    => 'disable_comments_delete_comments',
					'settings_URI'     => $this->settings_page_url(),
					'_nonce'           => wp_create_nonce('disable_comments_save_settings'),
					'is_network_admin' => is_network_admin() ? '1' : '0',
				)
			);
			wp_set_script_translations('disable-comments-scripts', 'disable-comments');
		} else {
			// notice css
			wp_enqueue_style('disable-comments-notice', DC_ASSETS_URI . 'css/notice.css', [], DC_VERSION);
		}
	}

	/**
	 * Remove comment links from the admin bar in a multisite network.
	 */
	public function remove_network_comment_links($wp_admin_bar) {
		if ($this->networkactive && is_user_logged_in()) {
			foreach ((array) $wp_admin_bar->user->blogs as $blog) {
				$wp_admin_bar->remove_menu('blog-' . $blog->userblog_id . '-c');
			}
		} else {
			// We have no way to know whether the plugin is active on other sites, so only remove this one.
			$wp_admin_bar->remove_menu('blog-' . get_current_blog_id() . '-c');
		}
	}

	public function discussion_notice() {
		$disabled_post_types = $this->get_disabled_post_types();
		if (get_current_screen()->id == 'options-discussion' && !empty($disabled_post_types)) {
			$names_escaped = array();
			foreach ($disabled_post_types as $type) {
				$names_escaped[$type] = esc_html(get_post_type_object($type)->labels->name);
			}

			// translators: %s: disabled post types.
			echo '<div class="notice notice-warning"><p>' . sprintf(esc_html__('Note: The <em>Disable Comments</em> plugin is currently active, and comments are completely disabled on: %s. Many of the settings below will not be applicable for those post types.', 'disable-comments'), implode(esc_html__(', ', 'disable-comments'), $names_escaped)) . '</p></div>';
		}
	}

	/**
	 * Return context-aware settings page URL
	 */
	private function settings_page_url() {
		$base = $this->networkactive && is_network_admin() ? network_admin_url('settings.php') : admin_url('options-general.php');
		return add_query_arg('page', DC_PLUGIN_SLUG, $base);
	}

	/**
	 * Return context-aware tools page URL
	 */
	private function tools_page_url() {
		$base = $this->networkactive && is_network_admin() ? network_admin_url('settings.php') : admin_url('tools.php');
		return add_query_arg('page', 'disable_comments_tools', $base);
	}


	public function setup_notice() {
		$current_screen = get_current_screen()->id;
		if (!in_array($current_screen, ['dashboard-network', 'dashboard'])) {
			return;
		}
		$hascaps = $this->networkactive && is_network_admin() ? current_user_can('manage_network_plugins') : current_user_can('manage_options');
		if ($this->networkactive && !is_network_admin() && !$this->options['sitewide_settings']) {
			$hascaps = false;
		}
		if ($hascaps) {
			$this->setup_notice_flag = true;
			// translators: %s: URL to Disabled Comment settings page.
			$html = sprintf(__('The <strong>Disable Comments</strong> plugin is active, but isn\'t configured to do anything yet. Visit the <a href="%s">configuration page</a> to choose which post types to disable comments on.', 'disable-comments'), esc_attr($this->settings_page_url()));
			// phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage
			echo wp_kses_post('<div class="notice dc-text__block disable__comment__alert mb30"><img height="30" src="' . esc_url(DC_ASSETS_URI . 'img/icon-logo.png') . '" alt=""><p>' . $html . '</p></div>');
		}
	}

	public function filter_admin_menu() {
		global $pagenow;

		if (empty($this->options['show_existing_comments'])) {
			if ($pagenow == 'comment.php' || $pagenow == 'edit-comments.php') {
				wp_die(esc_html__('Comments are closed.', 'disable-comments'), '', array('response' => 403));
			}

			remove_menu_page('edit-comments.php');
		}

		if (!$this->discussion_settings_allowed()) {
			if ($pagenow == 'options-discussion.php') {
				wp_die(esc_html__('Comments are closed.', 'disable-comments'), '', array('response' => 403));
			}

			remove_submenu_page('options-general.php', 'options-discussion.php');
		}
	}

	public function filter_dashboard() {
		remove_meta_box('dashboard_recent_comments', 'dashboard', 'normal');
	}

	public function admin_css() {
		echo '<style>
			#dashboard_right_now .comment-count,
			#dashboard_right_now .comment-mod-count,
			#latest-comments,
			#welcome-panel .welcome-comments,
			.user-comment-shortcuts-wrap {
				display: none !important;
			}
		</style>';
	}

	public function filter_existing_comments($comments, $post_id) {
		$post_type = get_post_type($post_id);
		$comments_disabled = $this->is_remove_everywhere() || $this->is_post_type_disabled($post_type);

		// If comments are disabled but show_existing_comments is enabled, return existing comments
		if ($comments_disabled && !empty($this->options['show_existing_comments'])) {
			$comments_disabled = false;
		}

		// If comments are disabled, filter out regular comments but keep allowed comment types
		if ($comments_disabled && !empty($comments)) {
			$filtered_comments = array();
			foreach ($comments as $comment) {
				// Keep comment types that are in the allowlist even when comments are disabled
				if (isset($comment->comment_type) && $this->is_comment_type_allowed($comment->comment_type)) {
					$filtered_comments[] = $comment;
				}
			}
			return $filtered_comments;
		}

		// Default behavior: return all comments if not disabled
		return $comments;
	}

	public function filter_comment_status($open, $post_id) {
		$post_type = get_post_type($post_id);
		return ($this->is_remove_everywhere() || $this->is_post_type_disabled($post_type) ? false : $open);
	}

	public function filter_comments_number($count, $post_id) {
		$post_type = get_post_type($post_id);
		$comments_disabled = $this->is_remove_everywhere() || $this->is_post_type_disabled($post_type);

		// If comments are disabled but show_existing_comments is enabled, return actual count
		if ($comments_disabled && !empty($this->options['show_existing_comments'])) {
			return $count;
		}

		// If comments are disabled but there are allowed comment types, count only those types
		if ($comments_disabled && $this->has_allowed_comment_types()) {
			return $this->count_allowed_comment_types($post_id);
		}

		return $comments_disabled ? 0 : $count;
	}

	/**
	 * Count comments of allowed types for a specific post
	 *
	 * @param int $post_id The post ID
	 * @return int The count of comments matching allowed types
	 */
	private function count_allowed_comment_types($post_id) {
		$allowed_types = $this->get_allowed_comment_types();
		if (empty($allowed_types)) {
			return 0;
		}

		$comments = get_comments(array(
			'post_id' => $post_id,
			'type__in' => $allowed_types,
			'status' => 'approve',
			'count' => true,
		));

		return (int) $comments;
	}

	public function disable_rc_widget() {
		unregister_widget('WP_Widget_Recent_Comments');
		/**
		 * The widget has added a style action when it was constructed - which will
		 * still fire even if we now unregister the widget... so filter that out
		 */
		add_filter('show_recent_comments_widget_style', '__return_false');
	}

	public function set_plugin_meta($links, $file) {
		static $plugin;
		$plugin = plugin_basename(__FILE__);
		if ($file == $plugin) {
			$links[] = '<a href="https://github.com/WPDevelopers/disable-comments">GitHub</a>';
		}
		return $links;
	}

	/**
	 * Add links to Settings page
	 */
	public function plugin_actions_links($links, $file) {
		static $plugin;
		$plugin = plugin_basename(__FILE__);
		if ($file == $plugin && current_user_can('manage_options')) {
			array_unshift(
				$links,
				sprintf('<a href="%s">%s</a>', esc_attr($this->settings_page_url()), __('Settings', 'disable-comments')),
				sprintf('<a href="%s">%s</a>', esc_attr($this->tools_page_url()), __('Tools', 'disable-comments'))
			);
		}

		return $links;
	}

	public function settings_menu() {
		$title = _x('Disable Comments', 'settings menu title', 'disable-comments');
		if ($this->networkactive && is_network_admin()) {
			add_submenu_page('settings.php', $title, $title, 'manage_network_plugins', DC_PLUGIN_SLUG, array($this, 'settings_page'));
		} elseif (!$this->networkactive || $this->options['sitewide_settings']) {
			add_submenu_page('options-general.php', $title, $title, 'manage_options', DC_PLUGIN_SLUG, array($this, 'settings_page'));
		}
	}

	public function tools_menu() {
		$title = __('Delete Comments', 'disable-comments');
		$hook = '';
		if ($this->networkactive && is_network_admin()) {
			$hook = add_submenu_page('settings.php', $title, $title, 'manage_network_plugins', 'disable_comments_tools', array($this, 'tools_page'));
		} elseif (!$this->networkactive || $this->options['sitewide_settings']) {
			$hook = add_submenu_page('tools.php', $title, $title, 'manage_options', 'disable_comments_tools', array($this, 'tools_page'));
		}
		add_action('load-' . $hook, array($this, 'redirectToMainSettingsPage'));
	}

	public function redirectToMainSettingsPage() {
		wp_safe_redirect($this->settings_page_url() . '#delete');
		exit;
	}

	public function get_all_comments_number() {
		global $wpdb;
		if (is_network_admin() && function_exists('get_sites') && class_exists('WP_Site_Query')) {
			$count = 0;
			$sites = get_sites([
				'number' => 0,
				'fields' => 'ids',
			]);
			foreach ($sites as $blog_id) {
				switch_to_blog($blog_id);
				$count += $this->__get_comment_count();
				restore_current_blog();
			}
			return $count;
		} else {
			return $this->__get_comment_count();
		}
	}

	public function get_all_comment_types($exclude_allowed = true) {
		if ($this->networkactive && is_network_admin() && function_exists('get_sites')) {
			$comment_types = [];
			$sites = get_sites([
				'number' => 0,
				'fields' => 'ids',
			]);
			foreach ($sites as $blog_id) {
				switch_to_blog($blog_id);
				$comment_types = array_merge($this->_get_all_comment_types($exclude_allowed), $comment_types);
				restore_current_blog();
			}
			return $comment_types;
		} else {
			return $this->_get_all_comment_types($exclude_allowed);
		}
	}
	public function _get_all_comment_types($exclude_allowed = true) {
		global $wpdb;
		$commenttypes = array();
		// we need fresh data in every call.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery -- We need to count comments across multiple sites
		$commenttypes_query = $wpdb->get_results("SELECT DISTINCT comment_type FROM $wpdb->comments", ARRAY_A);
		if (!empty($commenttypes_query) && is_array($commenttypes_query)) {
			foreach ($commenttypes_query as $entry) {
				$value = $entry['comment_type'];
				// Exclude comment types that are in the allowlist from deletable comment types
				// These are protected and should not appear in the "Delete Certain Comment Types" interface
				if ($exclude_allowed && $this->is_comment_type_allowed($value)) {
					continue;
				}
				if ('' === $value) {
					$commenttypes['default'] = __('Default (no type)', 'disable-comments');
				} else {
					$commenttypes[$value] = ucwords(str_replace('_', ' ', $value)) . ' (' . $value . ')';
				}
			}
		}
		return $commenttypes;
	}

	public function get_all_post_types($network = false) {
		$typeargs = array('public' => true);
		if ($network || $this->networkactive && is_network_admin()) {
			$typeargs['_builtin'] = true;   // stick to known types for network.
		}
		$types = get_post_types($typeargs, 'objects');
		foreach (array_keys($types) as $type) {
			if (!in_array($type, $this->modified_types) && !post_type_supports($type, 'comments')) {   // the type doesn't support comments anyway.
				unset($types[$type]);
			}
		}
		return $types;
	}

	public function get_roles($selected) {
		$roles = [
			[
				"id" => 'logged-out-users',
				"text" => __('Logged out users', 'disable-comments'),
				"selected" => in_array('logged-out-users', (array) $selected),
			]
		];
		$editable_roles = array_reverse(get_editable_roles());
		foreach ($editable_roles as $role => $details) {
			$roles[] = [
				"id" => esc_attr($role),
				"text" => esc_html(translate_user_role($details['name'])),
				"selected" => in_array($role, (array) $selected),
			];
		}
		return $roles;
	}

	public function tools_page() {
		return;
	}

	public function settings_page() {
		// Belt-and-suspenders: add_submenu_page already gates on capability,
		// but verify here too so a direct URL request can never render the page.
		$required_cap = $this->networkactive && is_network_admin() ? 'manage_network_plugins' : 'manage_options';
		if (!current_user_can($required_cap)) {
			wp_die(esc_html__('You do not have sufficient permissions to access this page.', 'disable-comments'), 403);
		}

		$avatar_status = '-1';
		if ($this->can_network_admin_ajax_context()) {
			$show_avatars = [];
			$sites = get_sites([
				'number' => 0,
				'fields' => 'ids',
			]);
			foreach ($sites as $blog_id) {
				switch_to_blog($blog_id);
				$show_avatars[] = (int) get_option('show_avatars', '0');
				restore_current_blog();
			}
			if (count($show_avatars) == array_sum($show_avatars)) {
				$avatar_status = '0';
			} elseif (0 == array_sum($show_avatars)) {
				$avatar_status = '1';
			}
		}

		include_once DC_PLUGIN_VIEWS_PATH . 'settings.php';
	}

	public function get_sub_sites() {
		$nonce = (isset($_REQUEST['nonce']) ? sanitize_text_field(wp_unslash($_REQUEST['nonce'])) : '');
		if (!wp_verify_nonce($nonce, 'disable_comments_save_settings')) {
			wp_send_json(['data' => [], 'totalNumber' => 0]);
		}
		// Listing subsites is always a network-level operation on multisite —
		// require manage_network_plugins regardless of how the plugin is activated
		// (network-wide or per-site). A per-site admin must never enumerate all
		// network sites. On single-site installs manage_options suffices.
		$required_cap = is_multisite() ? 'manage_network_plugins' : 'manage_options';
		if (!current_user_can($required_cap)) {
			wp_send_json(['data' => [], 'totalNumber' => 0]);
		}

		$_sub_sites = [];
		$type = isset($_GET['type']) ? sanitize_text_field(wp_unslash($_GET['type'])) : 'disabled';
		$search = isset($_GET['search']) ? sanitize_text_field(wp_unslash($_GET['search'])) : '';
		$pageSize = isset($_GET['pageSize']) ? sanitize_text_field(wp_unslash($_GET['pageSize'])) : 50;
		$pageNumber = isset($_GET['pageNumber']) ? sanitize_text_field(wp_unslash($_GET['pageNumber'])) : 1;
		$offset = ($pageNumber - 1) * $pageSize;
		$sub_sites = get_sites([
			'number' => $pageSize,
			'offset' => $offset,
			'search' => $search,
			'fields' => 'ids',
		]);
		$totalNumber = get_sites([
			// 'number' => $pageSize,
			// 'offset' => $offset,
			'search' => $search,
			'count' => true,
		]);

		if ($type == 'disabled') {
			$disabled_site_options = isset($this->options['disabled_sites']) ? $this->options['disabled_sites'] : [];
		} else { // if($type == 'delete')
			$disabled_site_options = $this->get_disabled_sites(true);
		}

		foreach ($sub_sites as $sub_site_id) {
			$blog = get_blog_details($sub_site_id);
			$is_checked = checked(!empty($disabled_site_options["site_$sub_site_id"]), true, false);
			$_sub_sites[] = [
				'site_id' => $sub_site_id,
				'is_checked' => $is_checked,
				'blogname' => $blog->blogname,
			];
		}
		wp_send_json(['data' => $_sub_sites, 'totalNumber' => $totalNumber]);
	}

	public function get_form_array_escaped($_args = array()) {
		$formArray = [];
		if (!empty($_args)) {
			$formArray = wp_parse_args($_args);
		}
		// nonce is verified in the calling function
		// phpcs:ignore WordPress.Security.NonceVerification.Missing
		else if (isset($_POST['data'])) {
			// need to use wp_parse_args before map_deep sanitize_text_field
			// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
			$formArray = map_deep(wp_parse_args(wp_unslash($_POST['data'])), 'sanitize_text_field');
		}
		return $formArray;
	}

	public function disable_comments_settings($_args = array()) {
		$nonce = (isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '');
		if (($this->is_CLI && !empty($_args)) || wp_verify_nonce($nonce, 'disable_comments_save_settings')) {
			// Resolve context ONCE — used for both cap check and save routing.
			$is_network_ctx = $this->is_network_admin_ajax_context();

			if (!$this->is_CLI) {
				if ($is_network_ctx) {
					// Network admin context → must be super admin.
					$required_cap = 'manage_network_plugins';
				} elseif ($this->networkactive && $this->sitewide_settings === '1') {
					// Sitewide lock is on → only super admin may write.
					$required_cap = 'manage_network_plugins';
				} else {
					// Subsite or single-site → manage_options suffices.
					$required_cap = 'manage_options';
				}
				if (!current_user_can($required_cap)) {
					wp_send_json_error(['message' => 'Insufficient permissions.'], 403);
				}
			}

			$formArray = $this->get_form_array_escaped($_args);

			$old_options = $this->options;
			$this->options = [];
			if ($this->is_CLI) {
				$this->options = $old_options;
			}

			if ($is_network_ctx && function_exists('get_sites') && empty($formArray['sitewide_settings'])) {
				$formArray['disabled_sites'] = isset($formArray['disabled_sites']) ? $formArray['disabled_sites'] : [];
				$this->options['disabled_sites'] = isset($old_options['disabled_sites']) ? $old_options['disabled_sites'] : [];
				$this->options['disabled_sites'] = array_merge($this->options['disabled_sites'], $formArray['disabled_sites']);
			} elseif ($is_network_ctx && !empty($formArray['sitewide_settings'])) {
				$this->options['disabled_sites'] = $old_options['disabled_sites'];
			}

			if (isset($formArray['mode'])) {
				$this->options['remove_everywhere'] = (sanitize_text_field($formArray['mode']) == 'remove_everywhere');
			}
			$post_types = $this->get_all_post_types($is_network_ctx);

			if ($this->options['remove_everywhere']) {
				$disabled_post_types = array_keys($post_types);
			} else {
				$disabled_post_types = (isset($formArray['disabled_types']) ? array_map('sanitize_key', (array) $formArray['disabled_types']) : ($this->is_CLI && isset($this->options['disabled_post_types']) ? $this->options['disabled_post_types'] : []));
			}

			$disabled_post_types = array_intersect($disabled_post_types, array_keys($post_types));
			$this->options['disabled_post_types'] = $disabled_post_types;

			// Extra custom post types.
			if ($this->networkactive && isset($formArray['extra_post_types'])) {
				$extra_post_types = array_filter(array_map('sanitize_key', explode(',', $formArray['extra_post_types'])));
				$this->options['extra_post_types'] = array_diff($extra_post_types, array_keys($post_types)); // Make sure we don't double up builtins.
			}

			if ($is_network_ctx && isset($formArray['sitewide_settings'])) {
				update_site_option('disable_comments_sitewide_settings', $formArray['sitewide_settings']);
			}

			if (isset($formArray['disable_avatar'])) {
				if ($is_network_ctx) {
					if ($formArray['disable_avatar'] == '0' || $formArray['disable_avatar'] == '1') {
						$sites = get_sites([
							'number' => 0,
							'fields' => 'ids',
						]);
						foreach ($sites as $blog_id) {
							switch_to_blog($blog_id);
							update_option('show_avatars', (bool) !$formArray['disable_avatar']);
							restore_current_blog();
						}
					}
				} else {
					update_option('show_avatars', (bool) !$formArray['disable_avatar']);
				}
			}

			if (isset($formArray['enable_exclude_by_role'])) {
				$this->options['enable_exclude_by_role'] = $formArray['enable_exclude_by_role'];
			}
			if (isset($formArray['exclude_by_role'])) {
				$this->options['exclude_by_role'] = $formArray['exclude_by_role'];
			}

			// xml rpc
			$this->options['remove_xmlrpc_comments'] = (isset($formArray['remove_xmlrpc_comments']) ? intval($formArray['remove_xmlrpc_comments']) : ($this->is_CLI && isset($this->options['remove_xmlrpc_comments']) ? $this->options['remove_xmlrpc_comments'] : 0));
			// rest api comments
			$this->options['remove_rest_API_comments'] = (isset($formArray['remove_rest_API_comments']) ? intval($formArray['remove_rest_API_comments']) : ($this->is_CLI && isset($this->options['remove_rest_API_comments']) ? $this->options['remove_rest_API_comments'] : 0));
			// show existing comments
			$this->options['show_existing_comments'] = (isset($formArray['show_existing_comments']) ? (bool) $formArray['show_existing_comments'] : ($this->is_CLI && isset($this->options['show_existing_comments']) ? $this->options['show_existing_comments'] : false));

			// allowed comment types (opt-in allowlist)
			if (isset($formArray['allowed_comment_types']) && is_array($formArray['allowed_comment_types'])) {
				// Sanitize and validate the allowed comment types
				$this->options['allowed_comment_types'] = array_map('sanitize_key', $formArray['allowed_comment_types']);
			} else {
				// Default: empty array (all special comment types disabled)
				$this->options['allowed_comment_types'] = array();
			}

			$this->options['db_version'] = self::DB_VERSION;
			$this->options['settings_saved'] = true;
			// save settings
			$this->update_options($is_network_ctx);
		}
		if (!$this->is_CLI) {
			wp_send_json_success(array('message' => __('Saved', 'disable-comments')));
			wp_die();
		}
	}

	public function is_configured() {
		$disabled_post_types = $this->get_disabled_post_types();

		if (empty($disabled_post_types) && empty($this->options['remove_everywhere']) && empty($this->options['remove_rest_API_comments']) && empty($this->options['remove_xmlrpc_comments'])) {
			return false;
		}
		return true;
	}

	public function delete_comments_settings($_args = array()) {
		global $deletedPostTypeNames;
		$log = '';
		$nonce = (isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '');

		if (($this->is_CLI && !empty($_args)) || wp_verify_nonce($nonce, 'disable_comments_save_settings')) {
			// Resolve context ONCE — used for both cap check and deletion routing.
			$is_network_ctx = $this->is_network_admin_ajax_context();

			if (!$this->is_CLI) {
				if ($is_network_ctx) {
					$required_cap = 'manage_network_plugins';
				} elseif ($this->networkactive && $this->sitewide_settings === '1') {
					$required_cap = 'manage_network_plugins';
				} else {
					$required_cap = 'manage_options';
				}
				if (!current_user_can($required_cap)) {
					wp_send_json_error(['message' => 'Insufficient permissions.'], 403);
				}
			}

			$formArray = $this->get_form_array_escaped($_args);

			if ($is_network_ctx && function_exists('get_sites') && class_exists('WP_Site_Query')) {
				$sites = get_sites([
					'number' => 0,
					'fields' => 'ids',
				]);
				foreach ($sites as $blog_id) {
					// $formArray['disabled_sites'] ids don't include "site_" prefix.
					if (!empty($formArray['disabled_sites']) && !empty($formArray['disabled_sites']["site_$blog_id"])) {
						switch_to_blog($blog_id);
						if (!is_super_admin() && !current_user_can('manage_options')) {
							restore_current_blog();
							continue;
						}
						$log = $this->delete_comments($_args, $is_network_ctx);
						restore_current_blog();
					}
				}
			} else {
				$log = $this->delete_comments($_args, $is_network_ctx);
			}
		}
		// message
		$deletedPostTypeNames = array_unique((array) $deletedPostTypeNames);
		$message = (count($deletedPostTypeNames) == 0 ? $log . '.' : $log . ' for ' . implode(", ", $deletedPostTypeNames) . '.');
		if (!$this->is_CLI) {
			wp_send_json_success(array('message' => $message));
			wp_die();
		} else {
			return $log;
		}
	}

	private function delete_comments($_args, $is_network_ctx = false) {
		global $wpdb;
		global $deletedPostTypeNames;

		$formArray = $this->get_form_array_escaped($_args);

		$types = $this->get_all_post_types($is_network_ctx);
		$commenttypes = $this->get_all_comment_types();
		$log = "";
		// comments delete
		if (isset($formArray['delete_mode'])) {
			if ($formArray['delete_mode'] == 'delete_everywhere') {
				// Delete all comment metadata except for allowed comment types
				$allowed_types = $this->get_allowed_comment_types();

				if (empty($allowed_types)) {
					// No allowed types, delete all comments
					// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
					$wpdb->query("DELETE FROM $wpdb->commentmeta");
					// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
					$wpdb->query("DELETE FROM $wpdb->comments");
				} else {
					// Build exclusion query for allowed comment types
					$placeholders = implode(', ', array_fill(0, count($allowed_types), '%s'));

					// Delete comment metadata
					$query = $wpdb->prepare(
						"DELETE cmeta FROM $wpdb->commentmeta cmeta INNER JOIN $wpdb->comments comments ON cmeta.comment_id=comments.comment_ID WHERE comments.comment_type NOT IN ($placeholders)",
						$allowed_types
					);
					// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
					$wpdb->query($query);

					// Delete comments
					$query = $wpdb->prepare(
						"DELETE FROM $wpdb->comments WHERE comment_type NOT IN ($placeholders)",
						$allowed_types
					);
					// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
					$wpdb->query($query);
				}

				// Update comment counts
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
				$wpdb->query("UPDATE $wpdb->posts SET comment_count = 0");
				$this->optimize_table($wpdb->commentmeta);
				$this->optimize_table($wpdb->comments);
				$log = __('All comments have been deleted', 'disable-comments');
			} elseif ($formArray['delete_mode'] == 'selected_delete_types') {
				$delete_post_types = empty($formArray['delete_types']) ? array() : (array) $formArray['delete_types'];
				$delete_post_types = array_intersect($delete_post_types, array_keys($types));

				// Extra custom post types.
				if ($this->networkactive && !empty($formArray['delete_extra_post_types'])) {
					$delete_extra_post_types = array_filter(array_map('sanitize_key', explode(',', $formArray['delete_extra_post_types'])));
					$delete_extra_post_types = array_diff($delete_extra_post_types, array_keys($types));    // Make sure we don't double up builtins.
					$delete_post_types = array_merge($delete_post_types, $delete_extra_post_types);
				}

				if (!empty($delete_post_types)) {
					// Loop through post_types and remove comments/meta (excluding allowed comment types) and set posts comment_count to 0.
					$allowed_types = $this->get_allowed_comment_types();

					foreach ($delete_post_types as $delete_post_type) {
						if (empty($allowed_types)) {
							// No allowed types, delete all comments for this post type
							// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
							$wpdb->query($wpdb->prepare("DELETE cmeta FROM $wpdb->commentmeta cmeta INNER JOIN $wpdb->comments comments ON cmeta.comment_id=comments.comment_ID INNER JOIN $wpdb->posts posts ON comments.comment_post_ID=posts.ID WHERE posts.post_type = %s", $delete_post_type));
							// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
							$wpdb->query($wpdb->prepare("DELETE comments FROM $wpdb->comments comments INNER JOIN $wpdb->posts posts ON comments.comment_post_ID=posts.ID WHERE posts.post_type = %s", $delete_post_type));
						} else {
							// Build exclusion query for allowed comment types
							$placeholders = implode(', ', array_fill(0, count($allowed_types), '%s'));
							$params = array_merge(array($delete_post_type), $allowed_types);

							// Delete comment metadata
							$query = $wpdb->prepare(
								"DELETE cmeta FROM $wpdb->commentmeta cmeta INNER JOIN $wpdb->comments comments ON cmeta.comment_id=comments.comment_ID INNER JOIN $wpdb->posts posts ON comments.comment_post_ID=posts.ID WHERE posts.post_type = %s AND comments.comment_type NOT IN ($placeholders)",
								$params
							);
							// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
							$wpdb->query($query);

							// Delete comments
							$query = $wpdb->prepare(
								"DELETE comments FROM $wpdb->comments comments INNER JOIN $wpdb->posts posts ON comments.comment_post_ID=posts.ID WHERE posts.post_type = %s AND comments.comment_type NOT IN ($placeholders)",
								$params
							);
							// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
							$wpdb->query($query);
						}

						// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
						$wpdb->query($wpdb->prepare("UPDATE $wpdb->posts SET comment_count = 0 WHERE post_author != 0 AND post_type = %s", $delete_post_type));

						$post_type_object = get_post_type_object($delete_post_type);
						$post_type_label = $post_type_object ? $post_type_object->labels->name : $delete_post_type;
						$deletedPostTypeNames[] = $post_type_label;
					}

					$this->optimize_table($wpdb->commentmeta);
					$this->optimize_table($wpdb->comments);
					$log = __('All comments have been deleted', 'disable-comments');
				}
			} elseif ($formArray['delete_mode'] == 'selected_delete_comment_types') {
				$delete_comment_types = empty($formArray['delete_comment_types']) ? array() : (array) $formArray['delete_comment_types'];
				$delete_comment_types = array_intersect($delete_comment_types, array_keys($commenttypes));

				if (!empty($delete_comment_types)) {
					// Loop through comment_types and remove comments/meta and set posts comment_count to 0.
					foreach ($delete_comment_types as $delete_comment_type) {
						// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
						$wpdb->query($wpdb->prepare("DELETE cmeta FROM $wpdb->commentmeta cmeta INNER JOIN $wpdb->comments comments ON cmeta.comment_id=comments.comment_ID WHERE comments.comment_type = %s", $delete_comment_type));
						// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
						$wpdb->query($wpdb->prepare("DELETE comments FROM $wpdb->comments comments  WHERE comments.comment_type = %s", $delete_comment_type));
						$deletedPostTypeNames[] = $commenttypes[$delete_comment_type];
					}

					// Update comment_count on post_types
					foreach ($types as $key => $value) {
						// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
						$comment_count = $wpdb->get_var($wpdb->prepare("SELECT COUNT(comments.comment_ID) FROM $wpdb->comments comments INNER JOIN $wpdb->posts posts ON comments.comment_post_ID=posts.ID WHERE posts.post_type = %s", $key));
						// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
						$wpdb->query($wpdb->prepare("UPDATE $wpdb->posts SET comment_count = %d WHERE post_author != 0 AND post_type = %s", $comment_count, $key));
					}

					$this->optimize_table($wpdb->commentmeta);
					$this->optimize_table($wpdb->comments);

					$log = __('All comments have been deleted', 'disable-comments');
				}
			} elseif ($formArray['delete_mode'] == 'delete_spam') {

				// Delete spam comments and their metadata (excluding allowed comment types)
				$allowed_types = $this->get_allowed_comment_types();

				if (empty($allowed_types)) {
					// No allowed types, delete all spam comments
					// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
					$wpdb->query($wpdb->prepare("DELETE cmeta FROM $wpdb->commentmeta cmeta INNER JOIN $wpdb->comments comments ON cmeta.comment_id=comments.comment_ID WHERE comments.comment_approved = %s", 'spam'));
					// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
					$wpdb->query($wpdb->prepare("DELETE comments FROM $wpdb->comments comments WHERE comments.comment_approved = %s", 'spam'));
				} else {
					// Build exclusion query for allowed comment types
					$placeholders = implode(', ', array_fill(0, count($allowed_types), '%s'));
					$params = array_merge(array('spam'), $allowed_types);

					// Delete comment metadata
					$query = $wpdb->prepare(
						"DELETE cmeta FROM $wpdb->commentmeta cmeta INNER JOIN $wpdb->comments comments ON cmeta.comment_id=comments.comment_ID WHERE comments.comment_approved = %s AND comments.comment_type NOT IN ($placeholders)",
						$params
					);
					// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
					$wpdb->query($query);

					// Delete comments
					$query = $wpdb->prepare(
						"DELETE comments FROM $wpdb->comments comments WHERE comments.comment_approved = %s AND comments.comment_type NOT IN ($placeholders)",
						$params
					);
					// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
					$wpdb->query($query);
				}

				$this->optimize_table($wpdb->commentmeta);
				$this->optimize_table($wpdb->comments);
				$log = __('All spam comments have been deleted.', 'disable-comments');
			}
		}
		delete_transient('wc_count_comments');
		return $log;
	}

	private function discussion_settings_allowed() {
		if (defined('DISABLE_COMMENTS_ALLOW_DISCUSSION_SETTINGS') && DISABLE_COMMENTS_ALLOW_DISCUSSION_SETTINGS == true) {
			return true;
		}
	}

	public function single_site_deactivate() {
		// for single sites, delete the options upon deactivation, not uninstall.
		delete_option('disable_comments_options');
	}

	/**
	 * We need fresh data in every call. Called after switching to blog in loop.
	 *
	 * @return int The number of comments.
	 */
	protected function __get_comment_count() {
		global $wpdb;

		// Exclude allowed comment types from the count since they cannot be deleted
		// and should not be displayed in the "Total Comments" count in the Delete Comments tab
		$allowed_types = $this->get_allowed_comment_types();

		if (empty($allowed_types)) {
			// No allowed types, count all comments
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
			return $wpdb->get_var("SELECT COUNT(comment_id) FROM $wpdb->comments");
		}

		// Build exclusion query for allowed comment types
		$placeholders = implode(', ', array_fill(0, count($allowed_types), '%s'));
		$query = $wpdb->prepare(
			"SELECT COUNT(comment_id) FROM $wpdb->comments WHERE comment_type NOT IN ($placeholders)",
			$allowed_types
		);

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
		return $wpdb->get_var($query);
	}

	/**
	 * Optimize a given table in the WordPress database.
	 *
	 * @param string $table_name The name of the table to optimize.
	 */
	protected function optimize_table($table_name) {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
		return $wpdb->query("OPTIMIZE TABLE " . esc_sql($table_name));
	}

	/**
	 * Truncate a given table in the WordPress database.
	 *
	 * @param string $table_name The name of the table to truncate.
	 */
	protected function truncate_table($table_name) {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
		return $wpdb->query("TRUNCATE TABLE " . esc_sql($table_name));
	}

	/**
	 * Get the current site-wide comment status as a descriptive string.
	 *
	 * This function analyzes the current Disable Comments plugin configuration
	 * and returns a string describing which content types have comments disabled.
	 *
	 * @return string The current comment status:
	 *                - 'all' if comments are disabled site-wide for all content types
	 *                - 'posts' if comments are disabled only for posts
	 *                - 'pages' if comments are disabled only for pages
	 *                - 'posts,pages' if comments are disabled for both posts and pages
	 *                - 'custom_type_name' for other specific content types
	 *                - 'multiple' if multiple specific types are disabled (not all)
	 *                - 'none' if comments are not disabled anywhere
	 *
	 * @since 2.5.2
	 */
	public function get_current_comment_status() {
		try {
			// Handle case where plugin is not properly initialized
			if (empty($this->options)) {
				return 'none';
			}

			// Check if comments are disabled everywhere
			if ($this->is_remove_everywhere()) {
				return 'all';
			}

			// Get disabled post types
			$disabled_post_types = $this->get_disabled_post_types();

			// If no post types are disabled, comments are enabled everywhere
			if (empty($disabled_post_types)) {
				return 'none';
			}

			// Get all available post types that support comments
			$all_post_types = $this->get_all_post_types();
			$all_post_type_keys = array_keys($all_post_types);

			// Check if all available post types are disabled
			if (count($disabled_post_types) >= count($all_post_type_keys)) {
				$missing_types = array_diff($all_post_type_keys, $disabled_post_types);
				if (empty($missing_types)) {
					return 'all';
				}
			}

			// Handle specific common cases
			if (count($disabled_post_types) === 1) {
				$disabled_type = $disabled_post_types[0];

				// Return the specific post type name for single disabled types
				switch ($disabled_type) {
					case 'post':
						return 'posts';
					case 'page':
						return 'pages';
					default:
						// For custom post types, return the post type slug
						return $disabled_type;
				}
			}

			// Handle multiple specific post types
			if (count($disabled_post_types) === 2 &&
				in_array('post', $disabled_post_types) &&
				in_array('page', $disabled_post_types)) {
				return 'posts,pages';
			}

			// For other combinations, return 'multiple' to indicate partial disabling
			return 'multiple';
		} catch (Exception $e) {
			// Error handling - return safe default
			if (defined('WP_DEBUG') && WP_DEBUG) {
				// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug logging for WP_DEBUG mode
				error_log('Disable Comments: Error in get_current_comment_status() - ' . $e->getMessage());
			}
			return 'none';
		}
	}

	/**
	 * Get detailed comment status information including API restrictions.
	 *
	 * This function provides comprehensive information about comment restrictions
	 * including post type restrictions, API-level restrictions, network settings,
	 * role exclusions, and comment counts.
	 *
	 * @return array Associative array with detailed status information:
	 *               - 'status' => Main status (same as get_current_comment_status())
	 *               - 'disabled_post_types' => Array of disabled post type slugs
	 *               - 'disabled_post_type_labels' => Array of disabled post type labels
	 *               - 'remove_everywhere' => Boolean indicating global disable
	 *               - 'xmlrpc_disabled' => Boolean indicating XML-RPC comments disabled
	 *               - 'rest_api_disabled' => Boolean indicating REST API comments disabled
	 *               - 'total_post_types' => Total number of available post types
	 *               - 'is_configured' => Boolean indicating if plugin is configured
	 *               - 'total_comments' => Total number of comments in database
	 *               - 'network_active' => Boolean indicating if plugin is network activated
	 *               - 'sitewide_settings' => Site-wide settings status
	 *               - 'role_exclusion_enabled' => Boolean indicating if role exclusions are enabled
	 *               - 'excluded_roles' => Array of excluded role slugs
	 *               - 'excluded_role_labels' => Array of human-readable excluded role names
	 *
	 * @since 2.5.2
	 */
	public function get_detailed_comment_status() {
		try {
			$status = $this->get_current_comment_status();
			$disabled_post_types = $this->get_disabled_post_types();
			$all_post_types = $this->get_all_post_types();

			// Get human-readable labels for disabled post types
			$disabled_labels = array();
			foreach ($disabled_post_types as $post_type) {
				if (isset($all_post_types[$post_type])) {
					$disabled_labels[] = $all_post_types[$post_type]->labels->name;
				} else {
					// Fallback for custom post types not in the main list
					$post_type_obj = get_post_type_object($post_type);
					$disabled_labels[] = $post_type_obj ? $post_type_obj->labels->name : $post_type;
				}
			}

			// Get total comments count
			$total_comments = $this->get_all_comments_number();

			// Determine site-wide settings status
			$sitewide_settings = 'not_applicable';
			if ($this->networkactive) {
				$sitewide_settings = isset($this->options['sitewide_settings']) && $this->options['sitewide_settings'] ?
					'enabled' : 'disabled';
			}

			// Process role-based exclusion information
			$role_exclusion_enabled = isset($this->options['enable_exclude_by_role']) && $this->options['enable_exclude_by_role'];
			$excluded_roles = isset($this->options['exclude_by_role']) ? $this->options['exclude_by_role'] : array();

			// Get human-readable role names
			$excluded_role_labels = array();
			if ($role_exclusion_enabled && !empty($excluded_roles)) {
				$editable_roles = get_editable_roles();

				foreach ($excluded_roles as $role) {
					if ($role === 'logged-out-users') {
						$excluded_role_labels[] = __('Logged out users', 'disable-comments');
					} elseif (isset($editable_roles[$role])) {
						$excluded_role_labels[] = translate_user_role($editable_roles[$role]['name']);
					} else {
						$excluded_role_labels[] = $role;
					}
				}
			}

			return array(
				'status' => $status,
				'disabled_post_types' => $disabled_post_types,
				'disabled_post_type_labels' => $disabled_labels,
				'remove_everywhere' => $this->is_remove_everywhere(),
				'xmlrpc_disabled' => !empty($this->options['remove_xmlrpc_comments']),
				'rest_api_disabled' => !empty($this->options['remove_rest_API_comments']),
				'show_existing_comments' => !empty($this->options['show_existing_comments']),
				'total_post_types' => count($all_post_types),
				'is_configured' => $this->is_configured(),
				'total_comments' => $total_comments,
				'network_active' => $this->networkactive,
				'sitewide_settings' => $sitewide_settings,
				'role_exclusion_enabled' => $role_exclusion_enabled,
				'excluded_roles' => $excluded_roles,
				'excluded_role_labels' => $excluded_role_labels
			);
		} catch (Exception $e) {
			// Error handling - return safe defaults
			if (defined('WP_DEBUG') && WP_DEBUG) {
				// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug logging for WP_DEBUG mode
				error_log('Disable Comments: Error in get_detailed_comment_status() - ' . $e->getMessage());
			}
			return array(
				'status' => 'none',
				'disabled_post_types' => array(),
				'disabled_post_type_labels' => array(),
				'remove_everywhere' => false,
				'xmlrpc_disabled' => false,
				'rest_api_disabled' => false,
				'show_existing_comments' => false,
				'total_post_types' => 0,
				'is_configured' => false,
				'total_comments' => 0,
				'network_active' => false,
				'sitewide_settings' => 'not_applicable',
				'role_exclusion_enabled' => false,
				'excluded_roles' => array(),
				'excluded_role_labels' => array()
			);
		}
	}
	/**
	 * Add Disable Comments information to WordPress Site Health Info panel.
	 *
	 * This method integrates the plugin's status information into WordPress's
	 * built-in Site Health system for easy debugging and site overview.
	 *
	 * @param array $debug_info The debug information array.
	 * @return array Modified debug information array.
	 *
	 * @since 2.5.2
	 */
	public function add_site_health_info($debug_info) {
		$data = $this->get_detailed_comment_status();

		// Create the main status description
		$status_descriptions = array(
			'all' => __('Comments are disabled site-wide for all content types', 'disable-comments'),
			'posts' => __('Comments are disabled only for blog posts', 'disable-comments'),
			'pages' => __('Comments are disabled only for pages', 'disable-comments'),
			'posts,pages' => __('Comments are disabled for both posts and pages', 'disable-comments'),
			'multiple' => __('Comments are disabled for multiple specific content types', 'disable-comments'),
			'none' => __('Comments are enabled everywhere', 'disable-comments'),
		);

		// translators: %s: disabled post types.
		$other_status_description = sprintf(__('Comments are disabled for: %s', 'disable-comments'), $data['status']);
		$status_description = isset($status_descriptions[$data['status']]) ?
			$status_descriptions[$data['status']] :
			$other_status_description;

		// Format site-wide settings value
		$sitewide_settings_labels = array(
			'enabled' => __('Enabled', 'disable-comments'),
			'disabled' => __('Disabled', 'disable-comments'),
			'not_applicable' => __('Not applicable', 'disable-comments'),
		);

		// Build the fields array using data from get_detailed_comment_status()
		$fields = array(
			'status' => array(
				'label' => __('Comment Status', 'disable-comments'),
				'value' => $status_description,
			),
			'plugin_configured' => array(
				'label' => __('Plugin Configured', 'disable-comments'),
				'value' => $data['is_configured'] ? __('Yes', 'disable-comments') : __('No', 'disable-comments'),
			),
			'total_comments' => array(
				'label' => __('Total Comments', 'disable-comments'),
				'value' => number_format_i18n($data['total_comments']),
			),
			'global_disable' => array(
				'label' => __('Global Disable Active', 'disable-comments'),
				'value' => $data['remove_everywhere'] ? __('Yes', 'disable-comments') : __('No', 'disable-comments'),
			),
			'disabled_post_type_count' => array(
				'label' => __('Disabled Post Types Count', 'disable-comments'),
				'value' => sprintf('%d of %d', count($data['disabled_post_types']), $data['total_post_types']),
			),
			'disabled_post_types' => array(
				'label' => __('Disabled Post Types', 'disable-comments'),
				'value' => !empty($data['disabled_post_type_labels']) ?
					implode(', ', $data['disabled_post_type_labels']) :
					__('None', 'disable-comments'),
			),
			'xmlrpc_comments' => array(
				'label' => __('XML-RPC Comments', 'disable-comments'),
				'value' => $data['xmlrpc_disabled'] ? __('Disabled', 'disable-comments') : __('Enabled', 'disable-comments'),
			),
			'rest_api_comments' => array(
				'label' => __('REST API Comments', 'disable-comments'),
				'value' => $data['rest_api_disabled'] ? __('Disabled', 'disable-comments') : __('Enabled', 'disable-comments'),
			),
			'show_existing_comments' => array(
				'label' => __('Show Existing Comments', 'disable-comments'),
				'value' => $data['show_existing_comments'] ? __('Yes', 'disable-comments') : __('No', 'disable-comments'),
			),
			'network_active' => array(
				'label' => __('Network Active', 'disable-comments'),
				'value' => $data['network_active'] ? __('Yes', 'disable-comments') : __('No', 'disable-comments'),
			),
			'sitewide_settings' => array(
				'label' => __('Site-wide Settings', 'disable-comments'),
				'value' => $sitewide_settings_labels[$data['sitewide_settings']],
			),
			'role_exclusion_enabled' => array(
				'label' => __('Role-based Exclusions', 'disable-comments'),
				'value' => $data['role_exclusion_enabled'] ? __('Enabled', 'disable-comments') : __('Disabled', 'disable-comments'),
			),
			'excluded_roles' => array(
				'label' => __('Excluded Roles', 'disable-comments'),
				'value' => !empty($data['excluded_role_labels']) ?
					implode(', ', $data['excluded_role_labels']) :
					__('None', 'disable-comments'),
			),
		);

		// Add the section to Site Health
		$debug_info['disable-comments'] = array(
			'label' => __('Disable Comments', 'disable-comments'),
			'description' => __('Complete overview of comment disable settings and configuration.', 'disable-comments'),
			'fields' => $fields,
		);

		return $debug_info;
	}
}

Disable_Comments::get_instance();
