<?php
/**
 * Service Worker
 *
 * @package Nisje
 */

declare( strict_types=1 );
namespace NisjeFrontend\SW;

/**
 * Serve service worker.
 */
function serve_service_worker_request() {
	/*
	 * Clear the currently-authenticated user to ensure that the service worker doesn't vary between users.
	 * Note that clearing the authenticated user in this way is in keeping with REST API requests wherein the
	 * WP_REST_Server::serve_request() method calls WP_REST_Server::check_authentication() which in turn applies
	 * the rest_authentication_errors filter which runs rest_cookie_check_errors() which is then responsible for
	 * calling wp_set_current_user( 0 ) if it was previously-determined a user was logged-in with the required
	 * nonce cookie set when wp_validate_auth_cookie() triggers one of the auth_cookie_* actions.
	 */
	\wp_set_current_user( 0 );

	// See wp_debug_mode() for how this is also done for REST API responses.
	@ini_set( 'display_errors', '0' ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged, WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_ini_set, WordPress.PHP.IniSet.display_errors_Blacklisted

	/*
	 * Per Workbox <https://developers.google.com/web/tools/workbox/guides/service-worker-checklist#cache-control_of_your_service_worker_file>:
	 * "Generally, most developers will want to set the Cache-Control header to no-cache,
	 * forcing browsers to always check the server for a new service worker file."
	 * Nevertheless, an ETag header is also sent with support for Conditional Requests
	 * to save on needlessly re-downloading the same service worker with each page load.
	 */
	@header( 'Cache-Control: no-cache' ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged, WordPress.PHP.NoSilencedErrors.Discouraged
	@header( 'X-Robots-Tag: noindex, follow' ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged, WordPress.PHP.NoSilencedErrors.Discouraged
	@header( 'Content-Type: text/javascript; charset=utf-8' ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged, WordPress.PHP.NoSilencedErrors.Discouraged

	$output = '';

	$sw_file = \get_stylesheet_directory() . '/build/service-worker/index.js';
	if ( file_exists( $sw_file ) ) {
		$output = file_get_contents( $sw_file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
	}

	$file_hash = md5( $output );
	$etag      = sprintf( '"%s"', $file_hash );
	@header( "ETag: $etag" ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged, WordPress.PHP.NoSilencedErrors.Discouraged

	$if_none_match = isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) ? trim( wp_unslash( $_SERVER['HTTP_IF_NONE_MATCH'] ) ) : false; // phpcs:ignore
	if ( $if_none_match === $etag ) {
		\status_header( 304 );
		return;
	} else {
		\status_header( 200 );
	}

	echo $output; // phpcs:ignore WordPress.XSS.EscapeOutput, WordPress.Security.EscapeOutput
}

/**
 * Serve the service worker for the frontend if requested.
 *
 * @param \WP_Query $query Query.
 */
function service_worker_loader( \WP_Query $query ) {
	global $wp;
	if ( ! $query->is_main_query() ) {
		return;
	}

	// Handle case where rewrite rules have not yet been flushed.
	if ( 'sw.nisje' === $wp->request ) {
		serve_service_worker_request();
		die();
	}
}

\add_action( 'parse_query', __NAMESPACE__ . '\\service_worker_loader' );
