<?php
/**
 * The Applepay module.
 *
 * @package WooCommerce\PayPalCommerce\Applepay
 */

declare(strict_types=1);

namespace WooCommerce\PayPalCommerce\Applepay;

use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry;
use WooCommerce\PayPalCommerce\Applepay\Assets\ApplePayButton;
use WooCommerce\PayPalCommerce\Applepay\Assets\AppleProductStatus;
use WooCommerce\PayPalCommerce\Applepay\Assets\PropertiesDictionary;
use WooCommerce\PayPalCommerce\Button\Assets\ButtonInterface;
use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface;
use WooCommerce\PayPalCommerce\Applepay\Helper\AvailabilityNotice;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;

/**
 * Class ApplepayModule
 */
class ApplepayModule implements ModuleInterface {
	/**
	 * {@inheritDoc}
	 */
	public function setup(): ServiceProviderInterface {
		return new ServiceProvider(
			require __DIR__ . '/../services.php',
			require __DIR__ . '/../extensions.php'
		);
	}

	/**
	 * {@inheritDoc}
	 */
	public function run( ContainerInterface $c ): void {
		$module = $this;

		// Clears product status when appropriate.
		add_action(
			'woocommerce_paypal_payments_clear_apm_product_status',
			function( Settings $settings = null ) use ( $c ): void {
				$apm_status = $c->get( 'applepay.apple-product-status' );
				assert( $apm_status instanceof AppleProductStatus );
				$apm_status->clear( $settings );
			}
		);

		add_action(
			'init',
			static function () use ( $c, $module ) {

				// Check if the module is applicable, correct country, currency, ... etc.
				if ( ! $c->get( 'applepay.eligible' ) ) {
					return;
				}

				// Load the button handler.
				$apple_payment_method = $c->get( 'applepay.button' );
				// add onboarding and referrals hooks.
				assert( $apple_payment_method instanceof ApplepayButton );
				$apple_payment_method->initialize();

				// Show notice if there are product availability issues.
				$availability_notice = $c->get( 'applepay.availability_notice' );
				assert( $availability_notice instanceof AvailabilityNotice );
				$availability_notice->execute();

				// Return if server not supported.
				if ( ! $c->get( 'applepay.server_supported' ) ) {
					return;
				}

				// Check if this merchant can activate / use the buttons.
				// We allow non referral merchants as they can potentially still use ApplePay, we just have no way of checking the capability.
				if ( ( ! $c->get( 'applepay.available' ) ) && $c->get( 'applepay.is_referral' ) ) {
					return;
				}

				if ( $apple_payment_method->is_enabled() ) {
					$module->load_assets( $c, $apple_payment_method );
					$module->handle_validation_file( $c, $apple_payment_method );
					$module->render_buttons( $c, $apple_payment_method );
					$apple_payment_method->bootstrap_ajax_request();
				}

				$module->load_admin_assets( $c, $apple_payment_method );
			},
			1
		);

		add_filter(
			'nonce_user_logged_out',
			/**
			 * Prevents nonce from being changed for non logged in users.
			 *
			 * @param int $uid The uid.
			 * @param string|int $action The action.
			 * @return int
			 *
			 * @psalm-suppress MissingClosureParamType
			 */
			function ( $uid, $action ) {
				if ( $action === PropertiesDictionary::NONCE_ACTION ) {
					return 0;
				}
				return $uid;
			},
			100,
			2
		);
	}

	/**
	 * Returns the key for the module.
	 *
	 * @return string|void
	 */
	public function getKey() {
	}

	/**
	 * Loads the validation string.
	 *
	 * @param boolean $is_sandbox The environment for this merchant.
	 */
	protected function load_domain_association_file( bool $is_sandbox ): void {
		if ( ! isset( $_SERVER['REQUEST_URI'] ) ) {
			return;
		}
		$request_uri = (string) filter_var( wp_unslash( $_SERVER['REQUEST_URI'] ), FILTER_SANITIZE_URL );
		if ( strpos( $request_uri, '.well-known/apple-developer-merchantid-domain-association' ) !== false ) {
			$validation_string = $this->validation_string( $is_sandbox );
			nocache_headers();
			header( 'Content-Type: text/plain', true, 200 );
			echo $validation_string;// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			exit;
		}
	}

