<?php

/**
 * The Gateway module.
 *
 * @package WooCommerce\PayPalCommerce\WcGateway
 */
declare (strict_types=1);
namespace WooCommerce\PayPalCommerce\WcGateway;

use Exception;
use WooCommerce\PayPalCommerce\Vendor\Psr\Log\LoggerInterface;
use Throwable;
use WC_Order;
use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message;
use WooCommerce\PayPalCommerce\AdminNotices\Repository\Repository;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Capture;
use WooCommerce\PayPalCommerce\ApiClient\Helper\ReferenceTransactionStatus;
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
use WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods\LocalApmProductStatus;
use WooCommerce\PayPalCommerce\Settings\Data\Definition\FeaturesDefinition;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Admin\FeesRenderer;
use WooCommerce\PayPalCommerce\WcGateway\Admin\OrderTablePaymentStatusColumn;
use WooCommerce\PayPalCommerce\WcGateway\Admin\PaymentStatusOrderDetail;
use WooCommerce\PayPalCommerce\WcGateway\Assets\FraudNetAssets;
use WooCommerce\PayPalCommerce\WcGateway\Assets\SettingsPageAssets;
use WooCommerce\PayPalCommerce\WcGateway\Assets\VoidButtonAssets;
use WooCommerce\PayPalCommerce\WcGateway\Checkout\CheckoutPayPalAddressPreset;
use WooCommerce\PayPalCommerce\WcGateway\Checkout\DisableGateways;
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\RefreshFeatureStatusEndpoint;
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ReturnUrlEndpoint;
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ShippingCallbackEndpoint;
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\VoidOrderEndpoint;
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\GatewayRepository;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WooCommerce\PayPalCommerce\WcGateway\Helper\CardPaymentsConfiguration;
use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCProductStatus;
use WooCommerce\PayPalCommerce\WcGateway\Helper\InstallmentsProductStatus;
use WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods\PayUponInvoice\PayUponInvoiceProductStatus;
use WooCommerce\PayPalCommerce\WcGateway\Helper\PWCProductStatus;
use WooCommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus;
use WooCommerce\PayPalCommerce\WcGateway\Notice\ConnectAdminNotice;
use WooCommerce\PayPalCommerce\WcGateway\Notice\GatewayWithoutPayPalAdminNotice;
use WooCommerce\PayPalCommerce\WcGateway\Notice\SendOnlyCountryNotice;
use WooCommerce\PayPalCommerce\WcGateway\Notice\UnsupportedCurrencyAdminNotice;
use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor;
use WooCommerce\PayPalCommerce\WcGateway\Processor\CreditCardOrderInfoHandlingTrait;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WooCommerce\PayPalCommerce\WcGateway\WcInboxNotes\InboxNoteRegistrar;
use WooCommerce\PayPalCommerce\WcGateway\WcTasks\Registrar\TaskRegistrarInterface;
use WooCommerce\PayPalCommerce\Settings\Data\SettingsProvider;
/**
 * Class WcGatewayModule
 */
