<?php
/**
 * Option Store
 *
 * Centralized option storage for WP PowerSuite. All plugin settings are
 * stored in a single option key with autoload = false for optimal performance.
 *
 * @package WP_PowerSuite\Core
 * @since 1.0.0
 */

namespace WP_PowerSuite\Core;

/**
 * Option Store Class
 *
 * Manages all plugin options in a single WordPress option. Provides safe
 * getters and setters with validation and type safety. Uses request-level
 * caching for optimal performance.
 *
 * @since 1.0.0
 */
class OptionStore {

	/**
	 * Option Store Instance
	 *
	 * Singleton instance of the OptionStore class.
	 *
	 * @var OptionStore|null
	 * @since 1.0.0
	 */
	private static ?OptionStore $instance = null;

	/**
	 * Option Key
	 *
	 * WordPress option key for storing all plugin settings.
	 *
	 * @var string
	 * @since 1.0.0
	 */
	private string $option_key = 'wp_powersuite_settings';

	/**
	 * Settings Cache
	 *
	 * Request-level cache of all settings.
	 *
	 * @var array<string, mixed>|null
	 * @since 1.0.0
	 */
	private ?array $settings_cache = null;

	/**
	 * Object Cache Group
	 *
	 * Dedicated cache group for the settings object-cache entry, so it can be
	 * invalidated independently of WordPress core's own options cache group.
	 *
	 * @var string
	 * @since 1.0.0
	 */
	/**
	 * Default Settings
	 *
	 * Default values for all settings.
	 *
	 * @var array<string, mixed>
	 * @since 1.0.0
	 */
	private array $defaults = [
		'social_oauth_debug'     => false,
		'enabled_modules'        => [],
		'favorites'              => [],
		/**
		 * When false, favorites list uses curated recommended IDs (until user stars/unstars).
		 */
		'favorites_customized'    => false,
		/** Set once migrate legacy installs (pre-v2 favorites behavior). */
		'legacy_favorites_customized_migration' => false,
		'module_order'           => [],
		'quick_launcher_shortcut' => 'ctrl-shift-k', // 'ctrl-k', 'ctrl-shift-k', 'alt-p', 'ctrl-alt-k'
		'enable_performance_monitor' => false,
		'interface_color_mode'    => 'wordpress_default', // 'wordpress_default' | 'custom_brand'
		'custom_brand_color'      => '#57BBB6',
		'ui_preferences'        => [
			'view_mode'       => 'grid', // 'grid' or 'list'
			'description_mode' => 'show', // 'show', 'hide', or 'truncate' (grid only)
		],
		'white_label'           => [
			'enabled'                   => false,
			'plugin_name'                 => '',
			'plugin_short_name'           => '',
			'plugin_description'          => '',
			'plugin_author'               => '',
			'plugin_author_uri'           => '',
			'company_name'                => '',
			'menu_title'                  => '',
			'dashboard_title'             => '',
			'website_url'                 => '',
			'support_url'                 => '',
			'documentation_url'           => '',
			'logo_attachment_id'          => 0,
			'menu_icon_attachment_id'     => 0,
			'email_sender_name'           => '',
			'email_footer_text'           => '',
			'hide_powered_by'             => true,
			'hide_license_menu'           => true,
			'hide_white_label_settings'   => false,
			'hide_external_links'         => true,
		],
	];

