<?php
/**
 * Core WordPress options and transients that must not be deleted.
 *
 * @package DatabaseCleanup
 */

namespace DatabaseCleanup\Includes;

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

class Protected_Items {

	/**
	 * @return list<string>
	 */
	public static function core_options(): array {
		return [
			'active_plugins',
			'blog_charset',
			'blogdescription',
			'blogname',
			'blog_public',
			'can_compress_scripts',
			'category_base',
			'comment_max_links',
			'comments_notify',
			'comments_per_page',
			'cron',
			'current_theme',
			'date_format',
			'default_category',
			'default_comment_status',
			'default_email_category',
			'default_link_category',
			'default_ping_status',
			'default_pingback_flag',
			'default_post_format',
			'default_role',
			'gmt_offset',
			'home',
			'html_type',
			'image_default_link_type',
			'image_default_size',
			'image_default_align',
			'large_size_h',
			'large_size_w',
			'links_updated_date_format',
			'mailserver_login',
			'mailserver_pass',
			'mailserver_port',
			'mailserver_url',
			'medium_size_h',
			'medium_size_w',
			'moderation_notify',
			'page_for_posts',
			'page_on_front',
			'permalink_structure',
			'ping_sites',
			'posts_per_page',
			'recently_edited',
			'require_name_email',
			'show_avatars',
			'show_on_front',
			'siteurl',
			'start_of_week',
			'sticky_posts',
			'tag_base',
			'template',
			'template_root',
			'stylesheet',
			'stylesheet_root',
			'thread_comments',
			'thread_comments_depth',
			'thumbnail_size_h',
			'thumbnail_size_w',
			'time_format',
			'timezone_string',
			'uninstall_plugins',
			'upload_path',
			'upload_url_path',
			'uploads_use_yearmonth_folders',
			'use_balanceTags',
			'use_smilies',
			'use_trackback',
			'users_can_register',
			'wp_user_roles',
			'wp_powersuite_settings',
			// Critical structural / lifecycle options. Deleting any of these can
			// break the site or trigger WordPress's full historical DB upgrade.
			'db_version',
			'initial_db_version',
			'db_upgraded',
			'secret',
			'fresh_site',
			'WPLANG',
			'recently_activated',
			'uninstall_plugins',
			'finished_splitting_shared_terms',
			'finished_updating_comment_type',
			'rewrite_rules',
			'nav_menu_options',
			'sidebars_widgets',
			'admin_email',
			'new_admin_email',
			'adminhash',
			'auth_salt',
			'logged_in_salt',
			'nonce_salt',
			'https_detection_errors',
			'recovery_keys',
			'recovery_mode_email_last_sent',
			'disallowed_keys',
			'blacklist_keys',
			'moderation_keys',
			'comment_previously_approved',
			'avatar_default',
			'avatar_rating',
			'close_comments_for_old_posts',
			'close_comments_days_old',
			'page_comments',
			'default_comments_page',
			'comment_order',
			'site_icon',
			'wp_page_for_privacy_policy',
			'show_comments_cookies_opt_in',
			'theme_switched',
			'widget_block',
		];
	}

	/**
	 * Option-name prefixes that always belong to WordPress core structures and
	 * must never be deleted from the browser (theme mods, widgets, etc.).
	 *
	 * @return list<string>
	 */
	public static function core_option_prefixes(): array {
		return [
			'theme_mods_',
			'widget_',
			'sidebars_',
			'nav_menu_',
			'category_children',
			'_site_transient_',
			'_transient_',
		];
	}

	/**
	 * Options stored with the site table prefix (e.g. {$prefix}user_roles), which
	 * differ on installs with a custom database prefix and are business-critical.
	 *
	 * @return list<string>
	 */
	public static function prefixed_core_options(): array {
		global $wpdb;

		$prefix = (string) $wpdb->prefix;

		return [
			$prefix . 'user_roles',
			$prefix . 'user_level',
			$prefix . 'capabilities',
			$prefix . 'user_count',
			$prefix . 'dashboard_quick_press_last_post_id',
		];
	}

