<?php
/**
 * Payment Form Block
 *
 * @package SimplePay
 * @subpackage Core
 * @copyright Copyright (c) 2022, Sandhills Development, LLC
 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
 * @since 4.4.2
 */

namespace SimplePay\Core\Block;

use SimplePay\Core\Admin\FormBuilder\FormStyle\Frontend as FormStyleFrontend;
use SimplePay\Core\Assets as CoreAssets;
use SimplePay\Core\Block\AbstractBlock;
use SimplePay\Core\License\LicenseAwareInterface;
use SimplePay\Core\License\LicenseAwareTrait;
use SimplePay\Pro\Assets as ProAssets;

/**
 * PaymentFormBlock class.
 *
 * @since 4.4.2
 */
class PaymentFormBlock extends AbstractBlock implements LicenseAwareInterface {

	use LicenseAwareTrait;

	/**
	 * {@inheritdoc}
	 */
	public function register() {
		$asset_file = SIMPLE_PAY_INC . 'core/assets/js/dist/simpay-block-payment-form.asset.php';

		if ( ! file_exists( $asset_file ) ) {
			return;
		}

		$this->register_assets( $asset_file );

		register_block_type(
			'simpay/payment-form',
			array(
				'title'           => _x(
					'WP Simple Pay - Payment Form',
					'block title',
					'stripe'
				),
				'description'     => _x(
					'Display a WP Simple Pay payment form.',
					'block description',
					'stripe'
				),
				'category'        => 'widgets',
				'keywords'        => array(
					_x( 'form', 'block keyword', 'stripe' ),
					_x( 'payment', 'block keyword', 'stripe' ),
					_x( 'stripe', 'block keyword', 'stripe' ),
					_x( 'simple pay', 'block keyword', 'stripe' ),
					_x( 'wp simple pay', 'block keyword', 'stripe' ),
				),
				'attributes'      => array(
					'formId'          => array(
						'type' => 'integer',
					),
					'showTitle'       => array(
						'type'    => 'boolean',
						'default' => true,
					),
					'showDescription' => array(
						'type'    => 'boolean',
						'default' => true,
					),
					'preview'         => array(
						'type'    => 'boolean',
						'default' => false,
					),
				),
				'example'         => array(
					'attributes' => array(
						'preview' => true,
					),
				),
				'supports'        => array(
					'html'  => false,
					'align' => array( 'center' ),
				),
				'editor_script'   => 'simpay-block-payment-form',
				'editor_style'    => 'simpay-block-payment-form',
				'render_callback' => array( $this, 'render' ),
			)
		);
	}

	/**
	 * Renders the block's output on the server.
	 *
	 * @since 4.4.2
	 *
	 * @param array<mixed> $attributes The block attributes.
	 * @return string Block content.
	 */
	public function render( $attributes ) {
		if ( ! isset( $attributes['formId'] ) || 0 === $attributes['formId'] ) {
			return '';
		}

		/** @var int $form_id */
		$form_id = $attributes['formId'];
		$form    = simpay_get_form( $form_id );

		if ( false === $form ) {
			return '';
		}

		$is_frontend = (
			! defined( 'REST_REQUEST' ) ||
			( defined( 'REST_REQUEST' ) && ! REST_REQUEST )
		);

		$vars               = $this->get_form_vars( $form );
		$styles             = $this->generate_inline_styles( $attributes );
		$wrapper_attributes = get_block_wrapper_attributes(
			array(
				'id' => 'simpay-block-payment-form-' . $form_id,
			)
		);

		// Include form style CSS for block editor previews, since
		// wp_print_footer_scripts does not fire during REST requests.
		if ( ! $is_frontend && class_exists( FormStyleFrontend::class ) ) {
			$form_style_frontend = FormStyleFrontend::get_instance();
			$styles             .= $form_style_frontend->get_form_css( $form_id );
		}

		$form_html = do_shortcode( sprintf( '[simpay id="%d"]', $form_id ) );

		// Inject data-form-id attributes inline for block editor previews,
		// since the footer JS injection does not run during REST requests.
		if ( ! $is_frontend ) {
			$heading_js = sprintf(
				'<script>
				(function() {
					var wrap = document.getElementById("simpay-embedded-form-wrap-%1$d");
					if (wrap) {
						wrap.setAttribute("data-form-id", "%1$d");
						var heading = wrap.previousElementSibling;
						if (heading && heading.classList && heading.classList.contains("simpay-embedded-heading")) {
							heading.setAttribute("data-form-id", "%1$d");
						}
					}
				})();
				</script>',
				$form_id
			);
		}

		return sprintf(
			'<div %1$s data-form-id="%2$s" data-form-vars=\'%3$s\'>%4$s</div><style>%5$s</style>%6$s',
			$wrapper_attributes,
			$form_id,
			// Only attach form variables in the block editor to prevent possible conflicts on the frontend.
			$is_frontend ? '' : wp_json_encode( $vars, JSON_HEX_QUOT | JSON_HEX_APOS ),
			$form_html,
			$styles,
			! $is_frontend ? $heading_js : ''
		);
	}

