<?php
/**
 * Serves the product selection for a given order.
 *
 * @package SkyServers\Retragere
 */

declare(strict_types=1);

namespace SkyServers\Retragere\Wc;

use SkyServers\Retragere\Core\ClientIp;
use SkyServers\Retragere\Wp\Shortcode;
use WC_Order;

/**
 * Same exposure as the submit flow: an order's product names are returned only
 * to someone who already knows the order number and the billing email, or who
 * owns the order. Rate limited on its own budget, because unlike the form this
 * endpoint can be called repeatedly without leaving a trace anywhere else.
 */
final class LinesAjax
{
    public const ACTION = 'ssrc_order_lines';

    private const RATE_LIMIT  = 15;
    private const RATE_WINDOW = 3600;

    public static function register(): void
    {
        add_action('wp_ajax_' . self::ACTION, [self::class, 'handle']);
        add_action('wp_ajax_nopriv_' . self::ACTION, [self::class, 'handle']);
    }

    public static function handle(): void
    {
        check_ajax_referer('ssrc_submit', 'nonce');

        if (self::rateLimited()) {
            wp_send_json_error(
                ['message' => __('Too many attempts. You can still send the request by filling in the details manually.', 'skyservers-retragere-contract')]
            );
        }

        $order = self::resolve();

        if (null === $order) {
            wp_send_json_error(
                ['message' => __('No order found with the details entered. You can send the request anyway by filling in the order number below.', 'skyservers-retragere-contract')]
            );
        }

        $evaluation = OrderEvaluation::forOrder($order);

        wp_send_json_success(
            [
                'html'     => Shortcode::template('order-lines', ['lines' => $evaluation['lines']]),
                'eligible' => (bool) $evaluation['eligible'],
            ]
        );
    }

    /**
     * @return WC_Order|null
     */
    private static function resolve()
    {
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified by check_ajax_referer above.
        $orderId = isset($_POST['order']) ? (int) $_POST['order'] : 0;

        if ($orderId > 0 && is_user_logged_in()) {
            $candidate = OrderLookup::byId($orderId);

            if (null !== $candidate && (int) $candidate->get_customer_id() === get_current_user_id()) {
                return $candidate;
            }
        }

        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified by check_ajax_referer above.
        $reference = isset($_POST['ref']) ? sanitize_text_field(wp_unslash((string) $_POST['ref'])) : '';
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified by check_ajax_referer above.
        $email = isset($_POST['email']) ? sanitize_email(wp_unslash((string) $_POST['email'])) : '';

        if ('' === $reference || '' === $email) {
            return null;
        }

        return GuestAccess::match($reference, $email);
    }

    private static function rateLimited(): bool
    {
        // Same reasoning as the form: shop staff testing must not be throttled.
        if (current_user_can('manage_woocommerce') || current_user_can('manage_options')) {
            return false;
        }

        $ip = ClientIp::resolve($_SERVER);

        if ('' === $ip) {
            return false;
        }

        $key   = 'ssrc_lookup_' . md5($ip);
        $count = (int) get_transient($key);

        if ($count >= self::RATE_LIMIT) {
            return true;
        }

        set_transient($key, $count + 1, self::RATE_WINDOW);

        return false;
    }
}