class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModule
{
    use ModuleClassNameIdTrait;
    use CreditCardOrderInfoHandlingTrait;
    /**
     * {@inheritDoc}
     */
    public function services(): array
    {
        return require __DIR__ . '/../services.php';
    }
    /**
     * {@inheritDoc}
     */
    public function extensions(): array
    {
        return require __DIR__ . '/../extensions.php';
    }
    /**
     * {@inheritDoc}
     */
    public function run(ContainerInterface $c): bool
    {
        $this->register_payment_gateways($c);
        $this->register_order_functionality($c);
        $this->register_payment_method_title_enrichment($c);
        $this->register_contact_handlers();
        $this->register_block_express_payment_method_handler($c);
        $this->register_columns($c);
        $this->register_checkout_paypal_address_preset($c);
        $this->register_wc_tasks($c);
        $this->register_woo_inbox_notes($c);
        $this->register_void_button($c);
        add_action('woocommerce_paypal_payments_order_captured', function (WC_Order $wc_order, Capture $capture) use ($c) {
            $breakdown = $capture->seller_receivable_breakdown();
            if ($breakdown) {
                $wc_order->update_meta_data(PayPalGateway::FEES_META_KEY, $breakdown->to_array());
                $paypal_fee = $breakdown->paypal_fee();
                if ($paypal_fee) {
                    $wc_order->update_meta_data('PayPal Transaction Fee', (string) $paypal_fee->value());
                }
                $wc_order->save_meta_data();
            }
            $order = $c->get('session.handler')->order();
            if (!$order) {
                return;
            }
            $fraud = $capture->fraud_processor_response();
            if ($fraud) {
                $this->handle_fraud($fraud, $order, $wc_order);
            }
            $this->handle_three_d_secure($order, $wc_order);
        }, 10, 2);
        add_action('woocommerce_paypal_payments_order_authorized', function (WC_Order $wc_order, Authorization $authorization) use ($c) {
            $order = $c->get('session.handler')->order();
            if (!$order) {
                return;
            }
            $fraud = $authorization->fraud_processor_response();
            if ($fraud) {
                $this->handle_fraud($fraud, $order, $wc_order);
            }
            $this->handle_three_d_secure($order, $wc_order);
        }, 10, 2);
        $fees_renderer = $c->get('wcgateway.admin.fees-renderer');
        assert($fees_renderer instanceof FeesRenderer);
        add_action('woocommerce_admin_order_totals_after_total', function (int $order_id) use ($fees_renderer) {
            $wc_order = wc_get_order($order_id);
            if (!$wc_order instanceof WC_Order) {
                return;
            }
            /**
             * The filter can be used to remove the rows with PayPal fees in WC orders.
             */
            if (!apply_filters('woocommerce_paypal_payments_show_fees_on_order_admin_page', \true, $wc_order)) {
                return;
            }
            // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
            echo $fees_renderer->render($wc_order);
        });
        add_action('woocommerce_admin_order_totals_after_total', function (int $order_id) use ($c) {
            $wc_order = wc_get_order($order_id);
            if (!$wc_order instanceof WC_Order) {
                return;
            }
            $fraud_result = $wc_order->get_meta(PayPalGateway::FRAUD_RESULT_META_KEY);
            if (empty($fraud_result['response_code']) || $fraud_result['response_code'] === '0000') {
                return;
            }
            $fraud = $c->get('api.factory.fraud-processor-response')->from_paypal_response((object) $fraud_result);
            printf('<tr><td class="label">%s:</td><td width="1%%"></td><td class="total">%s</td></tr>', esc_html__('Processor Response', 'woocommerce-paypal-payments'), esc_html($fraud->get_response_code_message()));
        });
        add_action('admin_enqueue_scripts', function () use ($c) {
            if (!is_admin() || wp_doing_ajax()) {
                return;
            }
            if (!$c->has('wcgateway.asset_getter')) {
                return;
            }
            $settings_status = $c->get('wcgateway.settings.status');
            assert($settings_status instanceof SettingsStatus);
            $settings = $c->get('wcgateway.settings');
            assert($settings instanceof Settings);
            $dcc_configuration = $c->get('wcgateway.configuration.card-configuration');
            assert($dcc_configuration instanceof CardPaymentsConfiguration);
            // todo: #legacy-ui assets that can be removed.
            $assets = new SettingsPageAssets($c->get('wcgateway.asset_getter'), $c->get('ppcp.asset-version'), $c->get('wc-subscriptions.helper'), $c->get('button.client_id_for_admin'), $c->get('api.shop.currency.getter'), $c->get('api.shop.country'), $c->get('settings.environment'), $settings_status->is_pay_later_button_enabled(), $settings->has('disable_funding') ? $settings->get('disable_funding') : array(), array(), $c->get('wcgateway.is-plugin-settings-page'), $dcc_configuration->is_enabled(), $c->get('api.reference-transaction-status'), $c->get('wcgateway.is-plugin-settings-page'));
            $assets->register_assets();
        });
        // todo: remove this with #legacy-ui code?
        add_filter(Repository::NOTICES_FILTER, static function ($notices) use ($c): array {
            $notice = $c->get('wcgateway.notice.connect');
            assert($notice instanceof ConnectAdminNotice);
            $connect_message = $notice->connect_message();
            if ($connect_message) {
                $notices[] = $connect_message;
            }
            $notice = $c->get('wcgateway.notice.currency-unsupported');
            assert($notice instanceof UnsupportedCurrencyAdminNotice);
            $unsupported_currency_message = $notice->unsupported_currency_message();
            if ($unsupported_currency_message) {
                $notices[] = $unsupported_currency_message;
            }
            foreach (array($c->get('wcgateway.notice.dcc-without-paypal'), $c->get('wcgateway.notice.card-button-without-paypal')) as $gateway_without_paypal_notice) {
                assert($gateway_without_paypal_notice instanceof GatewayWithoutPayPalAdminNotice);
                $message = $gateway_without_paypal_notice->message();
                if ($message) {
                    $notices[] = $message;
                }
            }
            $send_only_country_notice = $c->get('wcgateway.notice.send-only-country');
            assert($send_only_country_notice instanceof SendOnlyCountryNotice);
            $message = $send_only_country_notice->message();
            if ($message) {
                $notices[] = $message;
            }
            $authorize_order_action = $c->get('wcgateway.notice.authorize-order-action');
            $authorized_message = $authorize_order_action->message();
            if ($authorized_message) {
                $notices[] = $authorized_message;
            }
            return $notices;
        });
        add_action('woocommerce_paypal_commerce_gateway_deactivate', static function () {
            delete_option(Settings::KEY);
            delete_option('woocommerce_' . PayPalGateway::ID . '_settings');
            delete_option('woocommerce_' . CreditCardGateway::ID . '_settings');
        });
        add_action('wc_ajax_' . ReturnUrlEndpoint::ENDPOINT, static function () use ($c) {
            $endpoint = $c->get('wcgateway.endpoint.return-url');
            /**
             * The Endpoint.
             *
             * @var ReturnUrlEndpoint $endpoint
             */
            $endpoint->handle_request();
        });
        add_action('wc_ajax_' . RefreshFeatureStatusEndpoint::ENDPOINT, static function () use ($c) {
            $endpoint = $c->get('wcgateway.endpoint.refresh-feature-status');
            assert($endpoint instanceof RefreshFeatureStatusEndpoint);
            $endpoint->handle_request();
        });
        add_action('woocommerce_paypal_payments_gateway_migrate', static function () use ($c) {
            delete_option('ppcp-request-ids');
            $settings = $c->get('wcgateway.settings');
            assert($settings instanceof Settings);
            try {
                if ($settings->has('3d_secure_contingency') && $settings->get('3d_secure_contingency') === '3D_SECURE') {
                    $settings->set('3d_secure_contingency', 'SCA_ALWAYS');
                    $settings->persist();
                }
            } catch (NotFoundException $exception) {
                return;
            }
        });
        // Clear the cached ProductStatus information on plugin update.
        add_action('woocommerce_paypal_payments_gateway_migrate_on_update', static function () {
            do_action('woocommerce_paypal_payments_clear_apm_product_status');
        });
        add_action('woocommerce_paypal_payments_clear_apm_product_status', static function () use ($c) {
            $dcc_status = $c->get('wcgateway.helper.dcc-product-status');
            assert($dcc_status instanceof DCCProductStatus);
            $dcc_status->clear();
            $dcc_status->is_active();
            $pui_status = $c->get('wcgateway.pay-upon-invoice-product-status');
            assert($pui_status instanceof PayUponInvoiceProductStatus);
            $pui_status->clear();
            $pui_status->is_active();
        }, 20);
        add_action('wp_loaded', function () use ($c) {
            $fraudnet_assets = $c->get('wcgateway.fraudnet-assets');
            assert($fraudnet_assets instanceof FraudNetAssets);
            $fraudnet_assets->register_assets();
        });
        add_action('woocommerce_order_status_changed', static function (int $order_id, string $from, string $to) use ($c) {
            $wc_order = wc_get_order($order_id);
            if (!$wc_order instanceof WC_Order) {
                return;
            }
            $settings = $c->get('settings.settings-provider');
            assert($settings instanceof SettingsProvider);
            if (!$settings->capture_on_status_change()) {
                return;
            }
            $gateway_repository = $c->get('wcgateway.gateway-repository');
            assert($gateway_repository instanceof GatewayRepository);
            // Only allow proceeding if the payment method is one of our Gateways.
            if (!$gateway_repository->exists($wc_order->get_payment_method())) {
                return;
            }
            $intent = strtoupper((string) $wc_order->get_meta(PayPalGateway::INTENT_META_KEY));
            $captured = wc_string_to_bool($wc_order->get_meta(AuthorizedPaymentsProcessor::CAPTURED_META_KEY));
            if ($intent !== 'AUTHORIZE' || $captured) {
                return;
            }
            /**
             * The filter returning the WC order statuses which trigger capturing of payment authorization.
             */
            $capture_statuses = apply_filters('woocommerce_paypal_payments_auto_capture_statuses', array('processing', 'completed'), $wc_order);
            if (!in_array($to, $capture_statuses, \true)) {
                return;
            }
            $authorized_payment_processor = $c->get('wcgateway.processor.authorized-payments');
            assert($authorized_payment_processor instanceof AuthorizedPaymentsProcessor);
            try {
                if ($authorized_payment_processor->capture_authorized_payment($wc_order)) {
                    return;
                }
            } catch (Throwable $error) {
                $logger = $c->get('woocommerce.logger.woocommerce');
                assert($logger instanceof LoggerInterface);
                $logger->error("Capture failed. {$error->getMessage()} {$error->getFile()}:{$error->getLine()}");
            }
            $wc_order->update_status('failed', __('Could not capture the payment.', 'woocommerce-paypal-payments'));
        }, 10, 3);
        // Clears product status when appropriate.
        add_action('woocommerce_paypal_payments_clear_apm_product_status', static function () use ($c): void {
            // Clear DCC Product status.
            $dcc_product_status = $c->get('wcgateway.helper.dcc-product-status');
            if ($dcc_product_status instanceof DCCProductStatus) {
                $dcc_product_status->clear();
            }
            // Clear Pay Upon Invoice status.
            $pui_product_status = $c->get('wcgateway.pay-upon-invoice-product-status');
            if ($pui_product_status instanceof PayUponInvoiceProductStatus) {
                $pui_product_status->clear();
            }
            // Clear PWC status.
            $pwc_product_status = $c->get('wcgateway.pwc-product-status');
            if ($pwc_product_status instanceof PWCProductStatus) {
                $pwc_product_status->clear();
            }
        });
        /**
         * Param types removed to avoid third-party issues.
         *
         * @psalm-suppress MissingClosureParamType
         */
        add_filter('woocommerce_admin_billing_fields', fn($fields) => $this->insert_custom_fields_into_order_details($fields));
        /**
         * Param types removed to avoid third-party issues.
         *
         * @psalm-suppress MissingClosureParamType
         */
        add_action('woocommerce_admin_order_data_after_shipping_address', fn($order) => $this->display_original_contact_in_order_details($order));
        add_action('woocommerce_paypal_payments_gateway_migrate', function (string $installed_plugin_version) use ($c) {
            $settings = $c->get('wcgateway.settings');
            assert($settings instanceof Settings);
            if (!$installed_plugin_version) {
                $settings->set('allow_local_apm_gateways', \true);
                $settings->persist();
            }
        });
        add_filter('woocommerce_paypal_payments_rest_common_merchant_features', static function (array $features) use ($c): array {
            $is_connected = $c->get('settings.flag.is-connected');
            if (!$is_connected) {
                return $features;
            }
            $reference_transaction_status = $c->get('api.reference-transaction-status');
            assert($reference_transaction_status instanceof ReferenceTransactionStatus);
            $dcc_product_status = $c->get('wcgateway.helper.dcc-product-status');
            assert($dcc_product_status instanceof DCCProductStatus);
            $dcc_applies = $c->get('api.helpers.dccapplies');
            $apms_product_status = $c->get('ppcp-local-apms.product-status');
            assert($apms_product_status instanceof LocalApmProductStatus);
            $installments_product_status = $c->get('wcgateway.installments-product-status');
            assert($installments_product_status instanceof InstallmentsProductStatus);
            $pwc_product_status = $c->get('wcgateway.pwc-product-status');
            assert($pwc_product_status instanceof PWCProductStatus);
            $contact_module_check = $c->get('wcgateway.contact-module.eligibility.check');
            assert(is_callable($contact_module_check));
            $pui_product_status = $c->get('wcgateway.pay-upon-invoice-product-status');
            assert($pui_product_status instanceof PayUponInvoiceProductStatus);
            $save_payment_methods_check = $c->get('save-payment-methods.eligibility.check');
            assert(is_callable($save_payment_methods_check));
            $features[FeaturesDefinition::FEATURE_SAVE_PAYPAL_AND_VENMO] = array('enabled' => $reference_transaction_status->reference_transaction_enabled() && $save_payment_methods_check());
            $features[FeaturesDefinition::FEATURE_ADVANCED_CREDIT_AND_DEBIT_CARDS] = array('enabled' => $dcc_product_status->is_active() && $dcc_applies->for_country_currency());
            $features[FeaturesDefinition::FEATURE_ALTERNATIVE_PAYMENT_METHODS] = array('enabled' => $apms_product_status->is_active());
            // When local APMs are available, then PayLater messaging is also available.
            $features[FeaturesDefinition::FEATURE_PAY_LATER_MESSAGING] = array('enabled' => $features[FeaturesDefinition::FEATURE_ALTERNATIVE_PAYMENT_METHODS]['enabled']);
            $features[FeaturesDefinition::FEATURE_INSTALLMENTS] = array('enabled' => $installments_product_status->is_active());
            $features[FeaturesDefinition::FEATURE_PAY_WITH_CRYPTO] = array('enabled' => $pwc_product_status->is_active());
            $features[FeaturesDefinition::FEATURE_CONTACT_MODULE] = array('enabled' => $contact_module_check());
            $features[FeaturesDefinition::FEATURE_PAY_UPON_INVOICE] = array('enabled' => $pui_product_status->is_active());
            return $features;
        });
        add_action('rest_api_init', static function () use ($c) {
            $endpoint = $c->get('wcgateway.shipping.callback.endpoint');
            assert($endpoint instanceof ShippingCallbackEndpoint);
            $endpoint->register();
        });
        return \true;
    }
    /**
     * Registers the payment gateways.
     *
     * @param ContainerInterface $container The container.
     */
    private function register_payment_gateways(ContainerInterface $container)
    {
        add_filter('woocommerce_payment_gateways', static function ($methods) use ($container): array {
            $paypal_gateway = $container->get('wcgateway.paypal-gateway');
            assert($paypal_gateway instanceof \WC_Payment_Gateway);
            $paypal_gateway_enabled = wc_string_to_bool($paypal_gateway->get_option('enabled'));
            $methods[] = $paypal_gateway;
            $settings = $container->get('wcgateway.settings');
            assert($settings instanceof ContainerInterface);
            $is_our_page = $container->get('wcgateway.is-plugin-settings-page');
            $is_connected = $container->get('settings.flag.is-connected');
            if (!$is_connected) {
                return $methods;
            }
            $dcc_configuration = $container->get('wcgateway.configuration.card-configuration');
            assert($dcc_configuration instanceof CardPaymentsConfiguration);
            $standard_card_button = get_option('woocommerce_ppcp-card-button-gateway_settings');
            if ($dcc_configuration->is_acdc_enabled() && isset($standard_card_button['enabled'])) {
                $standard_card_button['enabled'] = 'no';
                update_option('woocommerce_ppcp-card-button-gateway_settings', $standard_card_button);
            }
            $dcc_applies = $container->get('api.helpers.dccapplies');
            assert($dcc_applies instanceof DccApplies);
            $dcc_product_status = $container->get('wcgateway.helper.dcc-product-status');
            assert($dcc_product_status instanceof DCCProductStatus);
            if ($dcc_applies->for_country_currency() && ($is_our_page || $dcc_product_status->is_active())) {
                $methods[] = $container->get('wcgateway.credit-card-gateway');
            }
            if ($paypal_gateway_enabled && apply_filters('woocommerce_paypal_payments_card_button_gateway_should_register_gateway', $container->get('wcgateway.settings.allow_card_button_gateway'))) {
                $methods[] = $container->get('wcgateway.card-button-gateway');
            }
            return (array) $methods;
        });
        add_filter('woocommerce_available_payment_gateways', static function ($methods) use ($container): array {
            $disabler = $container->get('wcgateway.disabler');
            /**
             * The Gateway disabler.
             *
             * @var DisableGateways $disabler
             */
            return $disabler->handler((array) $methods);
        });
    }
    /**
     * Registers the authorize order functionality.
     *
     * @param ContainerInterface $container The container.
     */
    private function register_order_functionality(ContainerInterface $container)
    {
        add_filter('woocommerce_order_actions', static function ($order_actions) use ($container): array {
            global $theorder;
            if (!$theorder instanceof WC_Order) {
                return $order_actions;
            }
            $render_reauthorize = $container->get('wcgateway.admin.render-reauthorize-action');
            $render_authorize = $container->get('wcgateway.admin.render-authorize-action');
            // Renders the authorize action in the select field.
            return $render_reauthorize->render($render_authorize->render($order_actions, $theorder), $theorder);
        });
        add_action('woocommerce_order_action_ppcp_authorize_order', static function (WC_Order $wc_order) use ($container) {
            /**
             * The authorized payments processor.
             *
             * @var AuthorizedPaymentsProcessor $authorized_payments_processor
             */
            $authorized_payments_processor = $container->get('wcgateway.processor.authorized-payments');
            $authorized_payments_processor->capture_authorized_payment($wc_order);
        });
        add_action('woocommerce_order_action_ppcp_reauthorize_order', static function (WC_Order $wc_order) use ($container) {
            $admin_notices = $container->get('admin-notices.repository');
            assert($admin_notices instanceof Repository);
            /**
             * The authorized payments processor.
             *
             * @var AuthorizedPaymentsProcessor $authorized_payments_processor
             */
            $authorized_payments_processor = $container->get('wcgateway.processor.authorized-payments');
            if ($authorized_payments_processor->reauthorize_payment($wc_order) !== AuthorizedPaymentsProcessor::SUCCESSFUL) {
                $message = sprintf('%1$s %2$s', esc_html__('Reauthorization with PayPal failed: ', 'woocommerce-paypal-payments'), $authorized_payments_processor->reauthorization_failure_reason() ?: '');
                $admin_notices->persist(new Message($message, 'error'));
            } else {
                $admin_notices->persist(new Message('Payment reauthorized.', 'info'));
                $wc_order->add_order_note(__('Payment reauthorized.', 'woocommerce-paypal-payments'));
            }
        });
    }
    /**
     * Registers the additional columns on the order list page.
     *
     * @param ContainerInterface $container The container.
     */
    private function register_columns(ContainerInterface $container)
    {
        add_action('woocommerce_order_actions_start', static function ($wc_order_id) use ($container) {
            /**
             * The Payment Status Order Detail.
             *
             * @var PaymentStatusOrderDetail $class
             */
            $class = $container->get('wcgateway.admin.order-payment-status');
            $class->render(intval($wc_order_id));
        });
        add_filter('manage_edit-shop_order_columns', static function ($columns) use ($container) {
            /**
             * The Order Table Payment Status object.
             *
             * @var OrderTablePaymentStatusColumn $payment_status_column
             */
            $payment_status_column = $container->get('wcgateway.admin.orders-payment-status-column');
            return $payment_status_column->register($columns);
        });
        add_action('manage_shop_order_posts_custom_column', static function ($column, $wc_order_id) use ($container) {
            /**
             * The column object.
             *
             * @var OrderTablePaymentStatusColumn $payment_status_column
             */
            $payment_status_column = $container->get('wcgateway.admin.orders-payment-status-column');
            $payment_status_column->render((string) $column, intval($wc_order_id));
        }, 10, 2);
    }
    /**
     * Registers the PayPal Address preset to overwrite Shipping in checkout.
     *
     * @param ContainerInterface $container The container.
     */
    private function register_checkout_paypal_address_preset(ContainerInterface $container)
    {
        add_filter('woocommerce_checkout_get_value', static function (...$args) use ($container) {
            /**
             * Its important to not instantiate the service too early as it
             * depends on SessionHandler and WooCommerce Session.
             */
            /**
             * The CheckoutPayPalAddressPreset object.
             *
             * @var CheckoutPayPalAddressPreset $service
             */
            $service = $container->get('wcgateway.checkout.address-preset');
            return $service->filter_checkout_field(...$args);
        }, 10, 2);
    }
    /**
     * Registers the tasks inside "Things to do next" WC section.
     *
     * @param ContainerInterface $container The container.
     * @return void
     */
    protected function register_wc_tasks(ContainerInterface $container): void
    {
        add_action('init', static function () use ($container): void {
            $logger = $container->get('woocommerce.logger.woocommerce');
            assert($logger instanceof LoggerInterface);
            try {
                $simple_redirect_tasks = $container->get('wcgateway.settings.wc-tasks.simple-redirect-tasks');
                if (empty($simple_redirect_tasks)) {
                    return;
                }
                $task_registrar = $container->get('wcgateway.settings.wc-tasks.task-registrar');
                assert($task_registrar instanceof TaskRegistrarInterface);
                $task_registrar->register('extended', $simple_redirect_tasks);
            } catch (Exception $exception) {
                $logger->error("Failed to create a task in the 'Things to do next' section of WC. " . $exception->getMessage());
            }
        });
    }
    /**
     * Registers inbox notes in the WooCommerce Admin inbox section.
     */
    protected function register_woo_inbox_notes(ContainerInterface $container): void
    {
        add_action('admin_init', static function () use ($container): void {
            $logger = $container->get('woocommerce.logger.woocommerce');
            assert($logger instanceof LoggerInterface);
            $inbox_note_registrar = $container->get('wcgateway.settings.inbox-note-registrar');
            assert($inbox_note_registrar instanceof InboxNoteRegistrar);
            try {
                $inbox_note_registrar->register();
            } catch (Exception $exception) {
                $logger->error('Failed to add note to the WooCommerce inbox section. ' . $exception->getMessage());
            }
        });
    }
    /**
     * Registers the assets and ajax endpoint for the void button.
     *
     * @param ContainerInterface $container The container.
     */
    protected function register_void_button(ContainerInterface $container): void
    {
        add_action('admin_enqueue_scripts', static function () use ($container) {
            $assets = $container->get('wcgateway.void-button.assets');
            assert($assets instanceof VoidButtonAssets);
            if ($assets->should_register()) {
                $assets->register();
            }
        });
        add_action('wc_ajax_' . VoidOrderEndpoint::ENDPOINT, static function () use ($container) {
            $endpoint = $container->get('wcgateway.void-button.endpoint');
            assert($endpoint instanceof VoidOrderEndpoint);
            $endpoint->handle_request();
        });
    }
    /**
     * Checks, if the provided argument is a WC_Order which was paid directly by PayPal.
     *
     * Only considers direct PayPal payments, and returns false for orders that were paid "via"
     * PayPal, like wallets (Google Pay, ...) or local APMs.
     *
     * @param WC_Order|mixed $order The order to verify.
     * @return bool True, if it's a valid order that was paid via PayPal.
     */
    private function is_order_paid_by_paypal($order): bool
    {
        if (!$order instanceof WC_Order) {
            return \false;
        }
        if (!$order->get_meta(PayPalGateway::ORDER_PAYER_EMAIL_META_KEY)) {
            return \false;
        }
        if ('paypal' !== $order->get_meta(PayPalGateway::ORDER_PAYMENT_SOURCE_META_KEY)) {
            return \false;
        }
        return \false === strpos($order->get_payment_method_title(), '(via PayPal)');
    }
    /**
     * Enriches the payment method title with payment details
     * (payer email or card brand + last 4 digits) for supported gateways.
     */
    private function register_payment_method_title_enrichment(ContainerInterface $c): void
    {
        add_filter('woocommerce_order_get_payment_method_title', static function ($title, $order) use ($c) {
            if (!is_string($title) || !$order instanceof WC_Order) {
                return $title;
            }
            $enricher = $c->get('wcgateway.payment-method-title-enricher');
            return $enricher->enrich($title, $order);
        }, 10, 2);
    }
    /**
     * Overwrite WC order email/phone.
     *
     * The contact module can provide a custom email and phone number.
     * These two properties are intended to be treated as primary contact details.
     */
    private function register_contact_handlers(): void
    {
        $set_order_contacts = function (WC_Order $wc_order): void {
            $email = $wc_order->get_meta(PayPalGateway::CONTACT_EMAIL_META_KEY);
            $phone = $wc_order->get_meta(PayPalGateway::CONTACT_PHONE_META_KEY);
            if ($email && is_email($email)) {
                $wc_order->set_billing_email($email);
            }
            if ($phone && is_string($phone)) {
                $wc_order->set_billing_phone($phone);
            }
        };
        add_action('woocommerce_paypal_payments_contacts_added', function (WC_Order $wc_order) use ($set_order_contacts): void {
            $set_order_contacts($wc_order);
        });
        // There is a race condition in express block checkout, the contacts set in woocommerce_paypal_payments_contacts_added
        // may get reverted by another ajax WC request. So we set them again after that.
        add_action('woocommerce_store_api_cart_update_order_from_request', function (WC_Order $wc_order) use ($set_order_contacts): void {
            // This hook fires for all methods, but we do not do anything if the meta is not set
            // so probably no need to add additional checks.
            $set_order_contacts($wc_order);
        });
    }
    /**
     * Ensures PayPal handles block-checkout express payments even when another PCP gateway is sorted first in WC Settings → Payments.
     */
    private function register_block_express_payment_method_handler(ContainerInterface $c): void
    {
        add_action('woocommerce_rest_checkout_process_payment_with_context', function ($context) use ($c): void {
            $payment_data = (array) ($context->payment_data ?? array());
            if (empty($payment_data['paypal_order_id'])) {
                return;
            }
            if (($context->payment_method ?? '') === PayPalGateway::ID) {
                return;
            }
            $available = WC()->payment_gateways->get_available_payment_gateways();
            if (!isset($available[PayPalGateway::ID])) {
                return;
            }
            $context->set_payment_method(PayPalGateway::ID);
            $order = $context->order ?? null;
            if ($order instanceof WC_Order) {
                $order->set_payment_method(PayPalGateway::ID);
                $session_handler = $c->get('session.handler');
                $funding_source_renderer = $c->get('wcgateway.funding-source.renderer');
                $funding_source = $payment_data['funding_source'] ?: ($session_handler->funding_source() ?: 'paypal');
                $order->set_payment_method_title($funding_source_renderer->render_name($funding_source));
                $order->save();
            }
        }, 100, 1);
        add_action('woocommerce_before_order_object_save', function ($order): void {
            if (!$order instanceof WC_Order) {
                return;
            }
            if (!$order->get_meta(PayPalGateway::ORDER_ID_META_KEY)) {
                return;
            }
            $payment_method = $order->get_payment_method();
            if ($payment_method && strpos($payment_method, 'ppcp-') === 0) {
                return;
            }
            if ($order->get_payment_method() === CreditCardGateway::ID) {
                return;
            }
            $order->set_payment_method(PayPalGateway::ID);
        }, 10, 1);
    }
    /**
     * Inserts custom fields into the order-detail view.
     *
     * @param mixed $fields The field-list provided by WooCommerce, should be an array.
     * @return array|mixed The filtered field list.
     *
     * @psalm-suppress MissingClosureParamType
     */
    private function insert_custom_fields_into_order_details($fields)
    {
        global $theorder;
        if (!is_array($fields)) {
            return $fields;
        }
        if (!$this->is_order_paid_by_paypal($theorder)) {
            return $fields;
        }
        /**
         * We use this filter to de-customize the order details - 'billing' and 'shipping' section.
         */
        if (!apply_filters('woocommerce_paypal_payments_order_details_show_paypal_email', \true)) {
            return $fields;
        }
        $email = $theorder->get_meta(PayPalGateway::ORDER_PAYER_EMAIL_META_KEY) ?: '';
        $fields['paypal_email'] = array('label' => __('PayPal email address', 'woocommerce-paypal-payments'), 'value' => $email, 'wrapper_class' => 'form-field-wide', 'custom_attributes' => array('disabled' => 'disabled'));
        return $fields;
    }
    /**
     * Displays a custom section in the order details page with the original contact details entered
     * during checkout.
     *
     * When the Contact module is active, those contact details are replaced with details provided
     * by PayPal; this section shows the (unused) details which the user originally entered.
     *
     * @param WC_Order|mixed $order The order which is rendered.
     * @return void
     */
    private function display_original_contact_in_order_details($order): void
    {
        if (!$this->is_order_paid_by_paypal($order)) {
            return;
        }
        if (!apply_filters('woocommerce_paypal_payments_order_details_show_original_contact', \true)) {
            return;
        }
        assert($order instanceof WC_Order);
        $contact_email = $order->get_meta(PayPalGateway::ORIGINAL_EMAIL_META_KEY);
        $contact_phone = $order->get_meta(PayPalGateway::ORIGINAL_PHONE_META_KEY);
        if (!$contact_email && !$contact_phone) {
            return;
        }
        ?>
		<div class="ppcp-original-contact-data address" style="clear:both">
			<h3>
				<?php 
        esc_html_e('Other', 'woocommerce-paypal-payments');
        ?>
				<span
					class="woocommerce-help-tip alignright" tabindex="0"
					data-tip="<?php 
        esc_attr_e('The customer entered these contact details during checkout, but provided different details in the PayPal popup. These details are kept for reference only.', 'woocommerce-paypal-payments');
        ?>"
				></span>
			</h3>
			<?php 
        if (!empty($contact_email)) {
            ?>
				<p>
					<strong>
						<?php 
            esc_html_e('Email address', 'woocommerce-paypal-payments');
            ?>:
					</strong>
					<a href="<?php 
            echo esc_url('mailto:' . $contact_email);
            ?>"><?php 
            echo esc_html($contact_email);
            ?></a>
				</p>
			<?php 
        }
        ?>
			<?php 
        if (!empty($contact_phone)) {
            ?>
				<p>
					<strong>
						<?php 
            esc_html_e('Phone', 'woocommerce-paypal-payments');
            ?>:
					</strong>
					<?php 
            echo wc_make_phone_clickable($contact_phone);
            ?>
				</p>
			<?php 
        }
        ?>
		</div>
		<?php 
    }
}