	/**
	 * Registers assets required to render the payment form in the block editor.
	 *
	 * @since 4.4.2
	 *
	 * @param string $asset_file Block script asset file.
	 * @return void
	 */
	private function register_assets( $asset_file ) {
		// Register frontend payment form assets.
		// Use the singleton to avoid adding duplicate wp_footer hooks for localize_scripts.
		$assets = CoreAssets::get_instance();

		if ( false === $this->license->is_lite() ) {
			ProAssets::get_instance();
		}

		$assets->register();

		$script_data = require $asset_file;

		// Register block editor payment form assets.
		wp_register_script(
			'simpay-block-payment-form',
			SIMPLE_PAY_INC_URL . 'core/assets/js/dist/simpay-block-payment-form.js',
			array_merge(
				$script_data['dependencies'],
				array_keys( $assets->scripts )
			),
			$script_data['version']
		);

		wp_localize_script(
			'simpay-block-payment-form',
			'simpayBlockPaymentForm',
			array(
				'isUpe'    => true,
				'isLite'   => $this->license->is_lite() ? 1 : 0,
				'previews' => array(
					'pro'  => SIMPLE_PAY_INC_URL . 'core/assets/images/blocks/payment-form-preview-pro.png',
					'lite' => SIMPLE_PAY_INC_URL . 'core/assets/images/blocks/payment-form-preview-lite.png',
				),
			)
		);

		wp_register_style(
			'simpay-block-payment-form',
			SIMPLE_PAY_INC_URL . 'core/assets/css/simpay-block-payment-form.min.css',
			array_keys( $assets->styles ),
			$script_data['version']
		);
	}

	/**
	 * Returns a list of variables used to manually initialize the payment form in the block editor.
	 *
	 * The non-UPE is messy, weird, and confusing. You are not crazy. It is a way to simulate the
	 * form of the `var simplePayForms = []` script data normally output on the frontend.
	 *
	 * @link https://github.com/awesomemotive/wp-simple-pay-pro/issues/860
	 *
	 * @since 4.4.2
	 *
	 * @param \SimplePay\Core\Abstracts\Form $form Payment form.
	 * @return array<mixed>
	 */
	private function get_form_vars( $form ) {
		$is_lite = $this->license->is_lite();

		/** @var \SimplePay\Core\Forms\Default_Form $form */

		$vars = $form->get_upe_script_variables();

		return $vars;
	}

	/**
	 * Generates CSS to show/hide pieces of the payemnt form.
	 *
	 * @since 4.4.2
	 *
	 * @param array<mixed> $attributes The block attributes.
	 * @return string
	 */
	private function generate_inline_styles( $attributes ) {
		$styles = '';
		/** @var int $form_id */
		$form_id = isset( $attributes['formId'] ) ? $attributes['formId'] : 0;

		// Hide the title.
		if (
			isset( $attributes['showTitle'] ) &&
			false === $attributes['showTitle']
		) {
			$styles .= sprintf(
				'[data-form-id="%d"] .simpay-form-title { display: none; }',
				$form_id
			);
		}

		// Hide the description.
		if (
			isset( $attributes['showDescription'] ) &&
			false === $attributes['showDescription']
		) {
			$styles .= sprintf(
				'[data-form-id="%d"] .simpay-form-description { display: none; }',
				$form_id
			);
		}

		// If both title and description are hidden, hide the header wrapper (embedded only).
		if (
			(
				isset( $attributes['showTitle'] ) &&
				false === $attributes['showTitle']
			) &&
			(
				isset( $attributes['showDescription'] ) &&
				false === $attributes['showDescription']
			)
		) {
			$styles .= sprintf(
				'[data-form-id="%d"] .simpay-embedded-heading { display: none; }',
				$form_id
			);
		}

		// Alignment.
		if ( isset( $attributes['align'] ) && 'center' === $attributes['align'] ) {
			$styles .= sprintf(
				'
				[data-form-id="%1$d"] .simpay-checkout-form,
				[data-form-id="%1$d"] .simpay-payment-btn,
				[data-form-id="%1$s"] .simpay-modal-control-open,
				[data-form-id="%1$s"] .simpay-embedded-heading {
					display: block;
					margin-left: auto;
					margin-right: auto;
				}
				[data-form-id="%1$s"] .simpay-embedded-heading.simpay-styled {
					max-width: 400px;
				}
				[data-form-id="%1$d"] .simpay-test-mode-badge-container {
					text-align: center;
				}',
				$form_id
			);
		}

		return $styles;
	}
}
