<?php

declare (strict_types=1);
namespace WooCommerce\PayPalCommerce\StoreSync\Registration;

use Firebase\JWT\JWT;
use Exception;
use WP_Error;
use WooCommerce\PayPalCommerce\Vendor\Psr\Log\LoggerInterface;
use WooCommerce\PayPalCommerce\StoreSync\Merchant\MerchantMetadataProvider;
use WooCommerce\PayPalCommerce\StoreSync\Config\AgenticWebhookConfiguration;
class RegistrationService
{
    private const REGISTRATION_TOKEN_KEY = 'ppcp_agentic_registration_token';
    private const ERROR_REGISTRATION_FAILED = 'registration_failed';
    private const ERROR_WEBHOOK_REQUEST = 'webhook_request_failed';
    private const ERROR_WEBHOOK_RESPONSE = 'webhook_response_failed';
    private AgenticWebhookConfiguration $webhook_urls;
    private MerchantMetadataProvider $metadata_provider;
    private LoggerInterface $logger;
    public function __construct(AgenticWebhookConfiguration $webhook_urls, MerchantMetadataProvider $metadata_provider, LoggerInterface $logger)
    {
        $this->webhook_urls = $webhook_urls;
        $this->metadata_provider = $metadata_provider;
        $this->logger = $logger;
    }
    /**
     * Register this store with PayPal Agentic Commerce.
     *
     * @return RegistrationResult|WP_Error
     */
    public function register()
    {
        if ($this->is_registered()) {
            return new WP_Error(self::ERROR_REGISTRATION_FAILED, 'Already registered');
        }
        $token = $this->create_token();
        $result = $this->call_installation_endpoint($token);
        if (is_wp_error($result)) {
            $this->logger->error('Registration failed: Endpoint returned error', array('endpoint' => $this->webhook_urls->get_registration_install_url(), 'error_code' => $result->get_error_code(), 'error_message' => $result->get_error_message()));
            return $result;
        }
        if ($result->success) {
            $this->save_registration_token($token);
            do_action('woocommerce_paypal_payments_store_sync_registered');
        } else {
            $this->logger->error('Registration failed: Endpoint rejected registration', array('endpoint' => $this->webhook_urls->get_registration_install_url(), 'error' => $result->error ?? 'Registration failed', 'message' => $result->message, 'payload' => $this->metadata_provider->get_metadata()));
            return new WP_Error(self::ERROR_REGISTRATION_FAILED, $result->error ?? 'Registration failed');
        }
        return $result;
    }
    /**
     * Deregister store from PayPal Agentic Commerce.
     *
     * Deregistration is fire-and-forget: the local token is always removed and the
     * deregistered action always fires, regardless of the endpoint's response. A
     * rejection (e.g. "store not found") means the store is already gone on the
     * endpoint's side, which is the desired outcome, not a reason to retry. Failures
     * are still logged.
     *
     * @return RegistrationResult|WP_Error|null Null if the store was not registered.
     */
    public function deregister()
    {
        if (!$this->is_registered()) {
            return null;
        }
        $token = (string) $this->get_registration_token();
        $result = $this->call_uninstallation_endpoint($token);
        if (is_wp_error($result)) {
            $this->logger->error('Deregistration failed: Endpoint returned error', array('endpoint' => $this->webhook_urls->get_registration_uninstall_url(), 'error_code' => $result->get_error_code(), 'error_message' => $result->get_error_message()));
        } elseif (!$result->success) {
            $this->logger->error('Deregistration failed: Endpoint rejected deregistration', array('endpoint' => $this->webhook_urls->get_registration_uninstall_url(), 'error' => $result->error ?? 'Deregistration failed', 'message' => $result->message));
        }
        $this->delete_registration_token();
        do_action('woocommerce_paypal_payments_store_sync_deregistered');
        return $result;
    }
    /**
     * Checks if the current store is registered to support PayPal Agentic Commerce.
     *
     * @return bool
     */
    public function is_registered(): bool
    {
        return (bool) $this->get_registration_token();
    }
    /**
     * Returns the store data used to register the store with PayPal Agentic Commerce.
     *
     * @return null|array
     */
    public function get_registration_data(): ?array
    {
        $jwt_token = $this->get_registration_token();
        if (!$jwt_token) {
            return null;
        }
        $parts = explode('.', $jwt_token);
        // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode -- Partially decode a token, no obfuscation.
        $body = base64_decode($parts[1]);
        if (!$body) {
            return null;
        }
        try {
            return json_decode($body, \true, 512, \JSON_THROW_ON_ERROR);
        } catch (Exception $exception) {
            return null;
        }
    }
    /**
     * Create a JWT token with store metadata.
     *
     * The token is signed with a dummy key (HS256) as PayPal does not validate
     * the signature - it only serves as a transport mechanism for store metadata.
     */
    private function create_token(): string
    {
        $metadata = $this->metadata_provider->get_metadata();
        $payload = array('storeName' => $metadata->store_name, 'storeUrl' => $metadata->store_url, 'apiBaseUrl' => $metadata->api_base_url, 'country' => $metadata->store_country, 'currency' => $metadata->currency, 'paypalMerchantId' => $metadata->paypal_merchant_id, 'wooSydeCommerceId' => $metadata->store_url, 'catalogDownloadUrl' => $metadata->catalog_url, 'favIcon' => '', 'shippingCountries' => array('US'));
        return JWT::encode($payload, 'no-signature', 'HS256');
    }
    /**
     * Call the "installation" (registration) endpoint.
     *
     * @param string $token JWT token with store metadata.
     * @return RegistrationResult|WP_Error
     */
    private function call_installation_endpoint(string $token)
    {
        return $this->webhook_call($token, $this->webhook_urls->get_registration_install_url());
    }
    /**
     * Call the "uninstallation" (deregistration) endpoint.
     *
     * @param string $token Previously generated registration token.
     * @return RegistrationResult|WP_Error
     */
    private function call_uninstallation_endpoint(string $token)
    {
        return $this->webhook_call($token, $this->webhook_urls->get_registration_uninstall_url());
    }
    /**
     * Make a call to PayPal's webhook endpoints.
     *
     * @param string $token       JWT token with store metadata.
     * @param string $webhook_url The absolute webhook URL to call.
     * @return RegistrationResult|WP_Error
     */
    private function webhook_call(string $token, string $webhook_url)
    {
        $response = wp_remote_post($webhook_url, array('body' => $token, 'headers' => array('Content-Type' => 'text/plain')));
        if (is_wp_error($response)) {
            $this->logger->error('Webhook request failed: HTTP request error', array('endpoint' => $webhook_url, 'error_code' => $response->get_error_code(), 'error_message' => $response->get_error_message()));
            return new WP_Error(self::ERROR_WEBHOOK_REQUEST, $response->get_error_message());
        }
        $status_code = wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);
        try {
            $body = json_decode($body, \true, 512, \JSON_THROW_ON_ERROR);
        } catch (Exception $exception) {
            $this->logger->error('Webhook response parse failed: Invalid JSON received from endpoint', array('endpoint' => $webhook_url, 'http_status_code' => $status_code, 'exception_message' => $exception->getMessage(), 'raw_response_body' => $body));
            return new WP_Error(self::ERROR_WEBHOOK_RESPONSE, $exception->getMessage());
        }
        return new \WooCommerce\PayPalCommerce\StoreSync\Registration\RegistrationResult($body['success'] ?? \false, $body['message'] ?? '', $body['error'] ?? null);
    }
    /**
     * Return the previously stored registration token.
     *
     * Protected to allow mocking in tests.
     *
     * @return string|false
     */
    protected function get_registration_token()
    {
        return get_option(self::REGISTRATION_TOKEN_KEY);
    }
    /**
     * Save the new registration token.
     *
     * Protected to allow mocking in tests.
     *
     * @param string $token Registration token.
     */
    protected function save_registration_token(string $token): void
    {
        update_option(self::REGISTRATION_TOKEN_KEY, $token);
    }
    /**
     * Delete registration token.
     *
     * Protected to allow mocking in tests.
     */
    protected function delete_registration_token(): void
    {
        delete_option(self::REGISTRATION_TOKEN_KEY);
    }
}
