<?php

/**
 * Generator service to build URLs to sign in to a PayPal account.
 *
 * @package WooCommerce\PayPalCommerce\Settings\Service
 */
declare (strict_types=1);
namespace WooCommerce\PayPalCommerce\Settings\Service;

use Exception;
use RuntimeException;
use WooCommerce\PayPalCommerce\Vendor\Psr\Log\LoggerInterface;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnerReferrals;
use WooCommerce\PayPalCommerce\ApiClient\Repository\PartnerReferralsData;
use WooCommerce\WooCommerce\Logging\Logger\NullLogger;
use WooCommerce\PayPalCommerce\WcGateway\Helper\EnvironmentConfig;
/**
 * Generator that builds the ISU connection URL.
 */
class ConnectionUrlGenerator
{
    /**
     * The partner referrals endpoint.
     *
     * @var EnvironmentConfig<PartnerReferrals>
     */
    protected EnvironmentConfig $partner_referrals;
    /**
     * The default partner referrals data.
     *
     * @var PartnerReferralsData
     */
    protected PartnerReferralsData $referrals_data;
    /**
     * Manages access to OnboardingUrl instances
     *
     * @var OnboardingUrlManager
     */
    protected \WooCommerce\PayPalCommerce\Settings\Service\OnboardingUrlManager $url_manager;
    /**
     * The logger
     *
     * @var LoggerInterface
     */
    private LoggerInterface $logger;
    /**
     * Constructor for the ConnectionUrlGenerator class.
     *
     * Initializes the cache and logger properties of the class.
     *
     * @param EnvironmentConfig<PartnerReferrals> $partner_referrals PartnerReferrals for URL generation.
     * @param PartnerReferralsData                $referrals_data    Default partner referrals data.
     * @param OnboardingUrlManager                $url_manager       Manages access to OnboardingUrl instances.
     * @param ?LoggerInterface                    $logger            The logger object for logging messages.
     *
     *  phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
     */
    public function __construct(EnvironmentConfig $partner_referrals, PartnerReferralsData $referrals_data, \WooCommerce\PayPalCommerce\Settings\Service\OnboardingUrlManager $url_manager, ?LoggerInterface $logger = null)
    {
        $this->partner_referrals = $partner_referrals;
        $this->referrals_data = $referrals_data;
        $this->url_manager = $url_manager;
        $this->logger = $logger ?: new NullLogger();
    }
    /**
     * Generates a PayPal onboarding URL for merchant sign-up.
     *
     * This function creates a URL for merchants to sign up for PayPal services.
     * It handles caching of the URL, generation of new URLs when necessary,
     * and works for both production and sandbox environments.
     *
     * @param array $products    An array of product identifiers to include in the sign-up process.
     *                           These determine the PayPal onboarding experience.
     * @param array $flags       Onboarding choices that will customize the ISU payload.
     * @param bool  $use_sandbox Whether to generate a sandbox URL.
     *
     * @return string The generated PayPal onboarding URL.
     * @throws RuntimeException If a new onboarding URL cannot be generated.
     */
    public function generate(array $products = array(), array $flags = array(), bool $use_sandbox = \false): string
    {
        $cache_key = $this->cache_key($products, $flags, $use_sandbox);
        $user_id = get_current_user_id();
        $onboarding_url = $this->url_manager->get($cache_key, $user_id);
        $cached_url = $this->try_get_from_cache($onboarding_url, $cache_key);
        if ($cached_url) {
            $this->logger->info('Using cached onboarding URL for: ' . $cache_key);
            return $cached_url;
        }
        $this->logger->info('Generating onboarding URL for: ' . $cache_key);
        $url = $this->generate_new_url($use_sandbox, $products, $flags, $onboarding_url, $cache_key);
        if ($url) {
            $this->persist_url($onboarding_url, $url);
        }
        return $url;
    }
    /**
     * Generates a cache key from the environment and sorted product array.
     *
     * Q: Why do we cache the connection URL?
     * A: The URL is generated by a partner-referrals API, i.e. it requires a
     *    remote request; caching the response avoids unnecessary API calls.
     *
     * @param array $products    Product identifiers that are part of the cache key.
     * @param array $flags       Onboarding flags.
     * @param bool  $for_sandbox Whether the cache contains a sandbox URL.
     *
     * @return string The cache key, defining the product list and environment.
     */
    protected function cache_key(array $products, array $flags, bool $for_sandbox): string
    {
        $environment = $for_sandbox ? 'sandbox' : 'production';
        // Sort products alphabetically, to improve cache implementation.
        sort($products);
        // Extract the names of active flags.
        $active_flags = array_keys(array_filter($flags));
        return strtolower($environment . '-' . implode('-', $products) . '-' . implode('-', $active_flags));
    }
    /**
     * Attempts to load the URL from cache.
     *
     * @param OnboardingUrl $onboarding_url The OnboardingUrl object.
     * @param string        $cache_key      The cache key.
     *
     * @return string The cached URL, or an empty string if no URL is found.
     */
    protected function try_get_from_cache(\WooCommerce\PayPalCommerce\Settings\Service\OnboardingUrl $onboarding_url, string $cache_key): string
    {
        if ($onboarding_url->load()) {
            $this->logger->debug('Loaded onboarding URL from cache: ' . $cache_key);
            /**
             * Filters the cached onboarding URL. Used for cache control
             * when testing or development.
             */
            return apply_filters('woocommerce_paypal_payments_cached_onboarding_url', $onboarding_url->get_onboarding_url(), $onboarding_url);
        }
        return '';
    }
    /**
     * Generates a new URL.
     *
     * @param bool          $for_sandbox    Whether to generate a sandbox URL.
     * @param array         $products       The products array.
     * @param array         $flags          Onboarding flags.
     * @param OnboardingUrl $onboarding_url The OnboardingUrl object.
     * @param string        $cache_key      The cache key.
     *
     * @return string The generated URL.
     * @throws RuntimeException If the onboarding token or signup URL cannot be generated.
     */
    protected function generate_new_url(bool $for_sandbox, array $products, array $flags, \WooCommerce\PayPalCommerce\Settings\Service\OnboardingUrl $onboarding_url, string $cache_key): string
    {
        $query_args = array('displayMode' => 'minibrowser');
        $onboarding_url->init();
        $onboarding_token = $onboarding_url->onboarding_token();
        $seller_nonce = $onboarding_url->seller_nonce();
        if (!$onboarding_token) {
            throw new RuntimeException('Could not generate an onboarding token for: ' . $cache_key);
        }
        $data = $this->prepare_referral_data($products, $flags, $onboarding_token, $seller_nonce);
        try {
            $referral = $this->partner_referrals->get_value($for_sandbox);
            $url = $referral->signup_link($data);
        } catch (Exception $e) {
            throw new RuntimeException('Could not generate an onboarding URL for: ' . $cache_key . ': ' . $e->getMessage(), 0, $e);
        }
        return add_query_arg($query_args, $url);
    }
    /**
     * Prepares the referral data.
     *
     * @param array  $products         The products array.
     * @param array  $flags            Onboarding flags.
     * @param string $onboarding_token The onboarding token.
     *
     * @return array The prepared referral data.
     */
    protected function prepare_referral_data(array $products, array $flags, string $onboarding_token, string $seller_nonce = ''): array
    {
        return $this->referrals_data->data($products, $onboarding_token, (bool) ($flags['useSubscriptions'] ?? \false), (bool) ($flags['useCardPayments'] ?? \false), $seller_nonce);
    }
    /**
     * Persists the generated URL.
     *
     * @param OnboardingUrl $onboarding_url The OnboardingUrl object.
     * @param string        $url            The URL to persist.
     */
    protected function persist_url(\WooCommerce\PayPalCommerce\Settings\Service\OnboardingUrl $onboarding_url, string $url): void
    {
        $onboarding_url->set_onboarding_url($url);
        $onboarding_url->persist();
    }
}
