<?php

defined( 'ABSPATH' ) || exit;

/**
 * Maintenance Mode module.
 *
 * Replaces Kadence Pro's threadbare maintenance page and the dozens of
 * "Coming Soon" plugins (SeedProd et al). Three presets:
 *
 *  - coming_soon       (HTTP 200, "We're getting ready") — pre-launch.
 *  - under_construction (HTTP 200, "Site is being updated") — partial work.
 *  - maintenance       (HTTP 503 + Retry-After, "Back shortly") — downtime.
 *
 * Bypass paths so admins don't lock themselves out:
 *  - is_admin() / wp_doing_ajax() / wp_doing_cron() requests pass through.
 *  - wp-login.php is always reachable (admins can sign in).
 *  - Users with `manage_options` bypass automatically.
 *  - Optional extra roles can be configured to bypass.
 *  - Optional bypass token URL ?bw_preview=TOKEN — visiting the URL sets a
 *    session cookie so the visitor sees the live site for the rest of their
 *    browser session. The token is auto-generated on first save when the
 *    mode becomes active.
 *
 * Output is a minimal self-contained HTML page (no theme load) with
 * `<meta name="robots" content="noindex,nofollow">` so search engines don't
 * cache the splash. Optional logo image, configurable heading, message, and
 * three colours (background, text, accent).
 *
 * @package BW_Dev
 */

class BW_Dev_Module_Maintenance_Mode implements BW_Dev_Module_Interface {

	const COOKIE_NAME    = 'bw_dev_maintenance_bypass';
	const QUERY_PARAM    = 'bw_preview';
	const ALLOWED_MODES  = array( 'off', 'coming_soon', 'under_construction', 'maintenance' );
	const COLOR_REGEX    = '/^#[0-9a-fA-F]{3,8}$/';

	public function slug(): string {
		return 'maintenance_mode';
	}

	public function label(): string {
		return __( 'Maintenance Mode', 'bw-dev' );
	}

	public function group(): string {
		return 'frontend';
	}

	public function default_settings(): array {
		return array(
			'mode'              => 'off',
			'heading'           => '',
			'message'           => '',
			'bg_color'          => '#f6f7f7',
			'text_color'        => '#1d2327',
			'accent_color'      => '#2271b1',
			'logo_url'          => '',
			'bypass_roles'      => array(),
			'bypass_token'      => '',
			'retry_after_hours' => 1,
		);
	}

	public function sanitize( array $data ): array {
		$mode = isset( $data['mode'] ) ? sanitize_key( (string) $data['mode'] ) : 'off';
		if ( ! in_array( $mode, self::ALLOWED_MODES, true ) ) {
			$mode = 'off';
		}

		$bypass_roles_raw = isset( $data['bypass_roles'] ) && is_array( $data['bypass_roles'] ) ? $data['bypass_roles'] : array();
		$bypass_roles     = array();
		foreach ( $bypass_roles_raw as $role_slug ) {
			$role_slug = sanitize_key( (string) $role_slug );
			if ( '' !== $role_slug ) {
				$bypass_roles[] = $role_slug;
			}
		}

		$token = isset( $data['bypass_token'] ) ? (string) $data['bypass_token'] : '';
		$token = preg_replace( '/[^a-zA-Z0-9]/', '', $token );
		// Auto-generate the first time the mode becomes active.
		if ( 'off' !== $mode && '' === $token ) {
			$token = wp_generate_password( 32, false, false );
		}

		$retry_hours = isset( $data['retry_after_hours'] ) ? (int) $data['retry_after_hours'] : 1;
		$retry_hours = max( 1, min( 168, $retry_hours ) );

		return array(
			'mode'              => $mode,
			'heading'           => isset( $data['heading'] ) ? sanitize_text_field( wp_unslash( (string) $data['heading'] ) ) : '',
			'message'           => isset( $data['message'] ) ? wp_kses_post( wp_unslash( (string) $data['message'] ) ) : '',
			'bg_color'          => $this->sanitize_hex( $data['bg_color'] ?? '' ),
			'text_color'        => $this->sanitize_hex( $data['text_color'] ?? '' ),
			'accent_color'      => $this->sanitize_hex( $data['accent_color'] ?? '' ),
			'logo_url'          => isset( $data['logo_url'] ) ? esc_url_raw( wp_unslash( (string) $data['logo_url'] ) ) : '',
			'bypass_roles'      => array_values( array_unique( $bypass_roles ) ),
			'bypass_token'      => $token,
			'retry_after_hours' => $retry_hours,
		);
	}

