<?php
/**
 * Shared frontend assets for content-protection modules (right-click, drag/drop, etc.).
 *
 * @package WP_PowerSuite\Core
 * @since 1.0.0
 */

namespace WP_PowerSuite\Core;

/**
 * Registers the shared script and style once per request when any feature is active.
 */
final class ContentProtection {

	/** Keyboard rules accepted by assets/content-protection.js */
	private const DISABLED_KEY_NAMES = [
		'devTools',
		'search',
		'ctrlC',
		'ctrlV',
		'ctrlA',
		'ctrlU',
		'ctrlP',
		'ctrlH',
		'ctrlL',
		'ctrlK',
		'ctrlO',
		'ctrlE',
		'ctrlF3',
		'altD',
		'f6',
		'f12',
	];

	private const MESSAGE_KEYS = [
		'disableRightClick',
		'disableDragDrop',
		'disableTextSelection',
		'disabledKeys',
	];

	private const POSITIONS = [
		'top-right',
		'top-left',
		'top-center',
		'bottom-right',
		'bottom-left',
		'bottom-center',
	];

	private static bool $enqueue_hook_registered = false;

	/**
	 * Normal frontend HTML document request — not admin, REST, cron, or feeds.
	 *
	 * Used by content-protection modules so hooks attach only where the shared script can run.
	 */
	/**
	 * Whether saved role/visitor settings could ever activate protection (no per-user check).
	 *
	 * @param array<string, mixed> $settings Module settings merged with defaults.
	 */
	public static function couldApplyForConfiguredAudience( array $settings ): bool {
		if ( ! empty( $settings['apply_to_visitors'] ) ) {
			return true;
		}
		$roles = $settings['roles'] ?? [];
		return is_array( $roles ) && $roles !== [];
	}

	public static function isBrowsableFrontendRequest(): bool {
		if ( is_admin() ) {
			return false;
		}
		if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
			return false;
		}
		if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
			return false;
		}
		if ( function_exists( 'is_feed' ) && is_feed() ) {
			return false;
		}
		return true;
	}

	/**
	 * Register a single wp_enqueue_scripts callback (idempotent).
	 *
	 * Call from content-protection modules on the {@see 'wp'} hook after user/context checks.
	 */
	public static function register_enqueue_callback(): void {
		if ( self::$enqueue_hook_registered ) {
			return;
		}
		self::$enqueue_hook_registered = true;
		add_action( 'wp_enqueue_scripts', [ self::class, 'enqueue_frontend_assets' ], 99 );
	}

	/**
	 * Enqueue shared CSS/JS if at least one protection flag is present in the merged config.
	 */
	public static function enqueue_frontend_assets(): void {
		if ( is_admin() ) {
			return;
		}

		if ( wp_script_is( 'wp-powersuite-content-protection', 'enqueued' ) ) {
			return;
		}

		$config = apply_filters( 'wp_powersuite_content_protection_config', [] );
		if ( ! is_array( $config ) ) {
			$config = [];
		}

		$config = self::prepare_config_for_localize( $config );

		$has_keys = ! empty( $config['disabledKeys'] );
		if ( ! $config['disableRightClick'] && ! $config['disableDragDrop'] && ! $config['disableTextSelection'] && ! $has_keys ) {
			return;
		}

		$base = defined( 'WP_POWERSUITE_PLUGIN_URL' ) ? WP_POWERSUITE_PLUGIN_URL : '';
		if ( $base === '' ) {
			return;
		}

		$version = defined( 'WP_POWERSUITE_VERSION' ) ? WP_POWERSUITE_VERSION : '1.0.0';

		wp_enqueue_style(
			'wp-powersuite-content-protection',
			$base . 'assets/content-protection.css',
			[],
			$version
		);
		wp_enqueue_script(
			'wp-powersuite-content-protection',
			$base . 'assets/content-protection.js',
			[],
			$version,
			true
		);
		wp_localize_script(
			'wp-powersuite-content-protection',
			'wpPowersuiteContentProtection',
			$config
		);
	}

	/**
	 * Normalize and sanitize config before JSON output to the browser.
	 *
	 * @param array<string, mixed> $raw Raw merged config (may include untrusted filter output).
	 * @return array<string, mixed>
	 */
	private static function prepare_config_for_localize( array $raw ): array {
		$default_msg = __( 'This function is disabled', 'wp-powersuite' );

		$out = [
			'disableRightClick'     => ! empty( $raw['disableRightClick'] ),
			'disableDragDrop'       => ! empty( $raw['disableDragDrop'] ),
			'disableTextSelection'  => ! empty( $raw['disableTextSelection'] ),
			'message'               => ( isset( $raw['message'] ) && is_string( $raw['message'] ) )
				? sanitize_text_field( $raw['message'] )
				: $default_msg,
			'messages'              => [],
		];

		$pos = isset( $raw['position'] ) ? sanitize_key( (string) $raw['position'] ) : 'top-right';
		$out['position'] = in_array( $pos, self::POSITIONS, true ) ? $pos : 'top-right';

		$dur = isset( $raw['duration'] ) ? absint( $raw['duration'] ) : 3000;
		$out['duration'] = min( 120000, max( 500, $dur ) );

		if ( isset( $raw['messages'] ) && is_array( $raw['messages'] ) ) {
			foreach ( self::MESSAGE_KEYS as $mk ) {
				if ( isset( $raw['messages'][ $mk ] ) && is_string( $raw['messages'][ $mk ] ) ) {
					$out['messages'][ $mk ] = sanitize_text_field( $raw['messages'][ $mk ] );
				}
			}
		}

		if ( ! empty( $raw['disabledKeys'] ) && is_array( $raw['disabledKeys'] ) ) {
			$keys = [];
			foreach ( self::DISABLED_KEY_NAMES as $kn ) {
				if ( ! empty( $raw['disabledKeys'][ $kn ] ) ) {
					$keys[ $kn ] = true;
				}
			}
			if ( ! empty( $keys ) ) {
				$out['disabledKeys'] = $keys;
			}
		}

		return $out;
	}
}
