<?php
/**
 * Coming Soon & Maintenance Module
 *
 * @package ComingSoonMaintenance
 * @since 1.0.0
 */

namespace ComingSoonMaintenance;

use WP_PowerSuite\Core\AbstractModule;
use WP_PowerSuite\Core\Contracts\DataLocationsInterface;
use WP_PowerSuite\Core\NetworkScope;
use WP_PowerSuite\Core\OptionStoreModuleSettingsRepository;

class Module extends AbstractModule implements DataLocationsInterface {

	private const MODULE_ID           = 'coming-soon-maintenance';
	private const MODULE_VERSION      = '1.1.0';
	private const SUBSCRIBERS_OPTION  = 'wp_powersuite_coming_soon_maintenance_subscribers';
	private const NONCE_ACTION        = 'wpps_csm_subscribe';
	private const BYPASS_COOKIE       = 'wpps_csm_bypass';
	private const AUTO_DISABLE_LOCK   = 'wpps_csm_auto_disable_lock';

	private ?array $settings_cache = null;

	public function getId(): string {
		return self::MODULE_ID;
	}

	public function getName(): string {
		return __( 'Coming Soon & Maintenance', 'wp-powersuite' );
	}

	public function getVersion(): string {
		return self::MODULE_VERSION;
	}

	public function getDescription(): string {
		return __( 'Coming-soon or maintenance landing pages; staff and allowed roles bypass to the real site.', 'wp-powersuite' );
	}

	public function getContext(): string|array {
		return [ 'frontend', 'admin', 'ajax' ];
	}

	public function getDependencies(): array {
		return [];
	}

	private function isLicenseActive(): bool {
		return function_exists( 'wp_powersuite_is_license_valid' ) && wp_powersuite_is_license_valid();
	}

	protected function registerHooks(): void {
		if ( ! $this->isLicenseActive() ) {
			return;
		}

		if ( is_admin() ) {
			add_action( 'admin_menu', [ $this, 'addAdminMenu' ], 20 );
			add_action( 'admin_enqueue_scripts', [ $this, 'enqueueAdminAssets' ], 10 );
			add_action( 'wp_ajax_wpps_csm_regenerate_bypass', [ $this, 'handleRegenerateBypass' ] );
		}

		$settings  = $this->getSettings();
		$is_active = ! empty( $settings['enabled'] );

		add_action( 'template_redirect', [ $this, 'maybeShowPage' ], 1 );
		add_action( 'init', [ $this, 'maybeBlockPublicAjax' ], 1 );
		add_filter( 'rest_pre_dispatch', [ $this, 'filterRestPreDispatch' ], 10, 3 );
		add_filter( 'xmlrpc_enabled', [ $this, 'filterXmlRpcEnabled' ] );

		if ( $is_active ) {
			add_action( 'do_feed_rdf', [ $this, 'disableFeed' ], 0 );
			add_action( 'do_feed_rss', [ $this, 'disableFeed' ], 0 );
			add_action( 'do_feed_rss2', [ $this, 'disableFeed' ], 0 );
			add_action( 'do_feed_atom', [ $this, 'disableFeed' ], 0 );
		}

		add_action( 'admin_bar_menu', [ $this, 'addAdminBarNotice' ], 100 );
		add_action( 'admin_head', [ $this, 'addAdminBarStyles' ], 999 );
		add_action( 'wp_head', [ $this, 'addAdminBarStyles' ], 999 );
		add_action( 'admin_footer', [ $this, 'addAdminBarScript' ] );
		add_action( 'wp_footer', [ $this, 'addAdminBarScript' ] );

		add_action( 'wp_ajax_wpps_csm_subscribe', [ $this, 'handleSubscription' ] );
		add_action( 'wp_ajax_nopriv_wpps_csm_subscribe', [ $this, 'handleSubscription' ] );
		add_action( 'wp_ajax_nopriv_wpps_csm_get_subscribe_nonce', [ $this, 'handleSubscribeNonce' ] );
		add_action( 'wp_ajax_wpps_csm_get_subscribe_nonce', [ $this, 'handleSubscribeNonce' ] );

		add_action( 'wp_powersuite_module_settings_saved', [ $this, 'onSettingsSaved' ], 10, 2 );
	}

	public function onSettingsSaved( string $module_id, array $settings ): void {
		if ( $module_id !== self::MODULE_ID ) {
			return;
		}
		self::purgeCaches();
	}

	/**
	 * Purge common page-cache plugins (callable from lifecycle listener).
	 *
	 * Deliberately does not call wp_cache_flush(): that would wipe the whole
	 * object cache (all sites' transients, queries, etc.) just because this
	 * module's settings changed.
	 */
	public static function purgeCaches(): void {
		if ( function_exists( 'w3tc_flush_all' ) ) {
			w3tc_flush_all();
		}
		if ( function_exists( 'wp_cache_clear_cache' ) ) {
			wp_cache_clear_cache();
		}
		if ( class_exists( 'LiteSpeed_Cache_API' ) && method_exists( 'LiteSpeed_Cache_API', 'purge_all' ) ) {
			\LiteSpeed_Cache_API::purge_all();
		}
		if ( function_exists( 'rocket_clean_domain' ) ) {
			rocket_clean_domain();
		}
		do_action( 'cache_enabler_clear_complete_cache' );
		do_action( 'wpps_csm_cache_purged' );
	}

	public function disableFeed(): void {
		$settings = $this->getSettings();
		if ( empty( $settings['enabled'] ) ) {
			return;
		}
		if ( ! apply_filters( 'wpps_csm_should_show_page', true, $settings ) ) {
			return;
		}

		$mode = $settings['mode'] ?? 'coming_soon';
		header( 'Content-Type: application/xml; charset=' . get_option( 'blog_charset' ) );
		header( 'X-Robots-Tag: noindex, nofollow', true );

		if ( $mode === 'maintenance' ) {
			header( 'HTTP/1.1 503 Service Unavailable', true, 503 );
			header( 'Retry-After: 3600' );
			echo '<?xml version="1.0" encoding="' . esc_attr( get_option( 'blog_charset' ) ) . '"?><status>Service unavailable.</status>';
			exit;
		}

		header( 'HTTP/1.1 200 OK', true, 200 );
		echo '<?xml version="1.0" encoding="' . esc_attr( get_option( 'blog_charset' ) ) . '"?><rss version="2.0"><channel><title>' . esc_xml( get_bloginfo( 'name' ) ) . '</title></channel></rss>';
		exit;
	}

	public function maybeShowPage(): void {
		if ( apply_filters( 'wpps_tl_is_token_login_request', false ) ) {
			return;
		}

		if ( ( defined( 'DOING_CRON' ) && DOING_CRON ) || ( defined( 'WP_CLI' ) && WP_CLI ) ) {
			return;
		}

		if ( is_admin() || $this->isLoginPage() ) {
			return;
		}

		if ( $this->isValidInternalFetchRequest() ) {
			return;
		}

		if ( is_feed() || $this->isRobotsTxt() ) {
			return;
		}

		$settings = $this->getSettings();
		$mode     = $settings['mode'] ?? 'coming_soon';
		$cap      = $this->adminCapability();

		$is_preview = isset( $_GET['wpps_csm_preview'] ) && current_user_can( $cap );
		if ( $is_preview ) {
			$this->outputPage( $settings, true );
			exit;
		}

		if ( empty( $settings['enabled'] ) ) {
			return;
		}

		if ( isset( $_GET['bypass'] ) && $this->verifyBypassKey( sanitize_text_field( wp_unslash( $_GET['bypass'] ) ) ) ) {
			$this->setBypassCookie();
			return;
		}

		if ( $this->hasBypassCookie() ) {
			return;
		}

		if ( $this->isExcludedUrl() ) {
			return;
		}

		if ( $this->isUserAllowedAccess() ) {
			return;
		}

		if ( ! apply_filters( 'wpps_csm_should_show_page', true, $settings ) ) {
			return;
		}

		if ( $this->maybeAutoDisable( $settings, $mode ) ) {
			return;
		}

		if ( ! empty( $settings['use_existing_page'] ) && ! empty( $settings['existing_page_url'] ) ) {
			$this->outputExistingPage( $settings );
			exit;
		}

		$this->outputPage( $settings, false );
		exit;
	}

	public function maybeBlockPublicAjax(): void {
		if ( ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
			return;
		}
		if ( ! $this->isPublicGateActive() ) {
			return;
		}
		if ( $this->hasBypassCookie() || $this->isUserAllowedAccess() ) {
			return;
		}

		$action  = isset( $_REQUEST['action'] ) ? sanitize_key( wp_unslash( $_REQUEST['action'] ) ) : '';
		$allowed = apply_filters(
			'wpps_csm_allowed_ajax_actions',
			[ 'wpps_csm_subscribe', 'wpps_csm_get_subscribe_nonce' ]
		);
		if ( in_array( $action, $allowed, true ) ) {
			return;
		}

		status_header( 503 );
		wp_send_json_error(
			[ 'message' => __( 'Service temporarily unavailable.', 'wp-powersuite' ) ],
			503
		);
	}

	/**
	 * @param mixed $result
	 * @return mixed
	 */
	public function filterRestPreDispatch( $result, $server, $request ) {
		unset( $server, $request );
		if ( ! $this->isPublicGateActive() ) {
			return $result;
		}
		if ( $this->hasBypassCookie() || $this->isUserAllowedAccess() ) {
			return $result;
		}
		return new \WP_Error(
			'wpps_csm_unavailable',
			__( 'Service temporarily unavailable.', 'wp-powersuite' ),
			[ 'status' => 503 ]
		);
	}