	/**
	 * Option name prefixes that must never be deleted from the browser.
	 *
	 * @return list<string>
	 */
	public static function protected_option_prefixes(): array {
		$prefixes = [
			'woocommerce_',
			'_woocommerce_',
			'wc_',
			'_wc_',
			'learndash_',
			'ld_',
			'memberpress_',
			'mepr_',
			'edd_',
			'give_',
			'sfwd-',
			'pmpro_',
			'bbpress_',
			'buddypress_',
			'wpml_',
			'icl_',
			'rank_math_',
			'yoast',
			'wordfence',
			'itsec_',
			'action_scheduler',
			// Page builders & template systems — deleting these breaks layouts.
			'elementor',
			'_elementor',
			'et_',
			'_et_',
			'divi_',
			'fl_builder',
			'_fl_builder',
			'oxygen_',
			'ct_',
			'bricks_',
			'_bricks',
			'acf_',
			'_acf',
			'wpb_',
			'vc_',
			'js_composer',
			'ux_',
			'flatsome_',
			'spectra_',
			'uagb_',
			'kadence_',
			'generateblocks_',
			'jetpack_',
			'wpforms_',
			'gravityforms_',
			'gf_',
			'fluentform',
			'ninja_forms',
			'revslider',
			'rs_',
			'aioseo_',
			'seopress_',
			'redirection_',
			'litespeed_',
			'w3tc_',
			'wp_rocket_',
			'autoptimize_',
		];

		/**
		 * Filter protected option name prefixes for the database cleanup browser.
		 *
		 * @param list<string> $prefixes
		 */
		return apply_filters( 'wpps_dc_protected_option_prefixes', $prefixes );
	}

	/**
	 * Post types that must never be bulk-deleted.
	 *
	 * @return list<string>
	 */
	public static function protected_post_types(): array {
		$types = [
			// Core content — never bulk-delete from the post-type browser.
			'post',
			'page',
			'attachment',
			'revision',
			'nav_menu_item',
			'custom_css',
			'customize_changeset',
			'oembed_cache',
			'user_request',
			'wp_block',
			'wp_template',
			'wp_template_part',
			'wp_global_styles',
			'wp_navigation',
			'wp_font_family',
			'wp_font_face',
			// WooCommerce / commerce.
			'shop_order',
			'shop_order_refund',
			'shop_coupon',
			'shop_webhook',
			'product',
			'product_variation',
			'subscription',
			'membership',
			// LMS.
			'course',
			'lesson',
			'topic',
			'quiz',
			'ld-exam',
			'groups',
			'sfwd-courses',
			'sfwd-lessons',
			'sfwd-topic',
			'sfwd-quiz',
			'sfwd-question',
			'sfwd-certificates',
			'sfwd-assignment',
			'memberpressproduct',
			'memberpressgroup',
			'memberpressrule',
			'download',
			'edd_payment',
			'edd_discount',
			'edd_license',
			'give_payment',
			'give_forms',
			// Page builders & template libraries (these look like "junk CPT" but are critical).
			'elementor_library',
			'e-landing-page',
			'elementor_snippet',
			'elementor_font',
			'elementor_icons',
			'et_pb_layout',
			'et_header_layout',
			'et_body_layout',
			'et_footer_layout',
			'et_tb_item',
			'et_tb_layout',
			'et_theme_builder',
			'fl-builder-template',
			'fl-theme-layout',
			'oxygen_template',
			'oxy_user_library',
			'ct_template',
			'bricks_template',
			'brxe-template',
			'acf-field-group',
			'acf-field',
			'acf-post-type',
			'acf-taxonomy',
			'acf-ui-options-page',
			'wpcf7_contact_form',
			'wpforms',
			'forminator_forms',
			'nf_sub',
			'vc_grid_item',
			'ux_template',
			'blocks',
			'wp_block',
			'kadence_form',
			'kadence_element',
			'kadence_navigation',
			'kadence_header',
			'kadence_query',
			'generate_page_header',
			'jet-theme-core',
			'jet-menu',
			'jet-popup',
			'jet-woo-builder',
			'premium-fonts',
			'premium-templates',
			'astra-advanced-hook',
			'bsf-sidebar',
			'spectra-popup',
			'popup',
			'popup_theme',
			'ml-slide',
			'ml-slider',
			'revslider',
			'revslider_slide',
			'revslider_navigation',
			'tribe_events',
			'tribe_venue',
			'tribe_organizer',
			'booked_appointments',
		];

		/**
		 * Filter protected post types for database cleanup.
		 *
		 * @param list<string> $types
		 */
		return apply_filters( 'wpps_dc_protected_post_types', $types );
	}

