<?php
/**
 * Analytics Integration Module
 *
 * Provides Google Analytics 4 pageview tracking with environment awareness,
 * consent-mode defaults, privacy controls, and duplicate-tracking guards.
 *
 * @package AnalyticsIntegration
 * @since 1.0.0
 */

namespace AnalyticsIntegration;

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

/**
 * Analytics Integration Module Class
 *
 * @since 1.0.0
 */
class Module extends AbstractModule implements DataLocationsInterface {

	private const MODULE_ID = 'analytics-integration';
	private const MODULE_VERSION = '1.1.0';
	public const GA4_ID_PATTERN = '/^G-[A-Z0-9]{10}$/';

	/** @var array<string, mixed>|null Cached settings for current request */
	private ?array $settings_cache = null;

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

	public function getName(): string {
		return __( 'Analytics Integration', 'wp-powersuite' );
	}

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

	public function getDescription(): string {
		return __( 'Google Analytics 4 pageview tracking with consent-mode defaults, staging guards, role exclusions, and duplicate-snippet prevention.', 'wp-powersuite' );
	}

	public function getContext(): string|array {
		return 'frontend';
	}

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

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

	protected function registerHooks(): void {
		add_action( 'wp_head', [ $this, 'injectTrackingCode' ], 1 );
	}

	/**
	 * Default module settings.
	 *
	 * @return array<string, mixed>
	 */
	public static function getDefaultSettings(): array {
		return [
			'ga4_id'               => '',
			'disable_for_admins'   => true,
			'exclude_roles'        => [],
			'disable_on_staging'   => true,
			'exclude_login'        => true,
			'anonymize_ip'         => true,
			'respect_dnt'          => true,
			'consent_mode'         => true,
			'cache_safe_exclusion' => true,
		];
	}

	/**
	 * Validate a GA4 measurement ID.
	 */
	public static function isValidGa4Id( string $ga4_id ): bool {
		return (bool) preg_match( self::GA4_ID_PATTERN, strtoupper( trim( $ga4_id ) ) );
	}

	/**
	 * Normalize and sanitize settings before save or runtime use.
	 *
	 * @param array<string, mixed> $settings Incoming settings.
	 * @param array<string, mixed> $existing Existing stored settings.
	 * @return array<string, mixed>
	 */
	public static function normalizeSettings( array $settings, array $existing = [] ): array {
		$merged = array_merge( self::getDefaultSettings(), $existing, $settings );

		$ga4_id = strtoupper( trim( (string) ( $merged['ga4_id'] ?? '' ) ) );
		if ( $ga4_id !== '' && ! self::isValidGa4Id( $ga4_id ) ) {
			$existing_id = strtoupper( trim( (string) ( $existing['ga4_id'] ?? '' ) ) );
			$ga4_id      = self::isValidGa4Id( $existing_id ) ? $existing_id : '';
		}

		$exclude_roles = $merged['exclude_roles'] ?? [];
		if ( ! is_array( $exclude_roles ) ) {
			$exclude_roles = [];
		}
		$exclude_roles = array_values(
			array_unique(
				array_filter(
					array_map( 'sanitize_key', $exclude_roles )
				)
			)
		);

		return [
			'ga4_id'               => $ga4_id,
			'disable_for_admins'   => ! empty( $merged['disable_for_admins'] ),
			'exclude_roles'        => $exclude_roles,
			'disable_on_staging'   => ! empty( $merged['disable_on_staging'] ),
			'exclude_login'        => array_key_exists( 'exclude_login', $merged ) ? ! empty( $merged['exclude_login'] ) : true,
			'anonymize_ip'         => ! empty( $merged['anonymize_ip'] ),
			'respect_dnt'          => ! empty( $merged['respect_dnt'] ),
			'consent_mode'         => array_key_exists( 'consent_mode', $merged ) ? ! empty( $merged['consent_mode'] ) : true,
			'cache_safe_exclusion' => array_key_exists( 'cache_safe_exclusion', $merged ) ? ! empty( $merged['cache_safe_exclusion'] ) : true,
		];
	}

	/**
	 * Resolve the effective WordPress environment for tracking decisions.
	 */
	public static function resolveEnvironment(): string {
		if ( defined( 'WP_ENV' ) ) {
			$wp_env = strtolower( (string) WP_ENV );
			if ( in_array( $wp_env, [ 'staging', 'local', 'dev', 'development' ], true ) ) {
				return $wp_env === 'dev' ? 'development' : $wp_env;
			}
		}

		$env_type = wp_get_environment_type();
		if ( in_array( $env_type, [ 'staging', 'local', 'development' ], true ) ) {
			return $env_type;
		}

		if ( $env_type === 'production' ) {
			$host = isset( $_SERVER['HTTP_HOST'] ) ? strtolower( (string) $_SERVER['HTTP_HOST'] ) : '';
			$server_addr = isset( $_SERVER['SERVER_ADDR'] ) ? (string) $_SERVER['SERVER_ADDR'] : '';
			if ( in_array( $host, [ 'localhost', '127.0.0.1' ], true ) ||
				str_contains( $host, '.local' ) ||
				str_contains( $host, '.test' ) ||
				in_array( $server_addr, [ '127.0.0.1', '::1' ], true ) ||
				str_starts_with( $server_addr, '192.168.' ) ) {
				return 'local';
			}
		}

		return $env_type;
	}