	/**
	 * Registers and enqueues the assets.
	 *
	 * @param ContainerInterface $c The container.
	 * @param ApplePayButton     $button The button.
	 * @return void
	 */
	public function load_assets( ContainerInterface $c, ApplePayButton $button ): void {
		if ( ! $button->is_enabled() ) {
			return;
		}

		add_action(
			'wp_enqueue_scripts',
			function () use ( $c, $button ) {
				$smart_button = $c->get( 'button.smart-button' );
				assert( $smart_button instanceof SmartButtonInterface );
				if ( $smart_button->should_load_ppcp_script() ) {
					$button->enqueue();
					return;
				}

				if ( has_block( 'woocommerce/checkout' ) || has_block( 'woocommerce/cart' ) ) {
					/**
					 * Should add this to the ButtonInterface.
					 *
					 * @psalm-suppress UndefinedInterfaceMethod
					 */
					$button->enqueue_styles();
				}
			}
		);
		add_action(
			'woocommerce_blocks_payment_method_type_registration',
			function( PaymentMethodRegistry $payment_method_registry ) use ( $c ): void {
				$payment_method_registry->register( $c->get( 'applepay.blocks-payment-method' ) );
			}
		);
	}

	/**
	 * Registers and enqueues the assets.
	 *
	 * @param ContainerInterface $c The container.
	 * @param ApplePayButton     $button The button.
	 * @return void
	 */
	public function load_admin_assets( ContainerInterface $c, ApplePayButton $button ): void {
		// Enqueue backend scripts.
		add_action(
			'admin_enqueue_scripts',
			static function () use ( $c, $button ) {
				if ( ! is_admin() || ! $c->get( 'wcgateway.is-ppcp-settings-standard-payments-page' ) ) {
					return;
				}

				/**
				 * Should add this to the ButtonInterface.
				 *
				 * @psalm-suppress UndefinedInterfaceMethod
				 */
				$button->enqueue_admin();
			}
		);

		// Adds ApplePay component to the backend button preview settings.
		add_action(
			'woocommerce_paypal_payments_admin_gateway_settings',
			function( array $settings ) use ( $c ): array {
				if ( is_array( $settings['components'] ) ) {
					$settings['components'][] = 'applepay';
				}
				return $settings;
			}
		);
	}

	/**
	 * Renders the Apple Pay buttons in the enabled places.
	 *
	 * @param ContainerInterface $c The container.
	 * @param ApplePayButton     $button The button.
	 * @return void
	 */
	public function render_buttons( ContainerInterface $c, ApplePayButton $button ): void {
		if ( ! $button->is_enabled() ) {
			return;
		}

		add_action(
			'wp',
			static function () use ( $c ) {
				if ( is_admin() ) {
					return;
				}
				$button = $c->get( 'applepay.button' );

				/**
				 * The Button.
				 *
				 * @var ButtonInterface $button
				 */
				$button->render();
			}
		);
	}

	/**
	 * Handles the validation file.
	 *
	 * @param ContainerInterface $c The container.
	 * @param ApplePayButton     $button The button.
	 * @return void
	 */
	public function handle_validation_file( ContainerInterface $c, ApplePayButton $button ): void {
		if ( ! $button->is_enabled() ) {
			return;
		}
		$env = $c->get( 'onboarding.environment' );
		assert( $env instanceof Environment );
		$is_sandobx = $env->current_environment_is( Environment::SANDBOX );
		$this->load_domain_association_file( $is_sandobx );
	}

	/**
	 * Returns the validation string, depending on the environment.
	 *
	 * @param bool $is_sandbox The environment for this merchant.
	 * @return string
	 */
	public function validation_string( bool $is_sandbox ) {
		$sandbox_string = $this->sandbox_validation_string();
		$live_string    = $this->live_validation_string();
		return $is_sandbox ? $sandbox_string : $live_string;
	}

	/**
	 * Returns the sandbox validation string.
	 *
	 * @return string
	 */
	private function sandbox_validation_string(): string {
		return apply_filters(
			'woocommerce_paypal_payments_applepay_sandbox_validation_string',
			'[encoded data, 9122 characters]'
		);
	}

	/**
	 * Returns the live validation string.
	 *
	 * @return string
	 */
	private function live_validation_string(): string {
		return apply_filters(
			'woocommerce_paypal_payments_applepay_live_validation_string',
			'[encoded data, 9118 characters]'
		);
	}
}