	/**
	 * Table suffixes (after site prefix) that must never be dropped or truncated.
	 *
	 * @return list<string>
	 */
	public static function protected_table_suffixes(): array {
		$suffixes = [
			'actionscheduler_actions',
			'actionscheduler_claims',
			'actionscheduler_groups',
			'actionscheduler_logs',
			'wc_orders',
			'wc_orders_meta',
			'wc_order_addresses',
			'wc_order_operational_data',
			'wc_order_product_lookup',
			'wc_order_stats',
			'wc_order_tax_lookup',
			'wc_order_coupon_lookup',
			'wc_product_meta_lookup',
			'wc_customer_lookup',
			'wc_tax_rate_classes',
			'wc_download_log',
			'wc_webhooks',
			'woocommerce_api_keys',
			'woocommerce_attribute_taxonomies',
			'woocommerce_downloadable_product_permissions',
			'woocommerce_log',
			'woocommerce_order_itemmeta',
			'woocommerce_order_items',
			'woocommerce_payment_tokenmeta',
			'woocommerce_payment_tokens',
			'woocommerce_sessions',
			'woocommerce_shipping_zone_locations',
			'woocommerce_shipping_zone_methods',
			'woocommerce_shipping_zones',
			'woocommerce_tax_rate_locations',
			'woocommerce_tax_rates',
			'learndash_user_activity',
			'learndash_user_activity_meta',
			'ld_course_steps',
			'ld_group_user',
			'ld_quiz_entries',
			'ld_quiz_questions',
			'ld_quiz_results',
			'mepr_events',
			'mepr_jobs',
			'mepr_members',
			'mepr_subscriptions',
			'mepr_transactions',
			'edd_customers',
			'edd_customermeta',
			'edd_logs',
			'edd_orders',
			'edd_ordermeta',
			// Page builders / forms / SEO — never DROP while recognizable.
			'e_events',
			'e_notes',
			'e_notes_users_relations',
			'e_submissions',
			'e_submissions_actions_log',
			'e_submissions_values',
			'elementor_library',
			'fluentform_entry_details',
			'fluentform_forms',
			'fluentform_form_meta',
			'fluentform_logs',
			'fluentform_submissions',
			'fluentform_submission_meta',
			'gf_entry',
			'gf_entry_meta',
			'gf_entry_notes',
			'gf_form',
			'gf_form_meta',
			'gf_form_revisions',
			'redirection_items',
			'redirection_logs',
			'redirection_404',
			'revslider_css',
			'revslider_layer_animations',
			'revslider_navigations',
			'revslider_sliders',
			'revslider_slides',
			'revslider_static_slides',
			'yoast_indexable',
			'yoast_indexable_hierarchy',
			'yoast_migrations',
			'yoast_primary_term',
			'yoast_seo_links',
			'rank_math_internal_links',
			'rank_math_internal_meta',
		];

		/**
		 * Filter protected table suffixes for destructive table actions.
		 *
		 * @param list<string> $suffixes
		 */
		return apply_filters( 'wpps_dc_protected_table_suffixes', $suffixes );
	}

	/**
	 * Cron hooks that must not be removed from the browser.
	 *
	 * @return list<string>
	 */
	public static function protected_cron_hooks(): array {
		$hooks = [
			'wp_version_check',
			'wp_update_plugins',
			'wp_update_themes',
			'wp_scheduled_delete',
			'delete_expired_transients',
			'wp_scheduled_auto_draft_delete',
			'wp_update_user_counts',
			'recovery_mode_clean_expired_keys',
			'wp_privacy_delete_old_export_files',
			'wpps_dc_scheduled_cleanup',
			'wpps_dc_scheduled_cleanup_continue',
			'action_scheduler_run_queue',
			'woocommerce_scheduled_sales',
			'woocommerce_cancel_unpaid_orders',
			'woocommerce_cleanup_sessions',
			'woocommerce_cleanup_logs',
			'woocommerce_geoip_updater',
			'gravityforms_cron',
			'fluentform_do_scheduled_tasks',
			'wp_mail_smtp_process_queue',
			'wp_mail_smtp_send_enqueued_email',
			'solid_central_cron',
			'itsec_scheduled_cron',
			'jetpack_clean_nonces',
			'redirection_log_delete',
		];

		/**
		 * Filter protected cron hooks for the database cleanup browser.
		 *
		 * @param list<string> $hooks
		 */
		return apply_filters( 'wpps_dc_protected_cron_hooks', $hooks );
	}

	public static function is_protected_option( string $name ): bool {
		$name = trim( $name );

		if ( $name === '' ) {
			return true;
		}

		if ( str_starts_with( $name, 'wpps_' ) || str_starts_with( $name, 'wp_powersuite_' ) ) {
			return true;
		}

		if ( in_array( $name, self::core_options(), true ) ) {
			return true;
		}

		if ( in_array( $name, self::prefixed_core_options(), true ) ) {
			return true;
		}

		foreach ( self::core_option_prefixes() as $prefix ) {
			if ( str_starts_with( $name, $prefix ) ) {
				return true;
			}
		}

		foreach ( self::protected_option_prefixes() as $prefix ) {
			if ( str_starts_with( $name, $prefix ) ) {
				return true;
			}
		}

		return false;
	}