	/**
	 * Whether the current environment should be treated as non-production.
	 */
	public static function isNonProductionEnvironment(): bool {
		return in_array( self::resolveEnvironment(), [ 'staging', 'local', 'development' ], true );
	}

	/**
	 * Whether tracking would be active for the given settings (ignores request/user context).
	 *
	 * @param array<string, mixed> $settings Normalized settings.
	 */
	public static function wouldTrackForSettings( array $settings ): bool {
		if ( ! self::isValidGa4Id( (string) ( $settings['ga4_id'] ?? '' ) ) ) {
			return false;
		}

		if ( ! empty( $settings['disable_on_staging'] ) && self::isNonProductionEnvironment() ) {
			return false;
		}

		return true;
	}

	/**
	 * Detect duplicate GA4/gtag snippets in other PowerSuite modules.
	 *
	 * @return array<int, string> Human-readable duplicate source labels.
	 */
	public static function detectDuplicateSources( string $ga4_id = '' ): array {
		$duplicates = [];

		if ( ! class_exists( OptionStoreModuleSettingsRepository::class ) ) {
			return $duplicates;
		}

		if ( OptionStoreModuleSettingsRepository::isModuleEnabled( 'header-footer-code' ) ) {
			$hfc = OptionStoreModuleSettingsRepository::forModule( 'header-footer-code' )->all( [] );
			if ( is_array( $hfc ) ) {
				foreach ( [ 'header_code', 'body_code', 'footer_code' ] as $location ) {
					$code = (string) ( $hfc[ $location ] ?? '' );
					if ( $code !== '' && self::codeContainsGaTracking( $code, $ga4_id ) ) {
						$duplicates[] = __( 'Header & Footer Code module', 'wp-powersuite' );
						break;
					}
				}
			}
		}

		return array_values( array_unique( $duplicates ) );
	}

	/**
	 * Whether stored code appears to output Google Analytics / gtag tracking.
	 */
	public static function codeContainsGaTracking( string $code, string $ga4_id = '' ): bool {
		$code = strtolower( $code );
		if ( $code === '' ) {
			return false;
		}

		if ( $ga4_id !== '' && str_contains( $code, strtolower( $ga4_id ) ) ) {
			return true;
		}

		return str_contains( $code, 'googletagmanager.com/gtag' )
			|| str_contains( $code, "gtag('config'" )
			|| str_contains( $code, 'gtag("config"' )
			|| preg_match( '/\bg-[a-z0-9]{10}\b/i', $code ) === 1;
	}

	/**
	 * Inject Tracking Code
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function injectTrackingCode(): void {
		if ( $this->shouldSkipRequest() ) {
			return;
		}

		$settings = $this->getSettings();
		$ga4_id   = (string) ( $settings['ga4_id'] ?? '' );

		if ( ! self::isValidGa4Id( $ga4_id ) ) {
			return;
		}

		if ( ! $this->shouldTrackInEnvironment() ) {
			return;
		}

		if ( self::hasExternalGaTracking( $ga4_id ) && apply_filters( 'wpps_analytics_skip_duplicate', true, $ga4_id ) ) {
			return;
		}

		if ( ! $this->shouldTrackUser() ) {
			return;
		}

		$allow_analytics = (bool) apply_filters( 'wpps_allow_analytics', true );
		if ( ! $allow_analytics && empty( $settings['consent_mode'] ) ) {
			return;
		}

		$this->injectGa4Code( $ga4_id, $settings, $allow_analytics );
	}

	/**
	 * Whether another enabled module already outputs GA tracking for this property.
	 */
	public static function hasExternalGaTracking( string $ga4_id ): bool {
		return self::detectDuplicateSources( $ga4_id ) !== [];
	}

