<?php

/**
 * The save payment methods module.
 *
 * @package WooCommerce\PayPalCommerce\Applepay
 */
declare (strict_types=1);
namespace WooCommerce\PayPalCommerce\SavePaymentMethods;

use WooCommerce\PayPalCommerce\Vendor\Psr\Log\LoggerInterface;
use WC_Order;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\UserIdToken;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentSource;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\ApiClient\Helper\ReferenceTransactionStatus;
use WooCommerce\PayPalCommerce\Assets\AssetGetter;
use WooCommerce\PayPalCommerce\Button\Helper\Context;
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentToken;
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentTokenForGuest;
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreateSetupToken;
use WooCommerce\PayPalCommerce\WcPaymentTokens\WooCommercePaymentTokens;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
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\Settings\Data\SettingsModel;
use WooCommerce\PayPalCommerce\Settings\Data\SettingsProvider;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WooCommerce\PayPalCommerce\WcSubscriptions\Endpoint\SubscriptionChangePaymentMethod;
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
/**
 * Class SavePaymentMethodsModule
 */
class SavePaymentMethodsModule implements ServiceModule, ExecutableModule
{
    use ModuleClassNameIdTrait;
    /**
     * {@inheritDoc}
     */
    public function services(): array
    {
        return require __DIR__ . '/../services.php';
    }
    /**
     * {@inheritDoc}
     */
    public function run(ContainerInterface $c): bool
    {
        if (!$c->get('save-payment-methods.eligible')) {
            return \true;
        }
        add_action('woocommerce_paypal_payments_gateway_migrate_on_update', function () use ($c) {
            $settings_model = $c->get('settings.data.settings');
            assert($settings_model instanceof SettingsModel);
            $reference_transaction_status = $c->get('api.reference-transaction-status');
            assert($reference_transaction_status instanceof ReferenceTransactionStatus);
            if (!$reference_transaction_status->reference_transaction_enabled()) {
                $settings_model->set_save_paypal_and_venmo(\false);
                $settings_model->save();
            }
        });
        add_action('after_setup_theme', function () use ($c) {
            $settings_provider = $c->get('settings.settings-provider');
            assert($settings_provider instanceof SettingsProvider);
            if (!$settings_provider->save_paypal_and_venmo() && !$settings_provider->save_card_details()) {
                return \true;
            }
            add_filter('woocommerce_paypal_payments_localized_script_data', function (array $localized_script_data) use ($c) {
                $subscriptions_helper = $c->get('wc-subscriptions.helper');
                assert($subscriptions_helper instanceof SubscriptionHelper);
                if (!is_user_logged_in() && !$subscriptions_helper->cart_contains_subscription()) {
                    return $localized_script_data;
                }
                $api = $c->get('api.user-id-token');
                assert($api instanceof UserIdToken);
                $logger = $c->get('woocommerce.logger.woocommerce');
                assert($logger instanceof LoggerInterface);
                return $this->add_id_token_to_script_data($api, $logger, $localized_script_data);
            });
            // Adds attributes needed to save payment method.
            add_filter('ppcp_create_order_request_body_data', function (array $data, string $payment_method, array $request_data) use ($c): array {
                $settings_provider = $c->get('settings.settings-provider');
                assert($settings_provider instanceof SettingsProvider);
                $new_attributes = array('vault' => array('store_in_vault' => 'ON_SUCCESS'));
                $target_customer_id = get_user_meta(get_current_user_id(), '_ppcp_target_customer_id', \true);
                if (!$target_customer_id) {
                    $target_customer_id = get_user_meta(get_current_user_id(), 'ppcp_customer_id', \true);
                }
                if ($target_customer_id) {
                    $new_attributes['customer'] = array('id' => $target_customer_id);
                }
                $funding_source = (string) ($request_data['funding_source'] ?? '');
                if ($payment_method === CreditCardGateway::ID) {
                    if (!$settings_provider->save_card_details()) {
                        return $data;
                    }
                    $save_payment_method = $request_data['save_payment_method'] ?? \false;
                    if (!$save_payment_method) {
                        return $data;
                    }
                } elseif ($payment_method === PayPalGateway::ID) {
                    if (!$settings_provider->save_paypal_and_venmo()) {
                        return $data;
                    }
                    if (!in_array($funding_source, array('paypal', 'venmo'), \true)) {
                        return $data;
                    }
                    $new_attributes['vault']['usage_type'] = 'MERCHANT';
                    $new_attributes['vault']['permit_multiple_payment_tokens'] = apply_filters('woocommerce_paypal_payments_permit_multiple_payment_tokens', \false);
                } else {
                    return $data;
                }
                $payment_source = (array) ($data['payment_source'] ?? array());
                $key = array_key_first($payment_source);
                if (!is_string($key) || empty($key)) {
                    $key = $payment_method;
                    if ($payment_method === PayPalGateway::ID && $funding_source) {
                        $key = $funding_source;
                    }
                    $payment_source[$key] = array();
                }
                $payment_source[$key] = (array) $payment_source[$key];
                $attributes = (array) ($payment_source[$key]['attributes'] ?? array());
                $payment_source[$key]['attributes'] = array_merge($attributes, $new_attributes);
                $data['payment_source'] = $payment_source;
                return $data;
            }, 20, 3);
            add_action('woocommerce_paypal_payments_after_order_processor', function (WC_Order $wc_order, Order $order) use ($c) {
                $payment_source = $order->payment_source();
                assert($payment_source instanceof PaymentSource);
                $payment_vault_attributes = $payment_source->properties()->attributes->vault ?? null;
                if ($payment_vault_attributes) {
                    $customer_id = $payment_vault_attributes->customer->id ?? '';
                    $token_id = $payment_vault_attributes->id ?? '';
                    if (!$customer_id || !$token_id) {
                        return;
                    }
                    update_user_meta($wc_order->get_customer_id(), '_ppcp_target_customer_id', $customer_id);
                    $wc_payment_tokens = $c->get('wc-payment-tokens.wc-payment-tokens');
                    assert($wc_payment_tokens instanceof WooCommercePaymentTokens);
                    try {
                        if ($wc_order->get_payment_method() === CreditCardGateway::ID) {
                            $wc_payment_tokens->create_payment_token_card($wc_order->get_customer_id(), (object) array('id' => $token_id, 'payment_source' => (object) array('card' => $payment_source->properties())));
                        }
                        if ($wc_order->get_payment_method() === PayPalGateway::ID) {
                            switch ($payment_source->name()) {
                                case 'venmo':
                                    $wc_payment_tokens->create_payment_token_venmo($wc_order->get_customer_id(), $token_id, $payment_source->properties()->email_address ?? '');
                                    break;
                                case 'apple_pay':
                                    $wc_payment_tokens->create_payment_token_applepay($wc_order->get_customer_id(), $token_id);
                                    break;
                                case 'card':
                                    $wc_payment_tokens->create_payment_token_card($wc_order->get_customer_id(), (object) array('id' => $token_id, 'payment_source' => (object) array('card' => $payment_source->properties())));
                                    break;
                                case 'paypal':
                                default:
                                    $wc_payment_tokens->create_payment_token_paypal($wc_order->get_customer_id(), $token_id, $payment_source->properties()->email_address ?? '');
                                    break;
                            }
                        }
                    } catch (\Exception $exception) {
                        $logger = $c->get('woocommerce.logger.woocommerce');
                        assert($logger instanceof LoggerInterface);
                        $logger->warning('Failed to save payment token for order: ' . $exception->getMessage(), array('order_id' => $wc_order->get_id(), 'customer_id' => $customer_id, 'token_id' => $token_id, 'exception' => $exception));
                    }
                }
            }, 10, 2);
            add_filter('woocommerce_paypal_payments_disable_add_payment_method', '__return_false');
            add_filter('woocommerce_paypal_payments_should_render_card_custom_fields', '__return_false');
            add_action('wp_enqueue_scripts', function () use ($c) {
                $context = $c->get('button.helper.context');
                assert($context instanceof Context);
                if (!is_user_logged_in() || !($context->is_add_payment_method_page() || $context->is_subscription_change_payment_method_page())) {
                    return;
                }
                $asset_getter = $c->get('save-payment-methods.asset_getter');
                assert($asset_getter instanceof AssetGetter);
                wp_enqueue_script('ppcp-add-payment-method', $asset_getter->get_asset_url('add-payment-method.js'), array('jquery'), $c->get('ppcp.asset-version'), \true);
                $api = $c->get('api.user-id-token');
                assert($api instanceof UserIdToken);
                try {
                    $target_customer_id = get_user_meta(get_current_user_id(), '_ppcp_target_customer_id', \true);
                    if (!$target_customer_id) {
                        $target_customer_id = get_user_meta(get_current_user_id(), 'ppcp_customer_id', \true);
                    }
                    $id_token = $api->id_token($target_customer_id);
                    $settings_provider = $c->get('settings.settings-provider');
                    assert($settings_provider instanceof SettingsProvider);
                    $verification_method = apply_filters('woocommerce_paypal_payments_three_d_secure_contingency', $settings_provider->three_d_secure_enum());
                    // phpcs:ignore WordPress.Security.NonceVerification
                    $change_payment_method = wc_clean(wp_unslash($_GET['change_payment_method'] ?? ''));
                    $is_subscription_change_payment_method_page = $context->is_subscription_change_payment_method_page();
                    $add_payment_method_data = array('client_id' => $c->get('button.client_id'), 'merchant_id' => $c->get('api.merchant_id'), 'id_token' => $id_token, 'payment_methods_page' => wc_get_account_endpoint_url('payment-methods'), 'view_subscriptions_page' => wc_get_account_endpoint_url('view-subscription'), 'is_subscription_change_payment_page' => $is_subscription_change_payment_method_page, 'subscription_id_to_change_payment' => $is_subscription_change_payment_method_page ? (int) $change_payment_method : 0, 'error_message' => __('Could not save payment method.', 'woocommerce-paypal-payments'), 'verification_method' => $verification_method, 'script_attributes' => (object) array(), 'user' => array('is_logged' => is_user_logged_in()), 'ajax' => array('create_setup_token' => array('endpoint' => \WC_AJAX::get_endpoint(CreateSetupToken::ENDPOINT), 'nonce' => wp_create_nonce(CreateSetupToken::nonce())), 'create_payment_token' => array('endpoint' => \WC_AJAX::get_endpoint(CreatePaymentToken::ENDPOINT), 'nonce' => wp_create_nonce(CreatePaymentToken::nonce())), 'subscription_change_payment_method' => array('endpoint' => \WC_AJAX::get_endpoint(SubscriptionChangePaymentMethod::ENDPOINT), 'nonce' => wp_create_nonce(SubscriptionChangePaymentMethod::nonce()))), 'labels' => array('error' => array('generic' => __('Something went wrong. Please try again or choose another payment source.', 'woocommerce-paypal-payments'))));
                    /**
                     * Filters the data localized for the Add Payment Method / Subscription
                     * Change Payment Method pages. Mirrors the equivalent filter for the
                     * checkout (`woocommerce_paypal_payments_localized_script_data`) so
                     * stage overrides (e.g. `script_attributes.sdkBaseUrl`) can be applied
                     * without touching the module.
                     */
                    $add_payment_method_data = apply_filters('woocommerce_paypal_payments_add_payment_method_localized_script_data', $add_payment_method_data);
                    wp_localize_script('ppcp-add-payment-method', 'ppcp_add_payment_method', $add_payment_method_data);
                } catch (RuntimeException $exception) {
                    $logger = $c->get('woocommerce.logger.woocommerce');
                    assert($logger instanceof LoggerInterface);
                    $error = $exception->getMessage();
                    if ($exception instanceof PayPalApiException) {
                        $error = $exception->get_details($error);
                    }
                    $logger->error($error);
                }
            });
            /**
             * Displays the PayPal button on the Add Payment Method page.
             */
            add_action('woocommerce_add_payment_method_form_bottom', function () {
                if (!is_user_logged_in() || !is_add_payment_method_page()) {
                    return;
                }
                echo '<div id="ppc-button-' . esc_attr(PayPalGateway::ID) . '-save-payment-method"></div>';
            });
            add_action('wc_ajax_' . CreateSetupToken::ENDPOINT, static function () use ($c) {
                $endpoint = $c->get('save-payment-methods.endpoint.create-setup-token');
                assert($endpoint instanceof CreateSetupToken);
                $endpoint->handle_request();
            });
            add_action('wc_ajax_' . CreatePaymentToken::ENDPOINT, static function () use ($c) {
                $endpoint = $c->get('save-payment-methods.endpoint.create-payment-token');
                assert($endpoint instanceof CreatePaymentToken);
                $endpoint->handle_request();
            });
            add_action('wc_ajax_' . CreatePaymentTokenForGuest::ENDPOINT, static function () use ($c) {
                $endpoint = $c->get('save-payment-methods.endpoint.create-payment-token-for-guest');
                assert($endpoint instanceof CreatePaymentTokenForGuest);
                $endpoint->handle_request();
            });
            add_action('woocommerce_paypal_payments_before_delete_payment_token', function (string $token_id) use ($c) {
                try {
                    $endpoint = $c->get('api.endpoint.payment-tokens');
                    assert($endpoint instanceof PaymentTokensEndpoint);
                    $endpoint->delete($token_id);
                } catch (RuntimeException $exception) {
                    $logger = $c->get('woocommerce.logger.woocommerce');
                    assert($logger instanceof LoggerInterface);
                    $error = $exception->getMessage();
                    if ($exception instanceof PayPalApiException) {
                        $error = $exception->get_details($error);
                    }
                    $logger->error($error);
                }
            });
            add_filter('woocommerce_paypal_payments_credit_card_gateway_supports', function (array $supports) use ($c): array {
                if (!$c->get('save-payment-methods.eligible')) {
                    return $supports;
                }
                $settings_provider = $c->get('settings.settings-provider');
                assert($settings_provider instanceof SettingsProvider);
                if ($settings_provider->save_card_details()) {
                    $supports[] = 'tokenization';
                    $supports[] = 'add_payment_method';
                }
                return $supports;
            });
            add_filter('woocommerce_paypal_payments_save_payment_methods_eligible', function () {
                return \true;
            });
        });
        return \true;
    }
    /**
     * Adds id token to localized script data.
     *
     * @param UserIdToken     $api User id token api.
     * @param LoggerInterface $logger The logger.
     * @param array           $localized_script_data The localized script data.
     * @return array
     */
    private function add_id_token_to_script_data(UserIdToken $api, LoggerInterface $logger, array $localized_script_data): array
    {
        try {
            $target_customer_id = '';
            if (is_user_logged_in()) {
                $target_customer_id = get_user_meta(get_current_user_id(), '_ppcp_target_customer_id', \true);
                if (!$target_customer_id) {
                    $target_customer_id = get_user_meta(get_current_user_id(), 'ppcp_customer_id', \true);
                }
            }
            $id_token = $api->id_token($target_customer_id);
            $localized_script_data['save_payment_methods'] = array('id_token' => $id_token);
            $localized_script_data['data_client_id']['set_attribute'] = \false;
        } catch (RuntimeException $exception) {
            $error = $exception->getMessage();
            if ($exception instanceof PayPalApiException) {
                $error = $exception->get_details($error);
            }
            $logger->error($error);
        }
        return $localized_script_data;
    }
}
