<?php
/**
 * AMP (Accelerated Mobile Pages) module (lean frontend class).
 *
 * @package GTM4WP
 * @author Thomas Geiger (original AMP integration by Vincent Koc <https://github.com/koconder/>)
 * @copyright 2013- Geiger Tamás e.v. (Thomas Geiger s.e.)
 * @license GNU General Public License, version 3
 */

namespace GTM4WP\Modules\Amp;

use GTM4WP\Frontend\ContainerCode;
use GTM4WP\Module\AbstractModule;
use GTM4WP\Plugin;

defined( 'ABSPATH' ) || exit;

/**
 * Adds the GTM amp-analytics snippet to AMP pages generated by the amp-wp
 * plugin (ampproject/amp-wp).
 *
 * Since amp-wp 2.0 the integration goes through the plugin's cross-mode
 * `amp_analytics_entries` filter: amp-wp builds the `<amp-analytics>` element,
 * emits its inline `application/json` config and auto-loads the correct
 * `amp-analytics` component script. This works in the Standard, Transitional
 * and Reader (theme) modes as well as the deprecated Legacy Reader mode - the
 * previous port only ran in Legacy Reader mode, because it relied on the
 * `amp_post_template_*` hooks that no longer fire in the modern modes.
 *
 * The GTM container is configured through a remote config URL
 * (googletagmanager.com/amp.json); the compiled GTM4WP data layer is passed as
 * the amp-analytics `vars`.
 */
final class AmpModule extends AbstractModule {

	/**
	 * Module id.
	 *
	 * @return string
	 */
	public function id(): string {
		return 'amp';
	}

	/**
	 * Option defaults, 1.x compatible.
	 *
	 * @return array<string, mixed>
	 */
	public function defaults(): array {
		return array(
			GTM4WP_OPTION_INTEGRATE_AMPID => '',
		);
	}

	/**
	 * Registers the frontend hooks.
	 *
	 * @return void
	 */
	protected function register_frontend_hooks(): void {
		if ( '' === (string) $this->opt( GTM4WP_OPTION_INTEGRATE_AMPID ) ) {
			return;
		}

		// Report to the container code that an AMP page is being generated so the
		// standard (non-AMP) GTM container snippet is suppressed.
		add_filter( ContainerCode::FILTER_AMP_RUNNING, array( $this, 'is_amp_request' ) );

		// Inject the GTM amp-analytics tag through the amp-wp plugin. amp-wp owns
		// the markup, the inline JSON encoding and the component script.
		add_filter( 'amp_analytics_entries', array( $this, 'add_amp_analytics_entries' ) );
	}

	/**
	 * Admin schema class name.
	 *
	 * @return string
	 */
	public function admin_schema(): string {
		return AdminSchema::class;
	}

	/**
	 * Check if we are running AMP.
	 *
	 * Uses amp_is_request() (amp-wp >= 2.0), falling back to the pre-2.0
	 * is_amp_endpoint() so older amp-wp installs keep working.
	 *
	 * @param bool $running Current filter value.
	 * @return bool Returns true if we are running on an AMP page.
	 */
	public function is_amp_request( $running = false ) {
		if ( function_exists( 'amp_is_request' ) && amp_is_request() ) {
			return true;
		}

		if ( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() ) {
			return true;
		}

		return (bool) $running;
	}

	/**
	 * Adds one GTM amp-analytics entry per configured container ID to the
	 * amp-wp `amp_analytics_entries` filter.
	 *
	 * The compiled GTM4WP data layer is handed to amp-wp raw as the
	 * amp-analytics `vars`: amp-wp serializes `config_data` to JSON itself, so
	 * pre-escaping here would double-encode. The JSON lands in a
	 * `<script type="application/json">` (data, not executable JS) that amp-wp
	 * re-parses and validates, so this is a delegated sink (see the RI-2 note in
	 * .security/code-review-patterns.md).
	 *
	 * @param array<string, array<string, mixed>> $entries Analytics entries collected by amp-wp.
	 * @return array<string, array<string, mixed>> Entries with the GTM container(s) appended.
	 */
	public function add_amp_analytics_entries( $entries ) {
		if ( ! is_array( $entries ) ) {
			$entries = array();
		}

		// Read the compiled data layer. In Standard/Transitional/Reader-theme
		// modes wp_head already compiled it; in Legacy Reader mode wp_head does
		// not run, so compile it on demand. compiled() has no side effects and
		// compile() does not fire twice thanks to the DataLayer cache.
		$datalayer      = Plugin::instance()->frontend()->datalayer();
		$datalayer_data = $datalayer->compiled();
		if ( empty( $datalayer_data ) ) {
			$datalayer_data = $datalayer->compile();
		}

		if ( empty( $datalayer_data ) ) {
			return $entries;
		}

		foreach ( explode( ',', (string) $this->opt( GTM4WP_OPTION_INTEGRATE_AMPID ) ) as $one_amp_id ) {
			$one_amp_id = trim( $one_amp_id );

			if ( '' === $one_amp_id ) {
				continue;
			}

			// Docs: https://developers.google.com/analytics/devguides/collection/amp-analytics/.
			$entries[ 'gtm4wp-' . $one_amp_id ] = array(
				'attributes'  => array(
					'config'           => 'https://www.googletagmanager.com/amp.json?id=' . rawurlencode( $one_amp_id ) . '&gtm.url=SOURCE_URL',
					'data-credentials' => 'include',
				),
				'config_data' => array(
					'vars' => $datalayer_data,
				),
			);
		}

		return $entries;
	}
}