	private function sanitize_hex( $value ): string {
		$value = strtolower( trim( (string) $value ) );
		if ( '' === $value ) {
			return '';
		}
		return preg_match( self::COLOR_REGEX, $value ) ? $value : '';
	}

	public function register(): void {
		add_action( 'init',              array( $this, 'maybe_set_bypass_cookie' ), 1 );
		add_action( 'template_redirect', array( $this, 'maybe_render' ), 1 );
		add_action( 'admin_enqueue_scripts', array( $this, 'maybe_enqueue_media' ) );
	}

	public function maybe_enqueue_media( $hook ): void {
		if ( 'settings_page_' . BW_Dev_Admin_Page::MENU_SLUG !== $hook ) {
			return;
		}
		$query = wp_unslash( $_GET ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$tab   = isset( $query['tab'] ) ? sanitize_key( (string) $query['tab'] ) : 'modules';
		if ( $this->slug() !== $tab ) {
			return;
		}
		wp_enqueue_media();
	}

	public function maybe_set_bypass_cookie(): void {
		$query = wp_unslash( $_GET ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$raw   = isset( $query[ self::QUERY_PARAM ] ) ? (string) $query[ self::QUERY_PARAM ] : '';
		if ( '' === $raw ) {
			return;
		}
		$token = preg_replace( '/[^a-zA-Z0-9]/', '', $raw );
		if ( '' === $token ) {
			return;
		}
		$stored = (string) bw_dev()->settings()->get( $this->slug(), 'bypass_token', '' );
		if ( '' === $stored || ! hash_equals( $stored, $token ) ) {
			return;
		}
		// Session cookie — expires when browser closes. Shared preview links stay short-lived.
		setcookie( self::COOKIE_NAME, $token, 0, COOKIEPATH ?: '/', COOKIE_DOMAIN ?: '', is_ssl(), true );
	}

	public function maybe_render(): void {
		$mode = (string) bw_dev()->settings()->get( $this->slug(), 'mode', 'off' );
		if ( 'off' === $mode || ! in_array( $mode, self::ALLOWED_MODES, true ) ) {
			return;
		}
		if ( $this->user_can_bypass() ) {
			return;
		}
		$this->render_template( $mode );
	}

	private function user_can_bypass(): bool {
		// Admin / AJAX / cron contexts always pass through.
		if ( is_admin() || wp_doing_ajax() || wp_doing_cron() ) {
			return true;
		}
		// wp-login.php must remain reachable.
		if ( isset( $GLOBALS['pagenow'] ) && 'wp-login.php' === $GLOBALS['pagenow'] ) {
			return true;
		}
		// Token cookie set by ?bw_preview=TOKEN.
		if ( $this->has_valid_bypass_cookie() ) {
			return true;
		}
		// Admin-level capability — typical Bowden Works "agency dev still working" path.
		if ( is_user_logged_in() && current_user_can( 'manage_options' ) ) {
			return true;
		}
		// Extra roles configured to bypass.
		if ( is_user_logged_in() && $this->user_in_bypass_roles() ) {
			return true;
		}
		return false;
	}

	private function has_valid_bypass_cookie(): bool {
		$cookies = wp_unslash( $_COOKIE );
		$raw     = isset( $cookies[ self::COOKIE_NAME ] ) ? (string) $cookies[ self::COOKIE_NAME ] : '';
		if ( '' === $raw ) {
			return false;
		}
		$cookie = preg_replace( '/[^a-zA-Z0-9]/', '', $raw );
		$stored = (string) bw_dev()->settings()->get( $this->slug(), 'bypass_token', '' );
		return '' !== $stored && '' !== $cookie && hash_equals( $stored, $cookie );
	}

	private function user_in_bypass_roles(): bool {
		$allowed = bw_dev()->settings()->get( $this->slug(), 'bypass_roles', array() );
		if ( ! is_array( $allowed ) || empty( $allowed ) ) {
			return false;
		}
		$user = wp_get_current_user();
		if ( ! ( $user instanceof WP_User ) || 0 === (int) $user->ID ) {
			return false;
		}
		foreach ( (array) $user->roles as $role ) {
			if ( in_array( $role, $allowed, true ) ) {
				return true;
			}
		}
		return false;
	}

	private function preset_defaults(): array {
		return array(
			'coming_soon'        => array(
				'heading' => __( 'Coming Soon', 'bw-dev' ),
				'message' => __( 'We\'re putting the finishing touches on the site. Check back soon.', 'bw-dev' ),
				'status'  => 200,
			),
			'under_construction' => array(
				'heading' => __( 'Under Construction', 'bw-dev' ),
				'message' => __( 'The site is being updated. Some pages may be temporarily unavailable.', 'bw-dev' ),
				'status'  => 200,
			),
			'maintenance'        => array(
				'heading' => __( 'Back Shortly', 'bw-dev' ),
				'message' => __( 'We\'re performing scheduled maintenance. The site will be back online shortly.', 'bw-dev' ),
				'status'  => 503,
			),
		);
	}

	private function render_template( string $mode ): void {
		$presets = $this->preset_defaults();
		$preset  = $presets[ $mode ];
		$s       = bw_dev()->settings()->get( $this->slug() );
		if ( ! is_array( $s ) ) {
			$s = array();
		}

		$heading = '' !== trim( (string) ( $s['heading'] ?? '' ) ) ? (string) $s['heading'] : $preset['heading'];
		$message = '' !== trim( (string) ( $s['message'] ?? '' ) ) ? (string) $s['message'] : $preset['message'];
		$bg      = $this->sanitize_hex( $s['bg_color']     ?? '' ) ?: '#f6f7f7';
		$text    = $this->sanitize_hex( $s['text_color']   ?? '' ) ?: '#1d2327';
		$accent  = $this->sanitize_hex( $s['accent_color'] ?? '' ) ?: '#2271b1';
		$logo    = isset( $s['logo_url'] ) ? (string) $s['logo_url'] : '';
		$status  = (int) $preset['status'];

		nocache_headers();
		status_header( $status );
		if ( 503 === $status ) {
			$retry = max( 1, (int) ( $s['retry_after_hours'] ?? 1 ) ) * 3600;
			header( 'Retry-After: ' . $retry );
		}
		header( 'Content-Type: text/html; charset=UTF-8' );

		$site_name   = get_bloginfo( 'name' );
		$title       = $heading . ' — ' . $site_name;
		$logo_markup = '' !== $logo
			? '<img src="' . esc_url( $logo ) . '" alt="" />'
			: '';

		echo '<!DOCTYPE html>' . "\n";
		?>
<html lang="<?php echo esc_attr( get_bloginfo( 'language' ) ); ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="robots" content="noindex,nofollow">
<title><?php echo esc_html( $title ); ?></title>
<style>
html,body{margin:0;padding:0;min-height:100%;}
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;background:<?php echo esc_html( $bg ); ?>;color:<?php echo esc_html( $text ); ?>;display:flex;align-items:center;justify-content:center;padding:40px 20px;min-height:100vh;box-sizing:border-box;}
.bw-dev-maintenance{max-width:560px;text-align:center;line-height:1.6;}
.bw-dev-maintenance img{max-width:240px;max-height:120px;margin-bottom:30px;display:inline-block;}
.bw-dev-maintenance h1{font-size:2.4rem;margin:0 0 20px;color:<?php echo esc_html( $accent ); ?>;font-weight:600;}
.bw-dev-maintenance p{font-size:1.05rem;margin:0 0 14px;}
@media (max-width:480px){.bw-dev-maintenance h1{font-size:1.8rem;}}
</style>
</head>
<body>
<div class="bw-dev-maintenance">
<?php echo wp_kses( $logo_markup, array( 'img' => array( 'src' => true, 'alt' => true ) ) ); ?>
<h1><?php echo esc_html( $heading ); ?></h1>
<?php echo wp_kses_post( wpautop( $message ) ); ?>
</div>
</body>
</html>
		<?php
		exit;
	}

	public function render_tab(): void {
		$s = bw_dev()->settings()->get( $this->slug() );
		if ( ! is_array( $s ) ) {
			$s = $this->default_settings();
		}

		$mode           = (string) ( $s['mode'] ?? 'off' );
		$token          = (string) ( $s['bypass_token'] ?? '' );
		$bypass_roles   = isset( $s['bypass_roles'] ) && is_array( $s['bypass_roles'] ) ? array_flip( $s['bypass_roles'] ) : array();
		$all_roles      = wp_roles()->roles;
		$base           = BW_Dev_Settings::OPTION . '[' . $this->slug() . ']';
		$is_active      = 'off' !== $mode && in_array( $mode, self::ALLOWED_MODES, true );
		$preview_url    = $is_active && '' !== $token ? home_url( '/?' . self::QUERY_PARAM . '=' . rawurlencode( $token ) ) : '';

		$mode_options = array(
			'off'                => __( 'Off — site is live', 'bw-dev' ),
			'coming_soon'        => __( 'Coming Soon (HTTP 200, pre-launch)', 'bw-dev' ),
			'under_construction' => __( 'Under Construction (HTTP 200, partial updates in progress)', 'bw-dev' ),
			'maintenance'        => __( 'Maintenance (HTTP 503, scheduled downtime — search engines + uptime monitors retry)', 'bw-dev' ),
		);
		?>
		<p class="description">
			<?php esc_html_e( 'Replace the front-end with a branded splash while you work. Admins and configured roles always see the live site.', 'bw-dev' ); ?>
		</p>

		<?php if ( $is_active ) : ?>
			<div style="background:#fff;border-left:4px solid #d63638;padding:12px 16px;margin:14px 0;max-width:720px;">
				<strong><?php esc_html_e( 'The front-end is hidden right now', 'bw-dev' ); ?></strong>
				<p style="margin:6px 0 0;">
					<?php
					printf(
						/* translators: %s: mode label */
						esc_html__( 'Mode: %s. Logged-out visitors see the splash page; logged-in admins (and any roles configured below) see the live site.', 'bw-dev' ),
						'<code>' . esc_html( $mode ) . '</code>'
					);
					?>
				</p>
			</div>
		<?php endif; ?>

		<h3><?php esc_html_e( 'Mode', 'bw-dev' ); ?></h3>
		<table class="form-table" role="presentation">
			<tbody>
				<tr>
					<th scope="row"><label for="bw-dev-maintenance-mode"><?php esc_html_e( 'Current state', 'bw-dev' ); ?></label></th>
					<td>
						<select id="bw-dev-maintenance-mode" name="<?php echo esc_attr( $base . '[mode]' ); ?>">
							<?php foreach ( $mode_options as $value => $label ) : ?>
								<option value="<?php echo esc_attr( $value ); ?>" <?php selected( $mode, $value ); ?>><?php echo esc_html( $label ); ?></option>
							<?php endforeach; ?>
						</select>
					</td>
				</tr>
				<tr>
					<th scope="row"><label for="bw-dev-maintenance-retry"><?php esc_html_e( 'Retry-After (hours)', 'bw-dev' ); ?></label></th>
					<td>
						<input type="number" id="bw-dev-maintenance-retry" name="<?php echo esc_attr( $base . '[retry_after_hours]' ); ?>" value="<?php echo esc_attr( (string) (int) ( $s['retry_after_hours'] ?? 1 ) ); ?>" min="1" max="168" step="1" class="small-text" />
						<p class="description"><?php esc_html_e( 'Only used in Maintenance mode — tells search engines and uptime monitors when to come back. 1–168 hours.', 'bw-dev' ); ?></p>
					</td>
				</tr>
			</tbody>
		</table>

		<h3><?php esc_html_e( 'Content', 'bw-dev' ); ?></h3>
		<p class="description"><?php esc_html_e( 'Leave fields blank to use the defaults for the selected mode.', 'bw-dev' ); ?></p>
		<table class="form-table" role="presentation">
			<tbody>
				<tr>
					<th scope="row"><label for="bw-dev-maintenance-heading"><?php esc_html_e( 'Heading', 'bw-dev' ); ?></label></th>
					<td><input type="text" id="bw-dev-maintenance-heading" name="<?php echo esc_attr( $base . '[heading]' ); ?>" value="<?php echo esc_attr( (string) ( $s['heading'] ?? '' ) ); ?>" class="regular-text" /></td>
				</tr>
				<tr>
					<th scope="row"><label for="bw-dev-maintenance-message"><?php esc_html_e( 'Message', 'bw-dev' ); ?></label></th>
					<td>
						<textarea id="bw-dev-maintenance-message" name="<?php echo esc_attr( $base . '[message]' ); ?>" rows="3" class="large-text"><?php echo esc_textarea( (string) ( $s['message'] ?? '' ) ); ?></textarea>
						<p class="description"><?php esc_html_e( 'Plain text or simple HTML (sanitised via wp_kses_post).', 'bw-dev' ); ?></p>
					</td>
				</tr>
				<tr>
					<th scope="row"><label for="bw-dev-maintenance-logo"><?php esc_html_e( 'Logo image', 'bw-dev' ); ?></label></th>
					<td>
						<div style="display:flex;align-items:center;gap:10px;">
							<input type="text" id="bw-dev-maintenance-logo" name="<?php echo esc_attr( $base . '[logo_url]' ); ?>" value="<?php echo esc_attr( (string) ( $s['logo_url'] ?? '' ) ); ?>" class="regular-text" placeholder="https://..." />
							<button type="button" id="bw-dev-maintenance-logo-upload" class="button button-secondary"><?php esc_html_e( 'Upload / Select', 'bw-dev' ); ?></button>
							<button type="button" id="bw-dev-maintenance-logo-clear" class="button button-link-delete"><?php esc_html_e( 'Clear', 'bw-dev' ); ?></button>
						</div>
					</td>
				</tr>
			</tbody>
		</table>

		<h3><?php esc_html_e( 'Colours', 'bw-dev' ); ?></h3>
		<table class="form-table" role="presentation">
			<tbody>
				<tr>
					<th scope="row"><label for="bw-dev-maintenance-bg"><?php esc_html_e( 'Background', 'bw-dev' ); ?></label></th>
					<td><input type="text" id="bw-dev-maintenance-bg" name="<?php echo esc_attr( $base . '[bg_color]' ); ?>" value="<?php echo esc_attr( (string) ( $s['bg_color'] ?? '' ) ); ?>" class="regular-text code" placeholder="#f6f7f7" /></td>
				</tr>
				<tr>
					<th scope="row"><label for="bw-dev-maintenance-text"><?php esc_html_e( 'Body text', 'bw-dev' ); ?></label></th>
					<td><input type="text" id="bw-dev-maintenance-text" name="<?php echo esc_attr( $base . '[text_color]' ); ?>" value="<?php echo esc_attr( (string) ( $s['text_color'] ?? '' ) ); ?>" class="regular-text code" placeholder="#1d2327" /></td>
				</tr>
				<tr>
					<th scope="row"><label for="bw-dev-maintenance-accent"><?php esc_html_e( 'Heading accent', 'bw-dev' ); ?></label></th>
					<td><input type="text" id="bw-dev-maintenance-accent" name="<?php echo esc_attr( $base . '[accent_color]' ); ?>" value="<?php echo esc_attr( (string) ( $s['accent_color'] ?? '' ) ); ?>" class="regular-text code" placeholder="#2271b1" /></td>
				</tr>
			</tbody>
		</table>

		<h3><?php esc_html_e( 'Bypass', 'bw-dev' ); ?></h3>
		<table class="form-table" role="presentation">
			<tbody>
				<tr>
					<th scope="row"><?php esc_html_e( 'Always allowed', 'bw-dev' ); ?></th>
					<td>
						<p style="margin:0 0 10px;">
							<?php esc_html_e( 'Users with the "manage_options" capability (administrators), wp-admin / wp-login.php / REST / cron / AJAX requests.', 'bw-dev' ); ?>
						</p>
					</td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e( 'Extra roles allowed to bypass', 'bw-dev' ); ?></th>
					<td>
						<fieldset>
							<?php foreach ( $all_roles as $role_slug => $role_data ) :
								$role_label = isset( $role_data['name'] ) ? translate_user_role( (string) $role_data['name'] ) : (string) $role_slug;
								$field_name = $base . '[bypass_roles][]';
								$is_checked = isset( $bypass_roles[ $role_slug ] );
								?>
								<label style="display:inline-block;margin-right:14px;">
									<input type="checkbox" name="<?php echo esc_attr( $field_name ); ?>" value="<?php echo esc_attr( $role_slug ); ?>" <?php checked( $is_checked ); ?> />
									<?php echo esc_html( $role_label ); ?>
								</label>
							<?php endforeach; ?>
						</fieldset>
						<p class="description"><?php esc_html_e( 'Administrators are always allowed — no need to tick that one.', 'bw-dev' ); ?></p>
					</td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e( 'Preview link', 'bw-dev' ); ?></th>
					<td>
						<?php if ( '' !== $preview_url ) : ?>
							<p style="margin:0 0 8px;">
								<?php esc_html_e( 'Share this URL with stakeholders so they can see the live site while maintenance mode is on. Visiting the link sets a session cookie that lasts until they close their browser.', 'bw-dev' ); ?>
							</p>
							<code style="display:block;background:#f6f7f7;padding:8px 12px;border-left:3px solid #2271b1;word-break:break-all;"><?php echo esc_html( $preview_url ); ?></code>
							<p class="description" style="margin-top:8px;">
								<?php esc_html_e( 'To rotate the token, clear the field below and save — a new one will be generated.', 'bw-dev' ); ?>
							</p>
							<input type="text" name="<?php echo esc_attr( $base . '[bypass_token]' ); ?>" value="<?php echo esc_attr( $token ); ?>" class="regular-text code" />
						<?php else : ?>
							<p style="margin:0;">
								<?php esc_html_e( 'A bypass token is generated automatically the first time you switch out of "Off" and save.', 'bw-dev' ); ?>
							</p>
							<input type="hidden" name="<?php echo esc_attr( $base . '[bypass_token]' ); ?>" value="" />
						<?php endif; ?>
					</td>
				</tr>
			</tbody>
		</table>

		<h3><?php esc_html_e( 'Notes', 'bw-dev' ); ?></h3>
		<ul style="list-style:disc;margin-left:20px;">
			<li><?php esc_html_e( 'The splash page is a self-contained HTML response — the active theme is not loaded, so it stays fast and works even if the theme is broken.', 'bw-dev' ); ?></li>
			<li><?php esc_html_e( 'Robots noindex,nofollow is emitted on the splash so search engines don\'t cache or rank it.', 'bw-dev' ); ?></li>
			<li><?php esc_html_e( 'Coming Soon and Under Construction send HTTP 200 (normal page). Maintenance sends HTTP 503 with a Retry-After header, which is the correct status for scheduled downtime.', 'bw-dev' ); ?></li>
		</ul>

		<script>
		(function ($) {
			$(function () {
				var $url     = $('#bw-dev-maintenance-logo');
				var frame;
				$('#bw-dev-maintenance-logo-upload').on('click', function (e) {
					e.preventDefault();
					if (frame) { frame.open(); return; }
					frame = wp.media({
						title:    <?php echo wp_json_encode( __( 'Choose maintenance logo', 'bw-dev' ) ); ?>,
						button:   { text: <?php echo wp_json_encode( __( 'Use as maintenance logo', 'bw-dev' ) ); ?> },
						multiple: false
					});
					frame.on('select', function () {
						var attachment = frame.state().get('selection').first().toJSON();
						$url.val(attachment.url);
					});
					frame.open();
				});
				$('#bw-dev-maintenance-logo-clear').on('click', function (e) {
					e.preventDefault();
					$url.val('');
				});
			});
		})(jQuery);
		</script>
		<?php
	}

	public function uninstall(): void {
		// Settings live under bw_dev_settings — root option drop covers them.
	}
}