	public function filterXmlRpcEnabled( bool $enabled ): bool {
		if ( ! $enabled || ! $this->isPublicGateActive() ) {
			return $enabled;
		}
		if ( $this->hasBypassCookie() || $this->isUserAllowedAccess() ) {
			return $enabled;
		}
		return false;
	}

	public function handleSubscribeNonce(): void {
		if ( ! $this->isSubscriptionAllowed() ) {
			wp_send_json_error( [ 'message' => __( 'Subscriptions are not available.', 'wp-powersuite' ) ] );
		}

		$rate_key = 'wpps_csm_nonce_' . md5( $this->getClientIp() );
		$requests = (int) get_transient( $rate_key );
		if ( $requests >= 20 ) {
			wp_send_json_error( [ 'message' => __( 'Too many requests. Please try again later.', 'wp-powersuite' ) ] );
		}
		set_transient( $rate_key, $requests + 1, MINUTE_IN_SECONDS );

		wp_send_json_success( [ 'nonce' => wp_create_nonce( self::NONCE_ACTION ) ] );
	}

	public function handleRegenerateBypass(): void {
		if ( ! current_user_can( $this->adminCapability() ) ) {
			wp_send_json_error( [ 'message' => __( 'Permission denied.', 'wp-powersuite' ) ] );
		}
		check_ajax_referer( 'wp_powersuite_nonce', 'nonce' );

		$settings                   = $this->getSettings();
		$settings['bypass_token']   = wp_generate_password( 32, false );
		$this->saveSettings( $settings );

		wp_send_json_success(
			[
				'bypass_url' => add_query_arg( 'bypass', $settings['bypass_token'], home_url( '/' ) ),
				'message'    => __( 'Bypass link regenerated.', 'wp-powersuite' ),
			]
		);
	}

	private function isPublicGateActive(): bool {
		$settings = $this->getSettings();
		return ! empty( $settings['enabled'] );
	}

	private function isSubscriptionAllowed(): bool {
		if ( ! $this->isPublicGateActive() ) {
			return false;
		}
		$settings = $this->getSettings();
		$mode     = $settings['mode'] ?? 'coming_soon';
		return $mode === 'coming_soon' && ! empty( $settings['show_subscribe'] );
	}

	private function maybeAutoDisable( array $settings, string $mode ): bool {
		if ( $mode !== 'coming_soon' || empty( $settings['launch_date'] ) || empty( $settings['auto_disable'] ) ) {
			return false;
		}

		try {
			$launch_dt = new \DateTime( (string) $settings['launch_date'], wp_timezone() );
			if ( $launch_dt->getTimestamp() > (int) current_time( 'timestamp' ) ) {
				return false;
			}
		} catch ( \Exception $e ) {
			return false;
		}

		if ( get_transient( self::AUTO_DISABLE_LOCK ) ) {
			return true;
		}

		set_transient( self::AUTO_DISABLE_LOCK, 1, MINUTE_IN_SECONDS );
		$settings['enabled'] = 0;
		$this->saveSettings( $settings );
		self::purgeCaches();
		delete_transient( self::AUTO_DISABLE_LOCK );

		return true;
	}

	private function outputExistingPage( array $settings ): void {
		$url = trim( (string) ( $settings['existing_page_url'] ?? '' ) );
		if ( $url === '' ) {
			$this->outputPage( $settings, false );
			return;
		}
		$url  = esc_url_raw( $url );
		$home = untrailingslashit( home_url() );
		$site = untrailingslashit( site_url() );
		if ( ! $url || ( ! str_starts_with( $url, $home . '/' ) && $url !== $home && ! str_starts_with( $url, $site . '/' ) && $url !== $site ) ) {
			$this->outputPage( $settings, false );
			return;
		}

		$fetch_url = add_query_arg(
			[
				'wpps_csm_fetch'       => '1',
				'wpps_csm_fetch_token' => $this->buildFetchToken( $url ),
			],
			$url
		);
		$resp = wp_remote_get(
			$fetch_url,
			[
				'timeout'    => 15,
				'sslverify'  => ! wp_doing_cron(),
				'user-agent' => 'WP-PowerSuite-Coming-Soon/1.1',
			]
		);
		if ( is_wp_error( $resp ) || wp_remote_retrieve_response_code( $resp ) !== 200 ) {
			$this->outputPage( $settings, false );
			return;
		}

		$mode = $settings['mode'] ?? 'coming_soon';
		$this->sendResponseHeaders( $mode, false );
		// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Same-site fetched page HTML.
		echo wp_remote_retrieve_body( $resp );
		exit;
	}