	/**
	 * Get Option Store Instance
	 *
	 * Returns the singleton instance of the OptionStore class.
	 *
	 * @since 1.0.0
	 * @return OptionStore OptionStore instance.
	 */
	public static function getInstance(): OptionStore {
		if ( self::$instance === null ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Constructor
	 *
	 * Private constructor to enforce singleton pattern.
	 *
	 * @since 1.0.0
	 */
	private function __construct() {
		// Constructor is private to prevent direct instantiation
	}

	/**
	 * Prevent Cloning
	 *
	 * Prevents cloning of the singleton instance.
	 *
	 * @since 1.0.0
	 * @return void
	 */
	private function __clone() {
		// Prevent cloning
	}

	/**
	 * Prevent Unserialization
	 *
	 * Prevents unserialization of the singleton instance.
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function __wakeup(): void {
		throw new \Exception( 'Cannot unserialize singleton' );
	}

	/**
	 * Get All Settings
	 *
	 * Returns all plugin settings merged with defaults.
	 *
	 * @since 1.0.0
	 * @return array<string, mixed> All settings.
	 */
	public function getAllSettings(): array {
		$this->ensureSettingsLoaded();
		return array_merge( $this->defaults, $this->settings_cache ?? [] );
	}

	/**
	 * Get Setting
	 *
	 * Retrieves a specific setting value by key path.
	 * Supports dot notation for nested keys (e.g., 'ui_preferences.view_mode').
	 *
	 * @since 1.0.0
	 * @param string $key Setting key or dot-notation path.
	 * @param mixed  $default Default value if setting not found.
	 * @return mixed Setting value or default.
	 */
	public function get( string $key, mixed $default = null ): mixed {
		$this->ensureSettingsLoaded();

		$settings = $this->getAllSettings();

		if ( strpos( $key, '.' ) !== false ) {
			$keys = explode( '.', $key );
			$value = $settings;

			foreach ( $keys as $k ) {
				if ( ! is_array( $value ) || ! isset( $value[ $k ] ) ) {
					return $default;
				}
				$value = $value[ $k ];
			}

			return $value;
		}

		// Simple key lookup
		return $settings[ $key ] ?? $default;
	}

	/**
	 * Whether runtime module performance tracking is enabled (Settings → Performance Monitor).
	 */
	public function isPerformanceMonitorEnabled(): bool {
		return (bool) $this->get( 'enable_performance_monitor', false );
	}

	/**
	 * Set Setting
	 *
	 * Sets a specific setting value by key path.
	 * Supports dot notation for nested keys.
	 *
	 * @since 1.0.0
	 * @param string $key Setting key or dot-notation path.
	 * @param mixed  $value Value to set.
	 * @return bool True on success, false on failure.
	 */
	public function set( string $key, mixed $value ): bool {
		$this->ensureSettingsLoaded();

		$settings = $this->settings_cache ?? [];

		if ( strpos( $key, '.' ) !== false ) {
			$keys = explode( '.', $key );
			$target = &$settings;

			foreach ( $keys as $k ) {
				if ( ! is_array( $target ) ) {
					$target = [];
				}
				if ( ! isset( $target[ $k ] ) ) {
					$target[ $k ] = [];
				}
				$target = &$target[ $k ];
			}

			$target = $value;
		} else {
			// Simple key assignment
			$settings[ $key ] = $value;
		}

		// Core list toggles must not re-sanitize every module settings blob.
		// That path runs enabled-module filters and can fail/timeout unrelated enables.
		$sanitize_module_blobs = ! in_array(
			$key,
			[
				'enabled_modules',
				'favorites',
				'favorites_customized',
				'legacy_favorites_customized_migration',
				'module_order',
				'quick_launcher_shortcut',
				'enable_performance_monitor',
				'social_oauth_debug',
			],
			true
		);

		$settings = $this->validateAndSanitize( $settings, $sanitize_module_blobs );

		// Persist first; only update the request cache after a durable write so
		// a failed update_option never leaves this process serving a phantom value.
		// Cross-request caching is left to WordPress options + the object-cache
		// drop-in (get_option / update_option), not a parallel wpps_* key.
		if ( ! $this->writeStoredSettings( $settings ) ) {
			return false;
		}

		$this->settings_cache = $settings;

		return true;
	}

	/**
	 * Get Enabled Modules
	 *
	 * Returns an array of enabled module IDs.
	 *
	 * @since 1.0.0
	 * @return array<string> Array of module IDs.
	 */
	public function getEnabledModules(): array {
		$modules = $this->get( 'enabled_modules', [] );
		return is_array( $modules ) ? $modules : [];
	}

	/**
	 * Set Enabled Modules
	 *
	 * Sets the list of enabled module IDs.
	 *
	 * @since 1.0.0
	 * @param array<string> $module_ids Array of module IDs.
	 * @return bool True on success, false on failure.
	 */
	public function setEnabledModules( array $module_ids ): bool {
		$module_ids = array_map( 'sanitize_key', $module_ids );
		$module_ids = array_filter( $module_ids, function( $id ) {
			return ! empty( $id );
		} );
		$module_ids = array_values( array_unique( $module_ids ) );

		return $this->set( 'enabled_modules', $module_ids );
	}

	/**
	 * Check if Module is Enabled
	 *
	 * Checks if a specific module is enabled.
	 *
	 * @since 1.0.0
	 * @param string $module_id Module ID to check.
	 * @return bool True if enabled, false otherwise.
	 */
	public function isModuleEnabled( string $module_id ): bool {
		$enabled_modules = $this->getEnabledModules();
		return in_array( sanitize_key( $module_id ), $enabled_modules, true );
	}

	/**
	 * Enable Module
	 *
	 * Enables a module by adding it to the enabled modules list.
	 *
	 * @since 1.0.0
	 * @param string $module_id Module ID to enable.
	 * @return bool True on success, false on failure.
	 */
	public function enableModule( string $module_id ): bool {
		$module_id = sanitize_key( $module_id );
		if ( empty( $module_id ) ) {
			return false;
		}

		$enabled_modules = $this->getEnabledModules();

		if ( ! in_array( $module_id, $enabled_modules, true ) ) {
			$enabled_modules[] = $module_id;
			return $this->setEnabledModules( $enabled_modules );
		}

		return true; // Already enabled
	}

	/**
	 * Disable Module
	 *
	 * Disables a module by removing it from the enabled modules list.
	 *
	 * @since 1.0.0
	 * @param string $module_id Module ID to disable.
	 * @return bool True on success, false on failure.
	 */
	public function disableModule( string $module_id ): bool {
		$module_id = sanitize_key( $module_id );
		if ( empty( $module_id ) ) {
			return false;
		}

		$enabled_modules = $this->getEnabledModules();
		$key = array_search( $module_id, $enabled_modules, true );

		if ( $key !== false ) {
			unset( $enabled_modules[ $key ] );
			$enabled_modules = array_values( $enabled_modules ); // Re-index
			return $this->setEnabledModules( $enabled_modules );
		}

		return true; // Already disabled
	}

	/**
	 * Get Favorites
	 *
	 * Returns an array of favorite module IDs.
	 *
	 * @since 1.0.0
	 * @return array<string> Array of favorite module IDs.
	 */
	public function getFavorites(): array {
		if ( $this->get( 'favorites_customized', false ) ) {
			$favorites = $this->get( 'favorites', [] );
			if ( ! is_array( $favorites ) ) {
				return [];
			}
			$favorites = array_map( 'sanitize_key', $favorites );
			$favorites = array_filter( $favorites, static function ( $id ) {
				return $id !== '';
			} );
			return array_values( array_unique( $favorites ) );
		}

		return $this->resolveRecommendedFavoriteIds();
	}

	/**
	 * Set Favorites
	 *
	 * Sets the list of favorite module IDs.
	 *
	 * @since 1.0.0
	 * @param array<string> $module_ids Array of favorite module IDs.
	 * @return bool True on success, false on failure.
	 */
	public function setFavorites( array $module_ids ): bool {
		$module_ids = array_map( 'sanitize_key', $module_ids );
		$module_ids = array_filter(
			$module_ids,
			static function ( $id ) {
				return ! empty( $id );
			}
		);
		$module_ids = array_values( array_unique( $module_ids ) );

		$ok = $this->set( 'favorites', $module_ids );
		if ( ! $ok ) {
			return false;
		}

		return $this->set( 'favorites_customized', true );
	}

	/**
	 * Check if Module is Favorite
	 *
	 * Checks if a specific module is marked as favorite.
	 *
	 * @since 1.0.0
	 * @param string $module_id Module ID to check.
	 * @return bool True if favorite, false otherwise.
	 */
	public function isFavorite( string $module_id ): bool {
		$favorites = $this->getFavorites();
		return in_array( sanitize_key( $module_id ), $favorites, true );
	}

	/**
	 * Add Favorite
	 *
	 * Adds a module to favorites.
	 *
	 * @since 1.0.0
	 * @param string $module_id Module ID to add to favorites.
	 * @return bool True on success, false on failure.
	 */
	public function addFavorite( string $module_id ): bool {
		$module_id = sanitize_key( $module_id );
		if ( empty( $module_id ) ) {
			return false;
		}

		$favorites = $this->getFavorites();

		if ( ! in_array( $module_id, $favorites, true ) ) {
			$favorites[] = $module_id;
			return $this->setFavorites( $favorites );
		}

		return true; // Already a favorite
	}

	/**
	 * Remove Favorite
	 *
	 * Removes a module from favorites.
	 *
	 * @since 1.0.0
	 * @param string $module_id Module ID to remove from favorites.
	 * @return bool True on success, false on failure.
	 */
	public function removeFavorite( string $module_id ): bool {
		$module_id = sanitize_key( $module_id );
		if ( empty( $module_id ) ) {
			return false;
		}

		$favorites = $this->getFavorites();
		$key = array_search( $module_id, $favorites, true );

		if ( $key !== false ) {
			unset( $favorites[ $key ] );
			$favorites = array_values( $favorites ); // Re-index
			return $this->setFavorites( $favorites );
		}

		return true; // Already not a favorite
	}

	/**
	 * Get UI Preferences
	 *
	 * Returns all UI preferences.
	 *
	 * @since 1.0.0
	 * @return array<string, mixed> UI preferences.
	 */
	public function getUiPreferences(): array {
		$prefs = $this->get( 'ui_preferences', [] );
		return is_array( $prefs ) ? $prefs : $this->defaults['ui_preferences'];
	}

	/**
	 * Set UI Preferences
	 *
	 * Sets UI preferences.
	 *
	 * @since 1.0.0
	 * @param array<string, mixed> $preferences UI preferences.
	 * @return bool True on success, false on failure.
	 */
	public function setUiPreferences( array $preferences ): bool {
		$current_prefs = $this->getUiPreferences();
		$preferences = array_merge( $current_prefs, $preferences );

		if ( isset( $preferences['view_mode'] ) ) {
			$preferences['view_mode'] = in_array( $preferences['view_mode'], [ 'grid', 'list' ], true )
				? $preferences['view_mode']
				: 'grid';
		}

		if ( isset( $preferences['description_mode'] ) ) {
			$preferences['description_mode'] = in_array( $preferences['description_mode'], [ 'show', 'hide', 'truncate' ], true )
				? $preferences['description_mode']
				: 'show';
		}

		return $this->set( 'ui_preferences', $preferences );
	}

	/**
	 * Get View Mode
	 *
	 * Returns the current view mode (grid or list).
	 *
	 * @since 1.0.0
	 * @return string View mode: 'grid' or 'list'.
	 */
	public function getViewMode(): string {
		$view_mode = $this->get( 'ui_preferences.view_mode', 'grid' );
		return in_array( $view_mode, [ 'grid', 'list' ], true ) ? $view_mode : 'grid';
	}

	/**
	 * Set View Mode
	 *
	 * Sets the view mode (grid or list).
	 *
	 * @since 1.0.0
	 * @param string $mode View mode: 'grid' or 'list'.
	 * @return bool True on success, false on failure.
	 */
	public function setViewMode( string $mode ): bool {
		$mode = in_array( $mode, [ 'grid', 'list' ], true ) ? $mode : 'grid';
		return $this->set( 'ui_preferences.view_mode', $mode );
	}

	/**
	 * Get Description Mode
	 *
	 * Returns the description display mode for grid view.
	 *
	 * @since 1.0.0
	 * @return string 'show', 'hide', or 'truncate'.
	 */
	public function getDescriptionMode(): string {
		$mode = $this->get( 'ui_preferences.description_mode', 'show' );
		return in_array( $mode, [ 'show', 'hide', 'truncate' ], true ) ? $mode : 'show';
	}

	/**
	 * Set Description Mode
	 *
	 * Sets the description display mode for grid view.
	 *
	 * @since 1.0.0
	 * @param string $mode 'show', 'hide', or 'truncate'.
	 * @return bool True on success, false on failure.
	 */
	public function setDescriptionMode( string $mode ): bool {
		$mode = in_array( $mode, [ 'show', 'hide', 'truncate' ], true ) ? $mode : 'show';
		return $this->set( 'ui_preferences.description_mode', $mode );
	}

	/**
	 * Get Module Order
	 *
	 * Returns the saved module order array.
	 *
	 * @since 1.0.0
	 * @return array<string> Array of module IDs in order.
	 */
	public function getModuleOrder(): array {
		$order = $this->get( 'module_order', [] );
		return is_array( $order ) ? $order : [];
	}

	/**
	 * Set Module Order
	 *
	 * Sets the module order array.
	 *
	 * @since 1.0.0
	 * @param array<string> $order Array of module IDs in order.
	 * @return bool True on success, false on failure.
	 */
	public function setModuleOrder( array $order ): bool {
		$order = array_map( 'sanitize_key', $order );
		$order = array_filter( $order, function( $id ) {
			return ! empty( $id );
		} );
		$order = array_values( array_unique( $order ) );

		return $this->set( 'module_order', $order );
	}

	/**
	 * Persist interface appearance settings in a single write.
	 */
	public function setInterfaceAppearance( string $mode, string $color ): bool {
		$appearance = AdminAppearance::sanitizeSettings(
			[
				'interface_color_mode' => $mode,
				'custom_brand_color'   => $color,
			]
		);

		$this->ensureSettingsLoaded();
		$settings = $this->settings_cache ?? [];
		$settings['interface_color_mode'] = $appearance['interface_color_mode'];
		$settings['custom_brand_color']   = $appearance['custom_brand_color'];
		$settings                         = $this->validateAndSanitize( $settings );

		if ( ! $this->writeStoredSettings( $settings ) ) {
			return false;
		}

		$this->settings_cache = $settings;
		AdminAppearance::clearCache();

		return true;
	}

	/**
	 * Curated default favorites (high-demand modules; only IDs present in the registry are shown).
	 *
	 * Order: security → login → infrastructure → admin → workflows → media → speed → site tools.
	 *
	 * @since 1.0.0
	 * @return array<string>
	 */
	private static function curatedFavoriteModuleIds(): array {
		return [
			// Security
			'limit-login-attempts',
			'two-factor-authentication',
			'change-login-url',
			'google-recaptcha',
			'cloudflare-turnstile',
			'simple-captcha',
			'advanced-user-activity-log',
			// Login & users
			'magic-login',
			'login-as-user',
			'temporary-login',
			'alphabet-avatars',
			'facebook-sign-in',
			'google-sign-in',
			'github-sign-in',
			'login-page-customizor',
			// Email & infrastructure
			'email-smtp',
			'backup-restore',
			'custom-wp-emails',
			'email-logs',
			// Admin
			'admin-menu-organizer',
			'remove-admin-notices',
			'admin-columns-customizer',
			// Content, SEO & workflows
			'redirect-manager',
			'404-redirect',
			'broken-link-checker',
			'duplicate-post-type',
			'custom-fields',
			'cpt-builder',
			'content-folders',
			'options-page-builder',
			'link-shortener',
			'basic-forms',
			// Media
			'webp-conversion',
			'ai-alt-text-generator',
			'svg-upload',
			// Site tools
			'coming-soon-maintenance',
			'code-snippet',
		];
	}

	/**
	 * Whether the user has ever customized favorites (star/unstar); when false, effective favorites use curated defaults.
	 *
	 * @since 1.0.0
	 * @return bool True when using stored favorites only.
	 */
	public function favoritesUseRecommendedDefaults(): bool {
		return ! $this->get( 'favorites_customized', false );
	}

	/**
	 * Resolve curated favorite IDs that exist in the module registry.
	 *
	 * @since 1.0.0
	 * @return array<string>
	 */
	private function resolveRecommendedFavoriteIds(): array {
		$recommended = self::curatedFavoriteModuleIds();
		try {
			$registry = ModuleRegistry::getInstance();
			$registry->init( false );
			$all_ids = array_keys( $registry->getAllModules() );
		} catch ( \Throwable $e ) {
			return [];
		}

		$out = [];
		foreach ( $recommended as $id ) {
			$id = sanitize_key( $id );
			if ( $id !== '' && in_array( $id, $all_ids, true ) ) {
				$out[] = $id;
			}
		}
		return $out;
	}

	/**
	 * One-time migration: infer favorites_customized from legacy installs that never stored this flag.
	 *
	 * @since 1.0.0
	 * @param array<string,mixed> $db_settings Raw option row from the database (not merged with defaults).
	 * @return bool True when the database row was updated.
	 */
	private function maybeMigrateLegacyFavoriteCustomization( array $db_settings ): bool {
		if ( ! empty( $db_settings['legacy_favorites_customized_migration'] ) ) {
			return false;
		}
		if ( array_key_exists( 'favorites_customized', $db_settings ) ) {
			return false;
		}

		$favorites_legacy = isset( $db_settings['favorites'] ) && is_array( $db_settings['favorites'] ) ? $db_settings['favorites'] : [];
		$db_settings['favorites_customized']                  = ! empty( $favorites_legacy );
		$db_settings['legacy_favorites_customized_migration'] = true;

		$merged = array_merge( $this->defaults, $db_settings );
		return $this->writeStoredSettings( $merged );
	}

	/**
	 * Ensure Settings Loaded
	 *
	 * Ensures settings are loaded from database if not already cached.
	 * Uses object cache to reduce database queries.
	 *
	 * @since 1.0.0
	 * @return void
	 */
	private function ensureSettingsLoaded(): void {
		if ( $this->settings_cache !== null ) {
			return;
		}

		// Always load via get_option / get_site_option so the active object-cache
		// drop-in stays the single cross-request cache for this payload.
		$raw_settings = $this->readStoredSettings();
		if ( ! is_array( $raw_settings ) ) {
			$raw_settings = [];
		}

		if ( $this->maybeMigrateLegacyFavoriteCustomization( $raw_settings ) ) {
			$raw_settings = $this->readStoredSettings();
		}

		$this->settings_cache = array_merge( $this->defaults, $raw_settings );
	}

	/**
	 * Read raw settings from the database for the active storage scope.
	 *
	 * @since 1.0.0
	 * @return array<string, mixed>
	 */
	private function readStoredSettings(): array {
		if ( NetworkScope::usesNetworkStorage() ) {
			$settings = get_site_option( $this->option_key, [] );
		} else {
			$settings = get_option( $this->option_key, [] );
		}

		return is_array( $settings ) ? $settings : [];
	}

	/**
	 * Persist settings to the database for the active storage scope.
	 *
	 * @since 1.0.0
	 * @param array<string, mixed> $settings Settings payload.
	 * @return bool
	 */
	private function writeStoredSettings( array $settings ): bool {
		if ( NetworkScope::usesNetworkStorage() ) {
			$updated = update_site_option( $this->option_key, $settings );
			if ( $updated ) {
				return true;
			}

			$current = $this->readStoredSettings();
			return maybe_serialize( $current ) === maybe_serialize( $settings );
		}

		$updated = update_option( $this->option_key, $settings, false );
		if ( $updated ) {
			return true;
		}

		// update_option() returns false when the value is unchanged as well as on failure.
		$current = $this->readStoredSettings();

		return maybe_serialize( $current ) === maybe_serialize( $settings );
	}

	/**
	 * Delete stored settings for the active storage scope.
	 *
	 * @since 1.0.0
	 * @return bool
	 */
	private function deleteStoredSettings(): bool {
		if ( NetworkScope::usesNetworkStorage() ) {
			return delete_site_option( $this->option_key );
		}

		return delete_option( $this->option_key );
	}

	/**
	 * Validate and Sanitize Settings
	 *
	 * Validates and sanitizes settings before saving to database.
	 *
	 * @since 1.0.0
	 * @param array<string, mixed> $settings              Settings to validate.
	 * @param bool                 $sanitize_module_blobs When false, preserve existing module
	 *                                                    settings blobs as-is (used for enable/favorites toggles).
	 * @return array<string, mixed> Validated and sanitized settings.
	 */
	private function validateAndSanitize( array $settings, bool $sanitize_module_blobs = true ): array {
		$validated = [];

		if ( isset( $settings['enabled_modules'] ) ) {
			$validated['enabled_modules'] = is_array( $settings['enabled_modules'] )
				? array_map( 'sanitize_key', $settings['enabled_modules'] )
				: [];
		}

		if ( isset( $settings['favorites'] ) ) {
			$validated['favorites'] = is_array( $settings['favorites'] )
				? array_map( 'sanitize_key', $settings['favorites'] )
				: [];
		}

		if ( array_key_exists( 'favorites_customized', $settings ) ) {
			$validated['favorites_customized'] = (bool) $settings['favorites_customized'];
		}

		if ( array_key_exists( 'legacy_favorites_customized_migration', $settings ) ) {
			$validated['legacy_favorites_customized_migration'] = (bool) $settings['legacy_favorites_customized_migration'];
		}

		if ( isset( $settings['module_order'] ) ) {
			$validated['module_order'] = is_array( $settings['module_order'] )
				? array_map( 'sanitize_key', $settings['module_order'] )
				: [];
		}

		if ( isset( $settings['ui_preferences'] ) && is_array( $settings['ui_preferences'] ) ) {
			$validated['ui_preferences'] = [];

			if ( isset( $settings['ui_preferences']['view_mode'] ) ) {
				$view_mode = $settings['ui_preferences']['view_mode'];
				$validated['ui_preferences']['view_mode'] = in_array( $view_mode, [ 'grid', 'list' ], true )
					? $view_mode
					: 'grid';
			}
			if ( isset( $settings['ui_preferences']['description_mode'] ) ) {
				$desc_mode = $settings['ui_preferences']['description_mode'];
				$validated['ui_preferences']['description_mode'] = in_array( $desc_mode, [ 'show', 'hide', 'truncate' ], true )
					? $desc_mode
					: 'show';
			}
		}

		$valid_shortcuts = [ 'ctrl-k', 'ctrl-shift-k', 'alt-p', 'ctrl-alt-k' ];
		if ( isset( $settings['quick_launcher_shortcut'] ) ) {
			$shortcut = $settings['quick_launcher_shortcut'];
			$validated['quick_launcher_shortcut'] = in_array( $shortcut, $valid_shortcuts, true ) ? $shortcut : 'ctrl-shift-k';
		}

		if ( array_key_exists( 'enable_performance_monitor', $settings ) ) {
			$validated['enable_performance_monitor'] = (bool) $settings['enable_performance_monitor'];
		}

		if ( isset( $settings['interface_color_mode'] ) || isset( $settings['custom_brand_color'] ) ) {
			$appearance = AdminAppearance::sanitizeSettings(
				[
					'interface_color_mode' => $settings['interface_color_mode'] ?? AdminAppearance::MODE_WORDPRESS_DEFAULT,
					'custom_brand_color'   => $settings['custom_brand_color'] ?? AdminAppearance::DEFAULT_BRAND_COLOR,
				]
			);
			$validated['interface_color_mode'] = $appearance['interface_color_mode'];
			$validated['custom_brand_color']   = $appearance['custom_brand_color'];
			AdminAppearance::clearCache();
		}

		// Module-specific settings blobs: sanitize only when the write intends to change them.
		if ( $sanitize_module_blobs ) {
			foreach ( $settings as $key => $value ) {
				if ( ! ModuleSettingsSanitizer::isModuleSettingsKey( (string) $key ) || ! is_array( $value ) ) {
					continue;
				}
				$validated[ $key ] = ModuleSettingsSanitizer::sanitizeModuleSettings( (string) $key, $value, 'save' );
			}
		}

		if ( isset( $settings['white_label'] ) && is_array( $settings['white_label'] ) ) {
			$validated['white_label'] = WhiteLabel::sanitizeSettings( $settings['white_label'] );
			WhiteLabel::clearCache();
		}

		$this->ensureSettingsLoaded();
		$validated = array_merge( $this->settings_cache ?? [], $validated );

		return $validated;
	}

	/**
	 * Clear Cache
	 *
	 * Clears the request-level settings cache.
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function clearCache(): void {
		$this->settings_cache = null;
		AdminAppearance::clearCache();
	}

	/**
	 * Seed default settings when nothing is stored yet (first activation).
	 *
	 * Does not overwrite existing settings on reactivation or upgrade.
	 *
	 * @since 1.0.0
	 * @return bool True when defaults were written, false when settings already exist.
	 */
	public function seedDefaultsIfMissing(): bool {
		if ( NetworkScope::usesNetworkStorage() ) {
			$exists = get_site_option( $this->option_key ) !== false;
		} else {
			$exists = get_option( $this->option_key ) !== false;
		}

		if ( $exists ) {
			return false;
		}

		$this->settings_cache = $this->defaults;
		return $this->writeStoredSettings( $this->defaults );
	}

	/**
	 * Reset to Defaults
	 *
	 * Resets all settings to default values.
	 *
	 * @since 1.0.0
	 * @return bool True on success, false on failure.
	 */
	public function resetToDefaults(): bool {
		$this->settings_cache = $this->defaults;
		return $this->writeStoredSettings( $this->defaults );
	}

	/**
	 * Delete All Settings
	 *
	 * Deletes all plugin settings from the database.
	 *
	 * @since 1.0.0
	 * @return bool True on success, false on failure.
	 */
	public function deleteAll(): bool {
		$this->settings_cache = null;
		return $this->deleteStoredSettings();
	}
}