	/**
	 * Inject GA4 Code
	 *
	 * @param string               $ga4_id           GA4 Measurement ID.
	 * @param array<string, mixed> $settings         Module settings.
	 * @param bool                 $allow_analytics  Whether analytics config should run now.
	 * @return void
	 */
	private function injectGa4Code( string $ga4_id, array $settings, bool $allow_analytics ): void {
		$consent_mode   = ! empty( $settings['consent_mode'] );
		$anonymize_ip   = ! empty( $settings['anonymize_ip'] );
		$respect_dnt    = ! empty( $settings['respect_dnt'] );
		$cache_safe     = ! empty( $settings['cache_safe_exclusion'] );
		$has_exclusion  = ! empty( $settings['disable_for_admins'] ) || ! empty( $settings['exclude_roles'] );
		$gtag_src       = 'https://www.googletagmanager.com/gtag/js?id=' . rawurlencode( $ga4_id );

		?>
		<!-- Google tag (gtag.js) - WP PowerSuite -->
		<?php if ( $cache_safe && $has_exclusion ) : ?>
		<script>
		(function(){if(document.cookie.indexOf('wordpress_logged_in_')!==-1){window['gtag-disable-<?php echo esc_js( $ga4_id ); ?>']=true;}})();
		</script>
		<?php endif; ?>
		<script>
		(function () {
			<?php if ( $respect_dnt ) : ?>
			if (navigator.doNotTrack === '1' || window.doNotTrack === '1' || navigator.msDoNotTrack === '1') {
				return;
			}
			<?php endif; ?>

			var gtagScript = document.createElement('script');
			gtagScript.async = true;
			gtagScript.src = <?php echo wp_json_encode( $gtag_src ); ?>;
			document.head.appendChild(gtagScript);

			window.dataLayer = window.dataLayer || [];
			function gtag(){dataLayer.push(arguments);}
			window.gtag = gtag;
			gtag('js', new Date());

			<?php if ( $consent_mode ) : ?>
			gtag('consent', 'default', {
				'analytics_storage': 'denied',
				'ad_storage': 'denied',
				'ad_user_data': 'denied',
				'ad_personalization': 'denied',
				'wait_for_update': 500
			});
			<?php endif; ?>

			<?php if ( $allow_analytics ) : ?>
				<?php if ( $consent_mode ) : ?>
			gtag('consent', 'update', {
				'analytics_storage': 'granted'
			});
				<?php endif; ?>
			var ga4Config = {};
				<?php if ( $anonymize_ip ) : ?>
			ga4Config.anonymize_ip = true;
				<?php endif; ?>
			gtag('config', <?php echo wp_json_encode( $ga4_id ); ?>, ga4Config);
			<?php endif; ?>
		})();
		</script>
		<?php
	}

	/**
	 * Skip unsupported request contexts.
	 */
	private function shouldSkipRequest(): bool {
		if ( is_admin() ) {
			return true;
		}

		if ( ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ||
			( defined( 'DOING_AJAX' ) && DOING_AJAX ) ||
			( defined( 'DOING_CRON' ) && DOING_CRON ) ) {
			return true;
		}

		if ( is_feed() || is_robots() || is_trackback() ) {
			return true;
		}

		$settings = $this->getSettings();
		if ( ! empty( $settings['exclude_login'] ) && $this->isLoginRequest() ) {
			return true;
		}

		return false;
	}

	/**
	 * Whether the current request is wp-login.php.
	 */
	private function isLoginRequest(): bool {
		if ( isset( $GLOBALS['pagenow'] ) && $GLOBALS['pagenow'] === 'wp-login.php' ) {
			return true;
		}

		if ( ! isset( $_SERVER['SCRIPT_NAME'] ) ) {
			return false;
		}

		$script = wp_normalize_path( sanitize_text_field( wp_unslash( (string) $_SERVER['SCRIPT_NAME'] ) ) );

		return str_ends_with( $script, '/wp-login.php' );
	}

	/**
	 * Should Track in Environment
	 *
	 * @return bool
	 */
	private function shouldTrackInEnvironment(): bool {
		$settings = $this->getSettings();

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

		return ! self::isNonProductionEnvironment();
	}

	/**
	 * Should Track User
	 *
	 * @return bool
	 */
	private function shouldTrackUser(): bool {
		if ( ! is_user_logged_in() ) {
			return true;
		}

		$settings = $this->getSettings();
		$user     = wp_get_current_user();
		if ( ! $user instanceof \WP_User ) {
			return true;
		}

		if ( ! empty( $settings['disable_for_admins'] ) && user_can( $user, 'manage_options' ) ) {
			return false;
		}

		$exclude_roles = $settings['exclude_roles'] ?? [];
		if ( ! is_array( $exclude_roles ) || $exclude_roles === [] ) {
			return true;
		}

		$user_roles = is_array( $user->roles ) ? $user->roles : [];
		foreach ( $exclude_roles as $role ) {
			if ( in_array( $role, $user_roles, true ) ) {
				return false;
			}
		}

		return true;
	}

	/**
	 * Get module settings (request-level cached).
	 *
	 * @return array<string, mixed>
	 */
	private function getSettings(): array {
		if ( $this->settings_cache !== null ) {
			return $this->settings_cache;
		}

		$settings = $this->moduleSettings()->all( [] );
		if ( ! is_array( $settings ) ) {
			$settings = [];
		}

		$this->settings_cache = self::normalizeSettings( $settings );

		return $this->settings_cache;
	}
}