	/**
	 * @param array<string, mixed> $settings
	 */
	private function outputPage( array $settings, bool $is_preview ): void {
		$settings = apply_filters( 'wpps_csm_settings', $settings );

		$mode = $settings['mode'] ?? 'coming_soon';
		$this->sendResponseHeaders( $mode, $is_preview );

		$title = $settings['page_title'] ?? '';
		if ( $title === '' ) {
			$title = get_bloginfo( 'name' ) . ' - ' . ( $mode === 'coming_soon' ? __( 'Coming Soon', 'wp-powersuite' ) : __( 'Under Maintenance', 'wp-powersuite' ) );
		}
		$heading     = $settings['heading'] ?? ( $mode === 'coming_soon' ? __( 'Something Amazing is Coming', 'wp-powersuite' ) : __( "We'll be back soon.", 'wp-powersuite' ) );
		$description = $settings['description'] ?? ( $mode === 'coming_soon' ? __( 'We are working hard to bring you something special. Stay tuned for updates!', 'wp-powersuite' ) : __( 'This site is undergoing maintenance. Please check back later.', 'wp-powersuite' ) );

		$template_vars = apply_filters(
			'wpps_csm_template_vars',
			[
				'title'       => $title,
				'heading'     => $heading,
				'description' => $description,
			],
			$settings
		);
		$title       = $template_vars['title'];
		$heading     = $template_vars['heading'];
		$description = $template_vars['description'];

		$show_logo = ! empty( $settings['show_logo'] );
		$logo_id   = (int) ( $settings['logo_id'] ?? 0 );
		$logo_url  = '';
		if ( $show_logo ) {
			$logo_source = (string) ( $settings['logo_source'] ?? 'upload' );
			if ( ! in_array( $logo_source, [ 'upload', 'site_icon', 'url', 'theme' ], true ) ) {
				$logo_source = 'upload';
			}
			$stored_logo_url = isset( $settings['logo_url'] ) ? esc_url_raw( trim( (string) $settings['logo_url'] ) ) : '';

			if ( $logo_source === 'url' && $stored_logo_url !== '' ) {
				$logo_url = $stored_logo_url;
			} elseif ( $logo_id > 0 ) {
				$logo_url = wp_get_attachment_image_url( $logo_id, 'medium' ) ?: '';
			} elseif ( $logo_source === 'upload' && $stored_logo_url !== '' ) {
				$logo_url = $stored_logo_url;
			} elseif ( $logo_source !== 'site_icon' ) {
				$custom_logo_id = (int) get_theme_mod( 'custom_logo', 0 );
				if ( $custom_logo_id ) {
					$logo_url = wp_get_attachment_image_url( $custom_logo_id, 'medium' ) ?: '';
				}
			}
			if ( ! $logo_url ) {
				$logo_url = get_site_icon_url( 512 ) ?: '';
			}
		}

		$content_align       = in_array( $settings['content_align'] ?? 'center', [ 'left', 'center', 'right' ], true ) ? (string) $settings['content_align'] : 'center';
		$container_max_width = max( 320, min( 1400, (int) ( $settings['container_max_width'] ?? 800 ) ) );
		$logo_align          = in_array( $settings['logo_align'] ?? 'center', [ 'left', 'center', 'right' ], true ) ? (string) $settings['logo_align'] : 'center';
		$design_css          = $this->buildDesignCss( $settings, $content_align, $container_max_width, $logo_align );
		$body_text_color     = $this->cssColor( $settings, 'text_color' ) ?: '#ffffff';

		$bg_type = $settings['bg_type'] ?? 'color';
		if ( in_array( $bg_type, [ 'lines', 'stripes', 'curves' ], true ) ) {
			$bg_type = 'patterns';
		}

		$bg_image_url = self::resolveBackgroundImageUrl( $settings );
		$show_subscribe = ! empty( $settings['show_subscribe'] ) && $mode === 'coming_soon';
		$launch_date    = $settings['launch_date'] ?? '';
		$custom_head    = $settings['custom_head'] ?? '';
		$custom_css     = $settings['custom_css'] ?? '';
		$consent_text   = trim( (string) ( $settings['subscribe_consent_text'] ?? '' ) );

		$social_links = $this->getSocialLinks( $settings );
		$has_social   = ! empty( $social_links );

		$bg_style      = $this->getBackgroundStyle( $settings );
		$needs_overlay = ( $bg_type === 'image' && $bg_image_url ) || $bg_type === 'patterns' || ( $bg_type === 'video' && ! empty( $settings['bg_video'] ?? '' ) );
		$overlay_style = $this->buildOverlayStyle( $settings, $bg_type, $needs_overlay );
		$overlay       = $needs_overlay ? '<div class="wpps-csm-overlay" style="' . $overlay_style . '"></div>' : '';

		if ( $has_social ) {
			$this->enqueueFrontendIconStyles();
		}

		?>
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
	<meta charset="<?php bloginfo( 'charset' ); ?>">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<meta name="robots" content="noindex, nofollow">
	<title><?php echo esc_html( $title ); ?></title>
	<?php wp_site_icon(); ?>
	<?php
	if ( $custom_head ) {
		echo wp_kses( $custom_head, $this->getKsesHeadRules() );
	}
	?>
	<style>
		* { margin: 0; padding: 0; box-sizing: border-box; }
		body {
			font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
			<?php echo $bg_style; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
			color: <?php echo esc_attr( $body_text_color ); ?>;
			display: flex;
			align-items: center;
			justify-content: center;
			min-height: 100vh;
			text-align: <?php echo esc_attr( $content_align ); ?>;
			padding: 20px;
			position: relative;
		}
		.wpps-csm-overlay { position: absolute; inset: 0; background: rgba(0,0,0,0.6); }
		.wpps-csm-container { max-width: <?php echo (int) $container_max_width; ?>px; position: relative; z-index: 1; animation: wpps-csm-fade 1s ease-out; margin-left: auto; margin-right: auto; }
		@keyframes wpps-csm-fade { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
		.wpps-csm-logo { margin-bottom: 2rem; text-align: <?php echo esc_attr( $logo_align ); ?>; }
		.wpps-csm-logo-inner { display: inline-block; line-height: 0; }
		.wpps-csm-logo img { width: auto; height: auto; display: block; border-radius: 0; box-shadow: none; object-fit: contain; }
		.wpps-csm-heading { font-size: clamp(2rem, 5vw, 3.5rem); font-weight: 800; line-height: 1.2; margin-bottom: 1rem; text-shadow: 0 2px 12px rgba(0,0,0,0.2); }
		.wpps-csm-description { font-size: clamp(1rem, 2vw, 1.25rem); opacity: 0.95; line-height: 1.6; margin-bottom: 2.5rem; }
		.wpps-csm-countdown { display: flex; flex-wrap: wrap; justify-content: center; gap: 1rem; margin-bottom: 2.5rem; }
		.wpps-csm-countdown-item { background: rgba(255,255,255,0.15); padding: 1.25rem 1.5rem; border-radius: 12px; backdrop-filter: blur(12px); border: 1px solid rgba(255,255,255,0.2); min-width: 80px; }
		.wpps-csm-countdown-value { font-size: clamp(2rem, 4vw, 2.5rem); font-weight: 800; display: block; }
		.wpps-csm-countdown-label { font-size: 0.75rem; opacity: 0.85; text-transform: uppercase; letter-spacing: 1px; margin-top: 0.5rem; }
		.wpps-csm-subscribe { max-width: 420px; margin: 0 auto 2rem; }
		.wpps-csm-subscribe form { display: flex; gap: 0.5rem; flex-wrap: wrap; justify-content: center; }
		.wpps-csm-subscribe label { width: 100%; text-align: center; font-size: 0.875rem; margin-bottom: 0.35rem; }
		.wpps-csm-subscribe input[type="email"] { flex: 1; min-width: 200px; padding: 0.875rem 1rem; border: 2px solid rgba(255,255,255,0.3); border-radius: 999px; background: rgba(255,255,255,0.1); color: #fff; font-size: 1rem; font-family: inherit; }
		.wpps-csm-subscribe input::placeholder { color: rgba(255,255,255,0.6); }
		.wpps-csm-subscribe button { padding: 0.875rem 1.5rem; background: #fff; color: #1e293b; border: none; border-radius: 999px; font-size: 1rem; font-weight: 700; font-family: inherit; cursor: pointer; transition: transform 0.2s, box-shadow 0.2s; }
		.wpps-csm-subscribe button:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(0,0,0,0.2); }
		.wpps-csm-subscribe button:disabled { opacity: 0.7; cursor: not-allowed; transform: none; }
		.wpps-csm-consent { font-size: 0.75rem; opacity: 0.85; margin-top: 0.75rem; text-align: center; line-height: 1.4; width: 100%; }
		.wpps-csm-message { margin-top: 1rem; padding: 0.75rem 1rem; border-radius: 8px; display: none; font-size: 1rem; font-family: inherit; }
		.wpps-csm-message.success { background: rgba(34,197,94,0.25); border: 1px solid rgba(34,197,94,0.5); }
		.wpps-csm-message.error { background: rgba(239,68,68,0.25); border: 1px solid rgba(239,68,68,0.5); }
		.wpps-csm-social { display: flex; justify-content: center; gap: 1rem; flex-wrap: wrap; }
		.wpps-csm-social a { width: 44px; height: 44px; background: rgba(255,255,255,0.15); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #fff; text-decoration: none; transition: background 0.2s, transform 0.2s; }
		.wpps-csm-social a:hover { background: rgba(255,255,255,0.3); transform: translateY(-2px); }
		.wpps-csm-social a i { font-size: 1.25rem; }
		<?php
		if ( $custom_css ) {
			echo $this->sanitizeCss( $custom_css ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		}
		if ( $design_css ) {
			echo $design_css; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		}
		if ( $bg_type === 'video' && ! empty( $settings['bg_video'] ?? '' ) ) {
			echo '.wpps-csm-video-wrap{position:fixed;inset:0;z-index:0;overflow:hidden;isolation:isolate;}.wpps-csm-video-wrap video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:0;pointer-events:none;}.wpps-csm-video-wrap .wpps-csm-overlay{position:absolute;inset:0;z-index:1;pointer-events:none;}body{position:relative;}.wpps-csm-container{position:relative;z-index:2;}';
		}
		?>
	</style>
</head>
<body>
	<?php
	$has_video_bg = ( $bg_type === 'video' && ! empty( $settings['bg_video'] ?? '' ) );
	if ( $has_video_bg ) {
		$video_url = esc_url( trim( (string) $settings['bg_video'] ) );
		echo '<div class="wpps-csm-video-wrap" aria-hidden="true"><video autoplay muted loop playsinline src="' . $video_url . '"></video>';
		if ( $overlay !== '' ) {
			echo $overlay; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		}
		echo '</div>';
	} elseif ( $overlay !== '' ) {
		echo $overlay; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	}
	?>
	<main class="wpps-csm-container">
		<?php if ( $logo_url ) : ?>
			<div class="wpps-csm-logo">
				<div class="wpps-csm-logo-inner">
					<img src="<?php echo esc_url( $logo_url ); ?>" alt="<?php echo esc_attr( get_bloginfo( 'name' ) ); ?>">
				</div>
			</div>
		<?php endif; ?>

		<h1 class="wpps-csm-heading"><?php echo wp_kses_post( $heading ); ?></h1>
		<div class="wpps-csm-description"><?php echo wp_kses_post( $description ); ?></div>

		<?php if ( $mode === 'coming_soon' && $launch_date ) : ?>
			<div class="wpps-csm-countdown" id="wpps-csm-countdown" aria-live="polite" aria-atomic="true">
				<div class="wpps-csm-countdown-item"><span class="wpps-csm-countdown-value" id="wpps-csm-days">00</span><span class="wpps-csm-countdown-label"><?php esc_html_e( 'Days', 'wp-powersuite' ); ?></span></div>
				<div class="wpps-csm-countdown-item"><span class="wpps-csm-countdown-value" id="wpps-csm-hours">00</span><span class="wpps-csm-countdown-label"><?php esc_html_e( 'Hours', 'wp-powersuite' ); ?></span></div>
				<div class="wpps-csm-countdown-item"><span class="wpps-csm-countdown-value" id="wpps-csm-minutes">00</span><span class="wpps-csm-countdown-label"><?php esc_html_e( 'Minutes', 'wp-powersuite' ); ?></span></div>
				<div class="wpps-csm-countdown-item"><span class="wpps-csm-countdown-value" id="wpps-csm-seconds">00</span><span class="wpps-csm-countdown-label"><?php esc_html_e( 'Seconds', 'wp-powersuite' ); ?></span></div>
			</div>
			<script>
			(function(){
				var wpTz = <?php echo wp_json_encode( $this->getLaunchTimestampMs( $launch_date ) ); ?>;
				if (wpTz > 0) {
					function u(){
						var d = wpTz - Date.now();
						if (d < 0) {
							document.getElementById('wpps-csm-countdown').innerHTML = '<div class="wpps-csm-countdown-item" style="min-width:auto;padding:1.5rem 2rem;"><strong><?php echo esc_js( __( "We're Live! Redirecting...", 'wp-powersuite' ) ); ?></strong></div>';
							setTimeout(function(){ location.reload(); }, 1500);
							return;
						}
						var days=Math.floor(d/86400000), h=Math.floor((d%86400000)/3600000), m=Math.floor((d%3600000)/60000), s=Math.floor((d%60000)/1000);
						['wpps-csm-days','wpps-csm-hours','wpps-csm-minutes','wpps-csm-seconds'].forEach(function(id,i){
							var el=document.getElementById(id); if(el) el.textContent=String([days,h,m,s][i]).padStart(2,'0');
						});
					}
					u(); setInterval(u,1000);
				}
			})();
			</script>
		<?php endif; ?>

		<?php if ( $show_subscribe ) : ?>
			<div class="wpps-csm-subscribe">
				<form id="wpps-csm-subscribe-form">
					<label for="wpps-csm-email"><?php esc_html_e( 'Email address', 'wp-powersuite' ); ?></label>
					<input type="email" id="wpps-csm-email" name="email" placeholder="<?php esc_attr_e( 'Enter your email', 'wp-powersuite' ); ?>" required autocomplete="email">
					<button type="submit"><?php esc_html_e( 'Notify Me', 'wp-powersuite' ); ?></button>
				</form>
				<?php if ( $consent_text !== '' ) : ?>
					<p class="wpps-csm-consent"><?php echo esc_html( $consent_text ); ?></p>
				<?php endif; ?>
				<div class="wpps-csm-message" id="wpps-csm-message" role="status" aria-live="polite"></div>
			</div>
			<script>
			(function(){
				var f=document.getElementById('wpps-csm-subscribe-form'), m=document.getElementById('wpps-csm-message'), ajaxUrl=<?php echo wp_json_encode( admin_url( 'admin-ajax.php' ) ); ?>;
				if(!f) return;
				function getNonce(){
					return fetch(ajaxUrl,{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'action=wpps_csm_get_subscribe_nonce'})
					.then(function(r){return r.json();})
					.then(function(d){return d.success&&d.data&&d.data.nonce?d.data.nonce:'';});
				}
				f.addEventListener('submit',function(e){
					e.preventDefault();
					var btn=f.querySelector('button'), email=f.querySelector('input[name="email"]').value;
					btn.disabled=true; btn.textContent=<?php echo wp_json_encode( __( 'Subscribing...', 'wp-powersuite' ) ); ?>;
					getNonce().then(function(nonce){
						if(!nonce){ m.textContent=<?php echo wp_json_encode( __( 'Unable to subscribe. Please refresh and try again.', 'wp-powersuite' ) ); ?>; m.className='wpps-csm-message error'; m.style.display='block'; btn.disabled=false; btn.textContent=<?php echo wp_json_encode( __( 'Notify Me', 'wp-powersuite' ) ); ?>; return; }
						var fd=new FormData(); fd.append('action','wpps_csm_subscribe'); fd.append('nonce',nonce); fd.append('email',email);
						return fetch(ajaxUrl,{method:'POST',body:fd}).then(function(r){return r.json();}).then(function(d){
							m.textContent=d.data&&d.data.message?d.data.message:'';
							m.className='wpps-csm-message '+(d.success?'success':'error');
							m.style.display='block';
							if(d.success) f.reset();
							btn.disabled=false; btn.textContent=<?php echo wp_json_encode( __( 'Notify Me', 'wp-powersuite' ) ); ?>;
						});
					});
				});
			})();
			</script>
		<?php endif; ?>

		<?php if ( $has_social ) : ?>
			<div class="wpps-csm-social">
				<?php foreach ( $social_links as $item ) : ?>
					<?php
					$url  = isset( $item['url'] ) ? esc_url( $item['url'] ) : '';
					$icon = isset( $item['icon'] ) ? $item['icon'] : 'link';
					if ( ! $url || $url === '#' ) {
						continue;
					}
					$labels = [
						'facebook'  => 'Facebook',
						'twitter'   => 'X',
						'instagram' => 'Instagram',
						'linkedin'  => 'LinkedIn',
						'youtube'   => 'YouTube',
						'pinterest' => 'Pinterest',
						'tiktok'    => 'TikTok',
						'github'    => 'GitHub',
						'rss'       => 'RSS',
						'link'      => 'Link',
						'mastodon'  => 'Mastodon',
						'threads'   => 'Threads',
					];
					$label = $labels[ $icon ] ?? 'Link';
					?>
					<a href="<?php echo $url; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>" target="_blank" rel="noopener noreferrer" aria-label="<?php echo esc_attr( $label ); ?>"><?php echo self::getSocialIconFa( $icon ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></a>
				<?php endforeach; ?>
			</div>
		<?php endif; ?>
	</main>
	<?php if ( $bg_type === 'video' && ! empty( $settings['bg_video'] ?? '' ) ) : ?>
	<script>(function(){var v=document.querySelector('.wpps-csm-video-wrap video');if(v&&v.pause){v.muted=true;v.play().catch(function(){});}})();</script>
	<?php endif; ?>
</body>
</html>
		<?php
	}

	private function sendResponseHeaders( string $mode, bool $is_preview ): void {
		if ( ! defined( 'DONOTCACHEPAGE' ) ) {
			define( 'DONOTCACHEPAGE', true );
		}

		header( 'X-Robots-Tag: noindex, nofollow', true );
		header( 'Cache-Control: no-store, no-cache, must-revalidate, max-age=0', true );
		header( 'Pragma: no-cache', true );

		if ( $is_preview ) {
			header( 'HTTP/1.1 200 OK', true, 200 );
			return;
		}

		if ( $mode === 'maintenance' ) {
			header( 'HTTP/1.1 503 Service Unavailable', true, 503 );
			header( 'Retry-After: 3600' );
			return;
		}

		header( 'HTTP/1.1 200 OK', true, 200 );
	}

	/**
	 * @param array<string, mixed> $settings
	 */
	private function buildOverlayStyle( array $settings, string $bg_type, bool $needs_overlay ): string {
		if ( ! $needs_overlay ) {
			return '';
		}

		$overlay_style = '';
		if ( $bg_type === 'video' ) {
			$oc = $settings['bg_video_overlay_color'] ?? '#000000';
			$oo = isset( $settings['bg_video_overlay_opacity'] ) ? max( 0, min( 100, (int) $settings['bg_video_overlay_opacity'] ) ) : 60;
			if ( preg_match( '/^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/', (string) $oc ) ) {
				$overlay_style = 'background-color:' . esc_attr( $oc ) . ';opacity:' . ( $oo / 100 ) . ';';
			}
		} elseif ( $bg_type === 'image' ) {
			$oc = $settings['bg_image_overlay_color'] ?? '#000000';
			$oo = isset( $settings['bg_image_overlay_opacity'] ) ? max( 0, min( 100, (int) $settings['bg_image_overlay_opacity'] ) ) : 60;
			if ( preg_match( '/^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/', (string) $oc ) ) {
				$overlay_style = 'background-color:' . esc_attr( $oc ) . ';opacity:' . ( $oo / 100 ) . ';';
			}
		} elseif ( $bg_type === 'patterns' ) {
			$oc = $settings['bg_pattern_overlay_color'] ?? '#000000';
			$oo = isset( $settings['bg_pattern_overlay_opacity'] ) ? max( 0, min( 100, (int) $settings['bg_pattern_overlay_opacity'] ) ) : 60;
			if ( preg_match( '/^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/', (string) $oc ) ) {
				$overlay_style = 'background-color:' . esc_attr( $oc ) . ';opacity:' . ( $oo / 100 ) . ';';
			}
		}

		return $overlay_style !== '' ? $overlay_style : 'background:rgba(0,0,0,0.6);';
	}

	private function enqueueFrontendIconStyles(): void {
		if ( wp_style_is( 'font-awesome-6', 'enqueued' ) ) {
			return;
		}
		wp_enqueue_style(
			'font-awesome-6',
			'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css',
			[],
			'6.5.1'
		);
		wp_print_styles( 'font-awesome-6' );
	}

	/**
	 * @param array<string, mixed> $settings
	 */
	private function getBackgroundStyle( array $settings ): string {
		$bg_type = $settings['bg_type'] ?? 'color';
		if ( in_array( $bg_type, [ 'lines', 'stripes', 'curves' ], true ) ) {
			$bg_type = 'patterns';
		}
		$bg_color     = $settings['bg_color'] ?? '#8dd3d3';
		$bg_image_url = self::resolveBackgroundImageUrl( $settings );

		if ( $bg_type === 'image' && $bg_image_url ) {
			$pos    = $this->sanitizeBgPosition( (string) ( $settings['bg_image_position'] ?? 'center center' ) );
			$size   = $this->sanitizeBgSize( (string) ( $settings['bg_image_size'] ?? 'cover' ) );
			$repeat = $this->sanitizeBgRepeat( (string) ( $settings['bg_image_repeat'] ?? 'no-repeat' ) );

			return "background-image: url('" . esc_url( $bg_image_url ) . "'); background-position: {$pos}; background-size: {$size}; background-repeat: {$repeat};";
		}
		if ( $bg_type === 'gradient' ) {
			$g = $this->buildGradientCss( $settings );
			return $g !== '' ? 'background: ' . $g . ';' : 'background-color: ' . esc_attr( sanitize_hex_color( $bg_color ) ?: '#8dd3d3' ) . ';';
		}
		if ( $bg_type === 'video' ) {
			return 'background-color: #000;';
		}
		if ( $bg_type === 'patterns' ) {
			$pattern = in_array( $settings['bg_pattern'] ?? '', [ 'lines', 'stripes', 'curves' ], true ) ? $settings['bg_pattern'] : 'lines';
			return $this->getSvgBackgroundStyle( $pattern );
		}
		return 'background-color: ' . esc_attr( sanitize_hex_color( $bg_color ) ?: '#8dd3d3' ) . ';';
	}

	/**
	 * @param array<string, mixed> $settings
	 */
	private function buildGradientCss( array $settings ): string {
		$type  = ( isset( $settings['bg_gradient_type'] ) && $settings['bg_gradient_type'] === 'radial' ) ? 'radial' : 'linear';
		$angle = isset( $settings['bg_gradient_angle'] ) ? max( 0, min( 360, (int) $settings['bg_gradient_angle'] ) ) : 135;
		$hex   = '/^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/';
		$c1    = isset( $settings['bg_gradient_color1'] ) && preg_match( $hex, (string) $settings['bg_gradient_color1'] ) ? (string) $settings['bg_gradient_color1'] : '#8dd3d3';
		$c2    = isset( $settings['bg_gradient_color2'] ) && preg_match( $hex, (string) $settings['bg_gradient_color2'] ) ? (string) $settings['bg_gradient_color2'] : '#e8e8e8';
		$p1    = isset( $settings['bg_gradient_pos1'] ) ? max( 0, min( 100, (int) $settings['bg_gradient_pos1'] ) ) : 0;
		$p2    = isset( $settings['bg_gradient_pos2'] ) ? max( 0, min( 100, (int) $settings['bg_gradient_pos2'] ) ) : 100;
		if ( $type === 'radial' ) {
			return 'radial-gradient(circle, ' . $c1 . ' ' . $p1 . '%, ' . $c2 . ' ' . $p2 . '%)';
		}
		return 'linear-gradient(' . $angle . 'deg, ' . $c1 . ' ' . $p1 . '%, ' . $c2 . ' ' . $p2 . '%)';
	}

	private function getSvgBackgroundStyle( string $pattern ): string {
		$svgs = [
			'lines'   => "url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='1920' height='1280' preserveAspectRatio='none' viewBox='0 0 1920 1280'%3e%3crect width='1920' height='1280' fill='%23f0f0f0'/%3e%3cpath d='M0 640 Q480 320 960 640 T1920 640' stroke='%23c5c5c5' stroke-width='2.5' fill='none'/%3e%3cpath d='M0 800 Q480 480 960 800 T1920 800' stroke='%23c5c5c5' stroke-width='2.5' fill='none'/%3e%3cpath d='M0 480 Q480 160 960 480 T1920 480' stroke='%23d0d0d0' stroke-width='1.5' fill='none'/%3e%3c/svg%3e\")",
			'stripes' => "url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='2560' height='2560' preserveAspectRatio='none' viewBox='0 0 2560 2560'%3e%3crect width='2560' height='2560' fill='%23e8e8e8'/%3e%3cpath d='M0 0L524 0L0 986z' fill='rgba(255,255,255,0.55)'/%3e%3cpath d='M0 986L524 0L684 0L0 1251z' fill='rgba(255,255,255,0.35)'/%3e%3cpath d='M2560 2560L1478 2560L2560 2129z' fill='rgba(0,0,0,0.06)'/%3e%3c/svg%3e\")",
			'curves'  => "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25' viewBox='0 0 1600 800'%3E%3Cpath fill='%23e5e5e5' d='M486 705.8c-109.3-21.8-223.4-32.2-335.3-19.4C99.5 692.1 49 703 0 719.8V800h843.8c-115.9-33.2-230.8-68.1-347.6-92.2C492.8 707.1 489.4 706.5 486 705.8z'/%3E%3Cpath fill='%23e8e8e8' d='M1600 0H0v719.8c49-16.8 99.5-27.8 150.7-33.5c111.9-12.7 226-2.4 335.3 19.4c3.4 0.7 6.8 1.4 10.2 2c116.8 24 231.7 59 347.6 92.2H1600V0z'/%3E%3Cpath fill='%23ebebeb' d='M478.4 581c3.2 0.8 6.4 1.7 9.5 2.5c196.2 52.5 388.7 133.5 593.5 176.6c174.2 36.6 349.5 29.2 518.6-10.2V0H0v574.9c52.3-17.6 106.5-27.7 161.1-30.9C268.4 537.4 375.7 554.2 478.4 581z'/%3E%3Cpath fill='%23efefef' d='M1600 0H0v136.3c62.3-20.9 127.7-27.5 192.2-19.2c93.6 12.1 180.5 47.7 263.3 89.6c2.6 1.3 5.1 2.6 7.7 3.9c158.4 81.1 319.7 170.9 500.3 223.2c210.5 61 430.8 49 636.6-16.6V0z'/%3E%3C/svg%3E\")",
		];
		$svg = $svgs[ $pattern ] ?? $svgs['stripes'];
		return 'background-image: ' . $svg . '; background-size: cover; background-position: center;';
	}

	private function sanitizeBgPosition( string $value ): string {
		$allowed = [ 'center center', 'top center', 'bottom center', 'center left', 'center right', 'top left', 'top right', 'bottom left', 'bottom right' ];
		return in_array( $value, $allowed, true ) ? $value : 'center center';
	}

	private function sanitizeBgSize( string $value ): string {
		$allowed = [ 'cover', 'contain', 'auto' ];
		return in_array( $value, $allowed, true ) ? $value : 'cover';
	}

	private function sanitizeBgRepeat( string $value ): string {
		$allowed = [ 'no-repeat', 'repeat', 'repeat-x', 'repeat-y' ];
		return in_array( $value, $allowed, true ) ? $value : 'no-repeat';
	}

	/**
	 * @param array<string, mixed> $settings
	 */
	private function cssColor( array $settings, string $key ): string {
		$raw = isset( $settings[ $key ] ) ? trim( (string) $settings[ $key ] ) : '';
		if ( $raw === '' ) {
			return '';
		}
		if ( preg_match( '/^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/', $raw ) ) {
			return $raw;
		}
		if ( preg_match( '/^rgba?\(/', $raw ) ) {
			return $raw;
		}

		return sanitize_hex_color( $raw ) ?: '';
	}

	/**
	 * @param array<string, mixed> $settings
	 */
	private function parseConfiguredLogoWidth( array $settings, string $key ): ?int {
		if ( ! isset( $settings[ $key ] ) || ! is_numeric( $settings[ $key ] ) || (int) $settings[ $key ] <= 0 ) {
			return null;
		}

		return max( 40, min( 600, (int) $settings[ $key ] ) );
	}

	/**
	 * @param array<string, mixed> $settings
	 * @return array{desktop: int, tablet: ?int, mobile: ?int}
	 */
	private function getLogoWidthSettings( array $settings ): array {
		$desktop = $this->parseConfiguredLogoWidth( $settings, 'logo_width_desktop' );
		if ( $desktop === null ) {
			$desktop = $this->parseConfiguredLogoWidth( $settings, 'logo_max_width' ) ?? 180;
		}

		return [
			'desktop' => $desktop,
			'tablet'  => $this->parseConfiguredLogoWidth( $settings, 'logo_width_tablet' ),
			'mobile'  => $this->parseConfiguredLogoWidth( $settings, 'logo_width_mobile' ),
		];
	}

	/**
	 * @param array<string, mixed> $settings
	 */
	private function buildDesignCss( array $settings, string $content_align, int $container_max_width, string $logo_align ): string {
		unset( $content_align, $container_max_width, $logo_align ); // Applied in base stylesheet.

		$imp   = ' !important';
		$rules = [];
		$text  = $this->cssColor( $settings, 'text_color' );
		if ( $text !== '' ) {
			$rules[] = 'body { color: ' . $text . $imp . '; }';
		}
		$heading = $this->cssColor( $settings, 'heading_color' );
		if ( $heading !== '' ) {
			$rules[] = '.wpps-csm-heading, .wpps-csm-heading * { color: ' . $heading . $imp . '; text-shadow: none' . $imp . '; }';
		}
		$description = $this->cssColor( $settings, 'description_color' );
		if ( $description !== '' ) {
			$rules[] = '.wpps-csm-description, .wpps-csm-description * { color: ' . $description . $imp . '; opacity: 1' . $imp . '; }';
		}

		$logo_radius = max( 0, min( 100, (int) ( $settings['logo_border_radius'] ?? 0 ) ) );
		if ( ! empty( $settings['logo_bg_enabled'] ) ) {
			$bg  = $this->cssColor( $settings, 'logo_bg_color' ) ?: '#ffffff';
			$pad = max( 0, min( 80, (int) ( $settings['logo_bg_padding'] ?? 16 ) ) );
			$rules[] = '.wpps-csm-logo-inner { background: ' . $bg . $imp . '; padding: ' . $pad . 'px' . $imp . '; border-radius: ' . $logo_radius . 'px' . $imp . '; }';
			$rules[] = '.wpps-csm-logo img { border-radius: ' . $logo_radius . 'px' . $imp . '; box-shadow: none' . $imp . '; }';
		} else {
			$rules[] = '.wpps-csm-logo-inner { background: transparent' . $imp . '; padding: 0' . $imp . '; border-radius: 0' . $imp . '; box-shadow: none' . $imp . '; }';
			$rules[] = '.wpps-csm-logo img { box-shadow: none' . $imp . '; border-radius: ' . $logo_radius . 'px' . $imp . '; }';
		}

		$logo_widths = $this->getLogoWidthSettings( $settings );
		$rules[]     = '.wpps-csm-logo img { max-width: ' . $logo_widths['desktop'] . 'px' . $imp . '; width: auto' . $imp . '; height: auto' . $imp . '; object-fit: contain' . $imp . '; }';
		if ( $logo_widths['tablet'] !== null ) {
			$rules[] = '@media (max-width: 1024px) { .wpps-csm-logo img { max-width: ' . $logo_widths['tablet'] . 'px' . $imp . '; } }';
		}
		if ( $logo_widths['mobile'] !== null ) {
			$rules[] = '@media (max-width: 600px) { .wpps-csm-logo img { max-width: ' . $logo_widths['mobile'] . 'px' . $imp . '; } }';
		}

		$cd_bg = $this->cssColor( $settings, 'countdown_bg_color' );
		if ( $cd_bg !== '' ) {
			$rules[] = '.wpps-csm-countdown-item { background: ' . $cd_bg . $imp . '; }';
		}
		$cd_text = $this->cssColor( $settings, 'countdown_text_color' );
		if ( $cd_text !== '' ) {
			$rules[] = '.wpps-csm-countdown-value, .wpps-csm-countdown-label { color: ' . $cd_text . $imp . '; }';
		}
		if ( ! empty( $settings['countdown_border_radius'] ) && (int) $settings['countdown_border_radius'] !== 12 ) {
			$rad = max( 0, min( 100, (int) $settings['countdown_border_radius'] ) );
			$rules[] = '.wpps-csm-countdown-item { border-radius: ' . $rad . 'px' . $imp . '; }';
		}

		$btn_bg = $this->cssColor( $settings, 'subscribe_btn_bg' );
		if ( $btn_bg !== '' ) {
			$rules[] = '.wpps-csm-subscribe button { background: ' . $btn_bg . $imp . '; }';
		}
		$btn_text = $this->cssColor( $settings, 'subscribe_btn_text' );
		if ( $btn_text !== '' ) {
			$rules[] = '.wpps-csm-subscribe button { color: ' . $btn_text . $imp . '; }';
		}
		$input_bg = $this->cssColor( $settings, 'subscribe_input_bg' );
		if ( $input_bg !== '' ) {
			$rules[] = '.wpps-csm-subscribe input[type="email"] { background: ' . $input_bg . $imp . '; }';
		}
		$input_text = $this->cssColor( $settings, 'subscribe_input_text' );
		if ( $input_text !== '' ) {
			$rules[] = '.wpps-csm-subscribe input[type="email"] { color: ' . $input_text . $imp . '; }';
		}
		$input_border = $this->cssColor( $settings, 'subscribe_input_border' );
		if ( $input_border !== '' ) {
			$rules[] = '.wpps-csm-subscribe input[type="email"] { border-color: ' . $input_border . $imp . '; }';
		}

		$social_bg = $this->cssColor( $settings, 'social_icon_bg' );
		if ( $social_bg !== '' ) {
			$rules[] = '.wpps-csm-social a { background: ' . $social_bg . $imp . '; }';
		}
		$social_color = $this->cssColor( $settings, 'social_icon_color' );
		if ( $social_color !== '' ) {
			$rules[] = '.wpps-csm-social a { color: ' . $social_color . $imp . '; }';
		}

		return implode( "\n", $rules );
	}

	/**
	 * @param array<string, mixed> $settings
	 * @return array<int, array{icon: string, url: string}>
	 */
	private function getSocialLinks( array $settings ): array {
		$links = $settings['social_links'] ?? [];
		if ( is_string( $links ) ) {
			$decoded = json_decode( $links, true );
			$links   = is_array( $decoded ) ? $decoded : [];
		}
		if ( ! empty( $links ) ) {
			$valid = [];
			foreach ( $links as $item ) {
				if ( ! is_array( $item ) ) {
					continue;
				}
				$url = isset( $item['url'] ) ? trim( (string) $item['url'] ) : '';
				if ( $url === '' || $url === '#' ) {
					continue;
				}
				$url = esc_url_raw( $url );
				if ( ! $url ) {
					continue;
				}
				$icon    = isset( $item['icon'] ) ? sanitize_key( (string) $item['icon'] ) : 'link';
				$valid[] = [ 'icon' => $icon, 'url' => $url ];
			}
			return $valid;
		}

		$old = [
			'facebook'  => $settings['social_facebook'] ?? '',
			'twitter'   => $settings['social_twitter'] ?? '',
			'instagram' => $settings['social_instagram'] ?? '',
			'linkedin'  => $settings['social_linkedin'] ?? '',
		];
		$migrated = [];
		foreach ( $old as $icon => $url ) {
			$url = trim( (string) $url );
			if ( $url !== '' && $url !== '#' ) {
				$url = esc_url_raw( $url );
				if ( $url ) {
					$migrated[] = [ 'icon' => $icon, 'url' => $url ];
				}
			}
		}
		return $migrated;
	}

	public static function getSocialIconFa( string $icon ): string {
		$classes = [
			'facebook'  => 'fa-brands fa-facebook-f',
			'twitter'   => 'fa-brands fa-x-twitter',
			'instagram' => 'fa-brands fa-instagram',
			'linkedin'  => 'fa-brands fa-linkedin-in',
			'youtube'   => 'fa-brands fa-youtube',
			'pinterest' => 'fa-brands fa-pinterest',
			'tiktok'    => 'fa-brands fa-tiktok',
			'github'    => 'fa-brands fa-github',
			'rss'       => 'fa-solid fa-rss',
			'link'      => 'fa-solid fa-link',
			'mastodon'  => 'fa-brands fa-mastodon',
			'threads'   => 'fa-brands fa-threads',
		];
		$fa_class = $classes[ $icon ] ?? $classes['link'];
		return '<i class="fa-fw ' . esc_attr( $fa_class ) . '" aria-hidden="true"></i>';
	}

	private function getLaunchTimestampMs( string $launch_date ): int {
		if ( ! $launch_date ) {
			return 0;
		}
		try {
			$dt = new \DateTime( $launch_date, wp_timezone() );
			return $dt->getTimestamp() * 1000;
		} catch ( \Exception $e ) {
			return 0;
		}
	}

	private function sanitizeCss( string $css ): string {
		$css = wp_strip_all_tags( $css );
		$css = preg_replace( '/url\s*\(\s*["\']?\s*javascript\s*:/i', 'url(#)', $css );
		$css = preg_replace( '/expression\s*\(/i', '/* blocked */ (', (string) $css );
		$css = preg_replace( '/-moz-binding\s*:/i', '/* blocked */:', (string) $css );
		$css = preg_replace( '/behavior\s*:/i', '/* blocked */:', (string) $css );
		$css = preg_replace( '/vbscript\s*:/i', '/* blocked */:', (string) $css );
		return is_string( $css ) ? $css : '';
	}

	private function getKsesHeadRules(): array {
		$allowed = wp_kses_allowed_html( 'post' );
		unset( $allowed['script'] );
		$allowed['style'] = [ 'type' => true ];
		$allowed['link']  = [ 'rel' => true, 'href' => true, 'type' => true, 'media' => true ];
		$allowed['meta']  = [
			'name'    => true,
			'content' => true,
			'property' => true,
		];
		return $allowed;
	}

	public function handleSubscription(): void {
		check_ajax_referer( self::NONCE_ACTION, 'nonce' );

		if ( ! $this->isSubscriptionAllowed() ) {
			wp_send_json_error( [ 'message' => __( 'Subscriptions are not available.', 'wp-powersuite' ) ] );
		}

		$ip       = $this->getClientIp();
		$rate_key = 'wpps_csm_sub_' . md5( $ip );
		$attempts = (int) get_transient( $rate_key );
		if ( $attempts >= 5 ) {
			wp_send_json_error( [ 'message' => __( 'Too many subscription attempts. Please try again later.', 'wp-powersuite' ) ] );
		}
		set_transient( $rate_key, $attempts + 1, HOUR_IN_SECONDS );

		$email = isset( $_POST['email'] ) ? sanitize_email( wp_unslash( $_POST['email'] ) ) : '';
		if ( ! is_email( $email ) ) {
			wp_send_json_error( [ 'message' => __( 'Please enter a valid email address.', 'wp-powersuite' ) ] );
		}

		$subscribers     = self::getSubscribersRaw();
		$max_subscribers = (int) apply_filters( 'wpps_csm_max_subscribers', 5000 );
		if ( $max_subscribers > 0 && count( $subscribers ) >= $max_subscribers ) {
			wp_send_json_error( [ 'message' => __( 'Subscriptions are temporarily unavailable. Please try again later.', 'wp-powersuite' ) ] );
		}

		if ( in_array( $email, array_column( $subscribers, 'email' ), true ) ) {
			wp_send_json_error( [ 'message' => __( "You're already subscribed!", 'wp-powersuite' ) ] );
		}

		$user_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '';

		$subscribers[] = [
			'email'         => $email,
			'subscribed_at' => gmdate( 'Y-m-d H:i:s' ),
			'ip'            => $ip,
			'user_agent'    => $user_agent,
		];
		update_option( self::SUBSCRIBERS_OPTION, $subscribers, false );

		wp_send_json_success( [ 'message' => __( "Thanks! We'll notify you when we launch.", 'wp-powersuite' ) ] );
	}

	public function addAdminBarNotice( \WP_Admin_Bar $wp_admin_bar ): void {
		if ( ! current_user_can( $this->adminCapability() ) ) {
			return;
		}
		$settings = $this->getSettings();
		if ( empty( $settings['enabled'] ) ) {
			return;
		}
		$mode  = $settings['mode'] ?? 'coming_soon';
		$label = $mode === 'coming_soon' ? __( 'Coming Soon: ON', 'wp-powersuite' ) : __( 'Maintenance: ON', 'wp-powersuite' );
		$icon  = $mode === 'coming_soon' ? 'dashicons-clock' : 'dashicons-hammer';
		$wp_admin_bar->add_node(
			[
				'id'     => 'wpps-csm-mode',
				'parent' => 'top-secondary',
				'title'  => '<span class="ab-icon dashicons ' . esc_attr( $icon ) . '"></span><span class="ab-label">' . esc_html( $label ) . '</span>',
				'href'   => admin_url( 'admin.php?page=powersuite-coming-soon-maintenance' ),
				'meta'   => [ 'title' => __( 'Open settings', 'wp-powersuite' ) ],
			]
		);
	}

	public function addAdminBarStyles(): void {
		if ( ! current_user_can( $this->adminCapability() ) ) {
			return;
		}
		$settings = $this->getSettings();
		if ( empty( $settings['enabled'] ) ) {
			return;
		}
		$mode     = $settings['mode'] ?? 'coming_soon';
		$bg       = $mode === 'coming_soon' ? '#e0f2fe' : '#fef3c7';
		$bg_hover = $mode === 'coming_soon' ? '#bae6fd' : '#fde68a';
		?>
		<style>
		#wpadminbar #wp-admin-bar-wpps-csm-mode .ab-item{display:flex!important;align-items:center!important;background:<?php echo esc_attr( $bg ); ?>!important}
		#wpadminbar #wp-admin-bar-wpps-csm-mode .ab-item .ab-icon{display:inline-flex!important;align-items:center!important;justify-content:center!important;line-height:1!important}
		#wpadminbar #wp-admin-bar-wpps-csm-mode .ab-item,
		#wpadminbar #wp-admin-bar-wpps-csm-mode .ab-item .ab-icon,
		#wpadminbar #wp-admin-bar-wpps-csm-mode .ab-item .ab-icon::before,
		#wpadminbar #wp-admin-bar-wpps-csm-mode .ab-item .ab-label{color:#1d2327!important;fill:#1d2327!important}
		#wpadminbar #wp-admin-bar-wpps-csm-mode:hover .ab-item,
		#wpadminbar #wp-admin-bar-wpps-csm-mode:hover .ab-item .ab-icon,
		#wpadminbar #wp-admin-bar-wpps-csm-mode:hover .ab-item .ab-icon::before,
		#wpadminbar #wp-admin-bar-wpps-csm-mode:hover .ab-item .ab-label{color:#1d2327!important;fill:#1d2327!important}
		#wpadminbar #wp-admin-bar-wpps-csm-mode:hover .ab-item{background:<?php echo esc_attr( $bg_hover ); ?>!important}
		</style>
		<?php
	}

	public function addAdminBarScript(): void {
		if ( ! current_user_can( $this->adminCapability() ) || ! is_admin_bar_showing() ) {
			return;
		}
		$settings = $this->getSettings();
		if ( empty( $settings['enabled'] ) ) {
			return;
		}
		?>
		<script type="text/javascript">
		(function() {
			var node = document.getElementById('wp-admin-bar-wpps-csm-mode');
			if (!node) return;
			var anchor = node.querySelector('a');
			if (!anchor) return;
			anchor.addEventListener('click', function(e) {
				e.preventDefault();
				window.location.href = <?php echo wp_json_encode( admin_url( 'admin.php?page=powersuite-coming-soon-maintenance' ) ); ?>;
			});
		})();
		</script>
		<?php
	}

	private function isLoginPage(): bool {
		$request_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
		return ( strpos( $request_uri, 'wp-login.php' ) !== false )
			|| ( strpos( $request_uri, 'wp-signup.php' ) !== false )
			|| ( strpos( $request_uri, 'wp-activate.php' ) !== false );
	}

	private function isRobotsTxt(): bool {
		$request_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
		return preg_match( '#/robots\.txt$#i', $request_uri ) === 1;
	}

	private function isValidInternalFetchRequest(): bool {
		if ( empty( $_GET['wpps_csm_fetch'] ) || empty( $_GET['wpps_csm_fetch_token'] ) ) {
			return false;
		}

		$page_url = $this->getCurrentPageUrlForFetch();
		$token    = sanitize_text_field( wp_unslash( (string) $_GET['wpps_csm_fetch_token'] ) );

		return hash_equals( $this->buildFetchToken( $page_url ), $token );
	}

	private function getCurrentPageUrlForFetch(): string {
		$request_uri = isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '/';
		$request_uri = is_string( $request_uri ) ? $request_uri : '/';
		$url         = home_url( $request_uri );
		return remove_query_arg( [ 'wpps_csm_fetch', 'wpps_csm_fetch_token' ], $url );
	}

	private function buildFetchToken( string $url ): string {
		return hash_hmac( 'sha256', untrailingslashit( $url ), $this->getFetchSecret() );
	}

	private function getFetchSecret(): string {
		$settings = $this->getSettings();
		$secret   = isset( $settings['fetch_secret'] ) ? (string) $settings['fetch_secret'] : '';
		if ( $secret !== '' ) {
			return $secret;
		}
		$secret                   = wp_generate_password( 32, false );
		$settings['fetch_secret'] = $secret;
		$this->saveSettings( $settings );
		return $secret;
	}

	private function verifyBypassKey( string $key ): bool {
		if ( $key === '' ) {
			return false;
		}

		// Only the random admin-issued bypass_token is valid. A previous check used
		// wp_check_password( site_url(), $key ), which accepted any WordPress password
		// hash of the public site URL — forgeable without site secrets.
		$token = $this->getBypassToken();
		if ( $token === '' ) {
			return false;
		}

		return hash_equals( $token, $key );
	}

	private function getBypassToken(): string {
		$settings = $this->getSettings();
		$token    = isset( $settings['bypass_token'] ) ? (string) $settings['bypass_token'] : '';
		if ( $token !== '' ) {
			return $token;
		}
		$token                    = wp_generate_password( 32, false );
		$settings['bypass_token'] = $token;
		$this->saveSettings( $settings );
		return $token;
	}

	private function setBypassCookie(): void {
		$hash = hash_hmac( 'sha256', $this->getBypassToken(), wp_salt( 'wpps_csm_bypass' ) );
		$path = defined( 'COOKIEPATH' ) ? COOKIEPATH : '/';
		$domain = defined( 'COOKIE_DOMAIN' ) ? COOKIE_DOMAIN : '';
		setcookie( self::BYPASS_COOKIE, $hash, time() + ( 2 * DAY_IN_SECONDS ), $path, $domain, is_ssl(), true );
		$_COOKIE[ self::BYPASS_COOKIE ] = $hash;
	}

	private function hasBypassCookie(): bool {
		if ( empty( $_COOKIE[ self::BYPASS_COOKIE ] ) ) {
			return false;
		}
		$cookie   = sanitize_text_field( wp_unslash( (string) $_COOKIE[ self::BYPASS_COOKIE ] ) );
		$expected = hash_hmac( 'sha256', $this->getBypassToken(), wp_salt( 'wpps_csm_bypass' ) );
		return hash_equals( $expected, $cookie );
	}

	private function isExcludedUrl(): bool {
		$settings    = $this->getSettings();
		$request_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
		$path        = strtok( $request_uri, '?' ) ?: $request_uri;

		$lines = [];
		if ( ! empty( $settings['exclude_woocommerce'] ) && class_exists( 'WooCommerce' ) ) {
			$lines = array_merge( $lines, [ '/cart', '/checkout', '/my-account' ] );
		}
		$excluded = trim( (string) ( $settings['exclude_urls'] ?? '' ) );
		if ( $excluded !== '' ) {
			$lines = array_merge( $lines, array_filter( array_map( 'trim', explode( "\n", $excluded ) ) ) );
		}

		foreach ( $lines as $line ) {
			if ( $line === '' ) {
				continue;
			}
			$line = '/' . trim( $line, '/' );
			if ( $path === $line || str_starts_with( $path, $line . '/' ) || str_starts_with( $path, $line ) ) {
				return true;
			}
		}
		return false;
	}

	private function isUserAllowedAccess(): bool {
		if ( ! is_user_logged_in() ) {
			return false;
		}
		$user = wp_get_current_user();
		if ( ! $user->exists() ) {
			return false;
		}

		$cap = $this->adminCapability();
		if ( current_user_can( $cap ) ) {
			return true;
		}

		$settings = $this->getSettings();
		$roles    = $settings['allowed_roles'] ?? [];
		$user_ids = $settings['allowed_user_ids'] ?? [];
		if ( ! is_array( $roles ) ) {
			$roles = [];
		}
		if ( ! is_array( $user_ids ) ) {
			$user_ids = [];
		}

		if ( in_array( (string) $user->ID, array_map( 'strval', $user_ids ), true ) ) {
			return true;
		}
		foreach ( (array) $user->roles as $role ) {
			if ( isset( $roles[ $role ] ) && $roles[ $role ] ) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Client IP for rate limits. Uses REMOTE_ADDR by default; X-Forwarded-For
	 * is honored only when REMOTE_ADDR is in wpps_csm_trusted_proxy_ips.
	 */
	private function getClientIp(): string {
		$remote = isset( $_SERVER['REMOTE_ADDR'] ) ? trim( (string) wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
		if ( ! filter_var( $remote, FILTER_VALIDATE_IP ) ) {
			return '';
		}

		$ip = $remote;
		if ( $this->isTrustedProxyIp( $remote ) && ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
			$parts  = explode( ',', (string) wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) );
			$client = trim( (string) ( $parts[0] ?? '' ) );
			if ( filter_var( $client, FILTER_VALIDATE_IP ) ) {
				$ip = $client;
			}
		}

		/**
		 * @param string $ip Resolved client IP.
		 */
		$ip = (string) apply_filters( 'wpps_csm_client_ip', $ip );

		return filter_var( $ip, FILTER_VALIDATE_IP ) ? $ip : '';
	}

	private function isTrustedProxyIp( string $remote_addr ): bool {
		/**
		 * @param list<string> $proxies Default empty (REMOTE_ADDR only).
		 */
		$proxies = apply_filters( 'wpps_csm_trusted_proxy_ips', [] );
		if ( ! is_array( $proxies ) ) {
			return false;
		}

		foreach ( $proxies as $trusted ) {
			if ( is_string( $trusted ) && $trusted !== '' && hash_equals( trim( $trusted ), $remote_addr ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * @return array<string, mixed>
	 */
	public static function getDefaultSettings(): array {
		return [
			'enabled'                    => 0,
			'use_existing_page'          => 0,
			'existing_page_url'          => '',
			'mode'                       => 'coming_soon',
			'page_preset'                => 'default',
			'page_title'                 => '',
			'show_logo'                  => 0,
			'logo_id'                    => 0,
			'logo_url'                   => '',
			'logo_source'                => 'upload',
			'logo_align'                 => 'center',
			'logo_max_width'             => 180,
			'logo_width_desktop'         => '',
			'logo_width_tablet'          => '',
			'logo_width_mobile'          => '',
			'logo_bg_enabled'            => 0,
			'logo_bg_color'              => '#ffffff',
			'logo_bg_padding'            => 16,
			'logo_border_radius'         => 0,
			'text_color'                 => '',
			'heading_color'              => '',
			'description_color'          => '',
			'content_align'              => 'center',
			'container_max_width'        => 800,
			'countdown_bg_color'         => '',
			'countdown_text_color'       => '',
			'countdown_border_radius'    => 12,
			'subscribe_btn_bg'           => '',
			'subscribe_btn_text'         => '',
			'subscribe_input_bg'         => '',
			'subscribe_input_text'       => '',
			'subscribe_input_border'     => '',
			'social_icon_bg'             => '',
			'social_icon_color'          => '',
			'bg_image_position'          => 'center center',
			'bg_image_size'              => 'cover',
			'bg_image_repeat'            => 'no-repeat',
			'heading'                    => __( 'Something Amazing is Coming', 'wp-powersuite' ),
			'description'                => __( 'We are working hard to bring you something special. Stay tuned for updates!', 'wp-powersuite' ),
			'launch_date'                => '',
			'auto_disable'               => 1,
			'show_subscribe'             => 1,
			'subscribe_consent_text'     => __( 'We will only use your email to notify you when the site launches.', 'wp-powersuite' ),
			'bg_type'                    => 'color',
			'bg_color'                   => '#8dd3d3',
			'bg_image_id'                => 0,
			'bg_image_url'               => '',
			'bg_image_overlay_color'     => '#000000',
			'bg_image_overlay_opacity'   => 60,
			'bg_pattern'                 => 'lines',
			'bg_pattern_overlay_color'   => '#000000',
			'bg_pattern_overlay_opacity' => 60,
			'bg_gradient_type'           => 'linear',
			'bg_gradient_angle'          => 135,
			'bg_gradient_color1'         => '#8dd3d3',
			'bg_gradient_color2'         => '#e8e8e8',
			'bg_gradient_pos1'           => 0,
			'bg_gradient_pos2'           => 100,
			'bg_video'                   => '',
			'bg_video_overlay_color'     => '#000000',
			'bg_video_overlay_opacity'   => 60,
			'social_links'               => [],
			'custom_head'                => '',
			'custom_css'                 => '',
			'exclude_urls'               => '',
			'exclude_woocommerce'        => 1,
			'allowed_roles'              => [],
			'allowed_user_ids'           => [],
			'bypass_token'               => '',
			'fetch_secret'               => '',
		];
	}

	/**
	 * @param array<string, mixed> $settings
	 */
	private static function resolveBackgroundImageUrl( array $settings ): string {
		$bg_image_id = (int) ( $settings['bg_image_id'] ?? 0 );
		if ( $bg_image_id > 0 ) {
			$url = wp_get_attachment_image_url( $bg_image_id, 'full' );
			if ( is_string( $url ) && $url !== '' ) {
				return $url;
			}
		}

		$external = isset( $settings['bg_image_url'] ) ? esc_url_raw( trim( (string) $settings['bg_image_url'] ) ) : '';

		return $external;
	}

	private function getSettings(): array {
		if ( $this->settings_cache !== null ) {
			return $this->settings_cache;
		}
		$raw = $this->moduleSettings()->all( [] );
		$this->settings_cache = is_array( $raw ) ? array_merge( self::getDefaultSettings(), $raw ) : self::getDefaultSettings();
		return $this->settings_cache;
	}

	/**
	 * @param array<string, mixed> $settings
	 */
	private function saveSettings( array $settings ): void {
		$this->moduleSettings()->save( $settings );
		$this->settings_cache = $settings;
	}

	public function getDataLocations(): array {
		return [
			'option_store_keys' => [ self::MODULE_ID ],
			'options'           => [ self::SUBSCRIBERS_OPTION ],
		];
	}

	public function enqueueAdminAssets( string $hook_suffix ): void {
		if ( strpos( $hook_suffix, 'powersuite-coming-soon-maintenance' ) === false ) {
			return;
		}

		wp_enqueue_media();
		wp_enqueue_editor();

		wp_enqueue_style(
			'wpps-csm-admin',
			plugins_url( 'assets/admin.css', __FILE__ ),
			[ 'wp-powersuite-admin' ],
			self::MODULE_VERSION
		);
		wp_enqueue_style(
			'font-awesome-6',
			'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css',
			[],
			'6.5.1'
		);

		$presets_file = __DIR__ . '/page-presets.php';
		if ( is_readable( $presets_file ) ) {
			require_once $presets_file;
			wp_enqueue_script(
				'wpps-csm-page-presets',
				plugins_url( 'assets/page-presets.js', __FILE__ ),
				[ 'jquery', 'wp-powersuite-admin' ],
				self::MODULE_VERSION,
				true
			);
			wp_localize_script(
				'wpps-csm-page-presets',
				'wppsCsmPagePresets',
				PagePresets::getPresetsForJs()
			);
			wp_add_inline_script(
				'wpps-csm-page-presets',
				'window.wppsCsmModuleId = ' . wp_json_encode( self::MODULE_ID ) . ';' .
				'window.wppsCsmPresetAppliedText = ' . wp_json_encode( __( 'Template applied. Your logo and enabled status are unchanged — click Save Settings to keep changes.', 'wp-powersuite' ) ) . ';' .
				'window.wppsCsmNoImageText = ' . wp_json_encode( __( 'No image', 'wp-powersuite' ) ) . ';',
				'before'
			);
		}
	}

	public function addAdminMenu(): void {
		add_submenu_page(
			'powersuite',
			__( 'Coming Soon & Maintenance', 'wp-powersuite' ),
			__( 'Coming Soon & Maintenance', 'wp-powersuite' ),
			$this->adminCapability(),
			'powersuite-coming-soon-maintenance',
			[ $this, 'renderComingSoonPage' ]
		);
	}

	public function renderComingSoonPage(): void {
		if ( ! current_user_can( $this->adminCapability() ) ) {
			wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'wp-powersuite' ) );
		}
		if ( ! $this->isLicenseActive() ) {
			?>
			<div class="wrap">
				<h1><?php esc_html_e( 'Coming Soon & Maintenance', 'wp-powersuite' ); ?></h1>
				<div class="notice notice-warning inline" style="margin:0;">
					<p>
						<?php esc_html_e( 'Coming Soon & Maintenance requires an active license. Activate your license to publish landing pages and maintenance modes.', 'wp-powersuite' ); ?>
						<a href="<?php echo esc_url( admin_url( 'admin.php?page=powersuite-license' ) ); ?>"><?php esc_html_e( 'Activate license', 'wp-powersuite' ); ?></a>
					</p>
				</div>
			</div>
			<?php
			return;
		}
		$module_id = self::MODULE_ID;
		include __DIR__ . '/admin.php';
	}

	/**
	 * @return array<int, array{email: string, subscribed_at: string|null, ip: string|null, user_agent: string|null}>
	 */
	private static function getSubscribersRaw(): array {
		$subs = get_option( self::SUBSCRIBERS_OPTION, [] );
		if ( ! is_array( $subs ) ) {
			return [];
		}
		$out = [];
		foreach ( $subs as $item ) {
			if ( is_string( $item ) ) {
				$out[] = [
					'email'         => $item,
					'subscribed_at' => null,
					'ip'            => null,
					'user_agent'    => null,
				];
			} elseif ( is_array( $item ) && isset( $item['email'] ) ) {
				$out[] = [
					'email'         => (string) $item['email'],
					'subscribed_at' => isset( $item['subscribed_at'] ) ? (string) $item['subscribed_at'] : null,
					'ip'            => isset( $item['ip'] ) ? (string) $item['ip'] : null,
					'user_agent'    => isset( $item['user_agent'] ) ? (string) $item['user_agent'] : null,
				];
			}
		}
		return $out;
	}

	/**
	 * @return array<int, array{email: string, subscribed_at: string|null, ip: string|null, user_agent: string|null}>
	 */
	public static function getSubscribersWithMeta(): array {
		return self::getSubscribersRaw();
	}

	/**
	 * Public bypass URL for admin display.
	 */
	public static function getBypassUrl(): string {
		$repo     = OptionStoreModuleSettingsRepository::forModule( self::MODULE_ID );
		$settings = $repo->all( [] );
		if ( ! is_array( $settings ) ) {
			$settings = [];
		}
		$token = ! empty( $settings['bypass_token'] ) ? (string) $settings['bypass_token'] : '';
		if ( $token === '' ) {
			$token                      = wp_generate_password( 32, false );
			$settings['bypass_token']   = $token;
			$repo->save( $settings );
		}
		return add_query_arg( 'bypass', $token, home_url( '/' ) );
	}
}