	public static function is_protected_transient( string $name ): bool {
		$name = trim( $name );

		if ( $name === '' ) {
			return true;
		}

		if ( str_starts_with( $name, 'wpps_' ) || str_starts_with( $name, 'wp_powersuite_' ) ) {
			return true;
		}

		foreach ( self::protected_option_prefixes() as $prefix ) {
			if ( str_starts_with( $name, $prefix ) ) {
				return true;
			}
		}

		return false;
	}

	public static function is_protected_post_type( string $post_type ): bool {
		$post_type = sanitize_key( $post_type );

		return $post_type !== '' && in_array( $post_type, self::protected_post_types(), true );
	}

	public static function is_protected_cron_hook( string $hook ): bool {
		$hook = trim( $hook );

		if ( $hook === '' ) {
			return true;
		}

		if ( in_array( $hook, self::protected_cron_hooks(), true ) ) {
			return true;
		}

		if ( str_starts_with( $hook, 'wpps_' ) || str_starts_with( $hook, 'wp_powersuite_' ) ) {
			return true;
		}

		foreach ( self::protected_option_prefixes() as $prefix ) {
			if ( str_starts_with( $hook, $prefix ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Meta keys that must never be deleted from browsers or bulk meta actions.
	 *
	 * @return list<string>
	 */
	public static function protected_meta_keys(): array {
		$keys = [
			'_edit_lock',
			'_edit_last',
			'_wp_page_template',
			'_wp_attachment_metadata',
			'_thumbnail_id',
			'_wp_attached_file',
			'_wp_attachment_image_alt',
			'_menu_item_type',
			'_menu_item_menu_item_parent',
			'_menu_item_object_id',
			'_menu_item_object',
			'_menu_item_target',
			'_menu_item_classes',
			'_menu_item_url',
			'_menu_item_xfn',
			'capabilities',
			'wp_capabilities',
			'wp_user_level',
			'session_tokens',
			'_customer_user',
			'_order_key',
			'_payment_method',
			'_payment_method_title',
			'_transaction_id',
			'_billing_email',
			'_billing_first_name',
			'_billing_last_name',
			'_elementor_edit_mode',
			'_elementor_template_type',
			'_elementor_data',
			'_elementor_page_settings',
			'_elementor_controls_usage',
			'_elementor_css',
			'_elementor_version',
			'_et_pb_use_builder',
			'_et_pb_old_content',
			'_fl_builder_data',
			'_fl_builder_draft',
			'_ct_builder_shortcodes',
			'_bricks_page_content_2',
			'_bricks_editor_mode',
		];

		/**
		 * Filter protected meta keys for database cleanup browsers.
		 *
		 * @param list<string> $keys
		 * @param string       $meta_type post|user|comment|term
		 */
		return apply_filters( 'wpps_dc_protected_meta_keys', $keys );
	}

	/**
	 * Meta key prefixes that must never be deleted.
	 *
	 * @return list<string>
	 */
	public static function protected_meta_key_prefixes(): array {
		$prefixes = [
			'_woocommerce',
			'_wc_',
			'woocommerce_',
			'_learndash',
			'learndash_',
			'_mepr_',
			'mepr_',
			'_edd_',
			'edd_',
			'_give_',
			'_stripe_',
			'_ppcp_',
			'_subscription',
			'wordfence',
			'itsec_',
			'_wpps_',
			'wp_powersuite_',
			'_elementor',
			'elementor_',
			'_et_',
			'et_',
			'_fl_builder',
			'fl_builder_',
			'_ct_',
			'oxygen_',
			'_bricks',
			'bricks_',
			'_acf',
			'acf_',
			'field_',
			'_wpb_',
			'vc_',
			'_ux_',
			'_kadence',
			'_generateblocks',
			'_uagb',
			'_jet_engine',
			'_yoast',
			'rank_math_',
			'_wpforms',
			'_forminator',
		];

		/**
		 * Filter protected meta key prefixes for database cleanup browsers.
		 *
		 * @param list<string> $prefixes
		 * @param string       $meta_type post|user|comment|term
		 */
		return apply_filters( 'wpps_dc_protected_meta_key_prefixes', $prefixes );
	}

	public static function is_protected_meta_key( string $key, string $meta_type = 'post' ): bool {
		unset( $meta_type );

		$key = trim( $key );

		if ( $key === '' ) {
			return true;
		}

		if ( in_array( $key, self::protected_meta_keys(), true ) ) {
			return true;
		}

		foreach ( self::protected_meta_key_prefixes() as $prefix ) {
			if ( str_starts_with( $key, $prefix ) ) {
				return true;
			}
		}

		foreach ( self::protected_option_prefixes() as $prefix ) {
			if ( str_starts_with( $key, $prefix ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * @return list<string>
	 */
	public static function core_table_suffixes(): array {
		global $wpdb;

		return array_values(
			array_unique(
				array_filter(
					array_map(
						static function ( string $table ) use ( $wpdb ): string {
							$prefix = (string) $wpdb->prefix;

							return str_starts_with( $table, $prefix )
								? substr( $table, strlen( $prefix ) )
								: $table;
						},
						array_merge( $wpdb->tables( 'all', true ), [ $wpdb->usermeta ] )
					),
					static fn( string $suffix ): bool => $suffix !== ''
				)
			)
		);
	}

	public static function table_suffix( string $table ): string {
		global $wpdb;

		$prefix = (string) $wpdb->prefix;

		return str_starts_with( $table, $prefix )
			? substr( $table, strlen( $prefix ) )
			: $table;
	}

	public static function is_core_table( string $table ): bool {
		if ( ! preg_match( '/^[a-zA-Z0-9_]+$/', $table ) ) {
			return false;
		}

		global $wpdb;

		if ( str_starts_with( $table, $wpdb->prefix . 'wpps_' ) ) {
			return false;
		}

		return in_array( self::table_suffix( $table ), self::core_table_suffixes(), true );
	}

	public static function is_protected_plugin_table( string $table ): bool {
		if ( ! preg_match( '/^[a-zA-Z0-9_]+$/', $table ) ) {
			return true;
		}

		if ( self::is_core_table( $table ) ) {
			return true;
		}

		$suffix = self::table_suffix( $table );

		if ( in_array( $suffix, self::protected_table_suffixes(), true ) ) {
			return true;
		}

		foreach ( self::protected_table_suffixes() as $protected ) {
			if ( str_starts_with( $suffix, $protected ) ) {
				return true;
			}
		}

		$prefixes = [
			'wc_',
			'woocommerce_',
			'actionscheduler_',
			'learndash_',
			'ld_',
			'mepr_',
			'memberpress_',
			'edd_',
			'give_',
			'e_',
			'elementor_',
			'et_',
			'fluentform_',
			'ff_',
			'gf_',
			'wpforms_',
			'yoast_',
			'rank_math_',
			'redirection_',
			'revslider_',
			'aioseo_',
			'seopress_',
			'litespeed_',
			'w3tc_',
			'wp_mail_smtp',
			'wpms_',
			'icl_',
			'wpml_',
			'bp_',
			'bb_',
			'nf_',
			'frm_',
			'formidable_',
			'ct_',
			'bricks_',
			'acf_',
			'acfe_',
		];

		foreach ( $prefixes as $prefix ) {
			if ( str_starts_with( $suffix, $prefix ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Whether bulk-deleting all posts of this type is allowed.
	 * Core content and template/library CPTs are always blocked.
	 */
	public static function is_bulk_post_type_delete_allowed( string $post_type ): bool {
		$post_type = sanitize_key( $post_type );

		if ( $post_type === '' || self::is_protected_post_type( $post_type ) ) {
			return false;
		}

		// Non-public CPTs are almost always plugin/theme structures (templates, forms, etc.).
		$object = get_post_type_object( $post_type );
		if ( $object && empty( $object->public ) && empty( $object->publicly_queryable ) ) {
			return false;
		}

		return true;
	}

	/**
	 * Whether a destructive table action (drop, truncate, empty) is allowed.
	 */
	public static function is_destructive_table_action_allowed( string $table, string $action ): bool {
		$action = sanitize_key( $action );

		if ( ! in_array( $action, [ 'drop', 'empty' ], true ) ) {
			return false;
		}

		if ( ! preg_match( '/^[a-zA-Z0-9_]+$/', $table ) ) {
			return false;
		}

		return ! self::is_protected_plugin_table( $table );
	}

	/**
	 * Whether table maintenance (optimize, repair, convert) is allowed.
	 */
	public static function is_table_maintenance_allowed( string $table ): bool {
		return preg_match( '/^[a-zA-Z0-9_]+$/', $table ) === 1;
	}
}
