<?php
/**
 * Alpine initialization script — Pro overlay only.
 *
 * Secure design (rafter-secure-design / ingestion):
 * - Trust boundary: site admin POST → stored option → wp_add_inline_script.
 * - Lite zip does not ship this file, so wordpress.org builds cannot save
 *   or run pasted JavaScript. Presence of this file + freemium unlock
 *   is required before any init_code is accepted or printed.
 * - AuthZ on save: admin AJAX nonce (AjaxRouter) and unfiltered_html.
 * - Runtime does not re-check unfiltered_html (the visitor is not the author).
 *   Stored bytes are cleaned again (nulls, tags, length) before print.
 *
 * @package AlpineJsEnqueue
 */

namespace AlpineJsEnqueue;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

if ( class_exists( InitCode::class, false ) ) {
	return;
}

final class InitCode {

	private const MAX_LENGTH = 10000;

	public static function unlocked(): bool {
		return function_exists( 'wp_powersuite_freemium_unlocked' )
			&& wp_powersuite_freemium_unlocked( 'alpine-js-enqueue' );
	}

	/**
	 * Accept pasted JS only from a privileged admin when the overlay is unlocked.
	 */
	public static function sanitizeForStorage( string $value ): string {
		if ( ! self::unlocked() || ! current_user_can( 'unfiltered_html' ) ) {
			return '';
		}

		return self::clean( $value );
	}

	/**
	 * Defense-in-depth cleanup for stored or submitted init code.
	 */
	public static function clean( string $value ): string {
		$value = wp_unslash( $value );
		$value = str_replace( "\0", '', $value );
		$value = wp_strip_all_tags( $value );
		$value = preg_replace( '#</?script[^>]*>#i', '', $value ) ?? '';
		$value = trim( $value );

		if ( $value === '' ) {
			return '';
		}

		if ( strlen( $value ) > self::MAX_LENGTH ) {
			$value = substr( $value, 0, self::MAX_LENGTH );
		}

		return $value;
	}

	public static function prepareInline( string $init_code ): string {
		$init_code = self::clean( $init_code );
		if ( $init_code === '' ) {
			return '';
		}

		return "document.addEventListener('alpine:init', function() {\n" . $init_code . "\n});";
	}

	/**
	 * @param array<string, mixed> $settings
	 */
	public static function attach( string $handle, array $settings ): void {
		if ( ! self::unlocked() ) {
			return;
		}

		$script = self::prepareInline( isset( $settings['init_code'] ) && is_string( $settings['init_code'] ) ? $settings['init_code'] : '' );
		if ( $script === '' ) {
			return;
		}

		wp_add_inline_script( $handle, $script, 'after' );
	}

	/**
	 * @param array<string, mixed> $settings Incoming sanitized settings.
	 * @param array<string, mixed> $raw      Original submitted payload.
	 * @return array<string, mixed>
	 */
	public static function mergeOnSave( array $settings, array $raw ): array {
		if ( ! self::unlocked() ) {
			unset( $settings['init_code'] );
			return $settings;
		}

		if ( isset( $raw['init_code'] ) && is_string( $raw['init_code'] ) ) {
			$settings['init_code'] = self::sanitizeForStorage( $raw['init_code'] );
		} else {
			unset( $settings['init_code'] );
		}

		return $settings;
	}
}

if ( function_exists( 'add_filter' ) ) {
	add_filter(
		'wp_powersuite_preserve_pro_settings_on_save',
		static function ( $settings, string $module_id, $existing ) {
			if ( $module_id !== 'alpine-js-enqueue' || ! is_array( $settings ) ) {
				return $settings;
			}

			if ( ! InitCode::unlocked() ) {
				unset( $settings['init_code'] );
				return $settings;
			}

			if ( array_key_exists( 'init_code', $settings ) ) {
				return $settings;
			}

			$existing = is_array( $existing ) ? $existing : [];
			if ( isset( $existing['init_code'] ) && is_string( $existing['init_code'] ) ) {
				$settings['init_code'] = InitCode::clean( $existing['init_code'] );
			}

			return $settings;
		},
		10,
		3
	);
}
