<?php
/**
 * Shows which custom fields an order actually carries.
 *
 * @package SkyServers\Retragere
 */

declare(strict_types=1);

namespace SkyServers\Retragere\Wc;

/**
 * Company identifiers live under a different meta key in every Romanian
 * checkout and invoicing plugin. Rather than guess, we let the shop look at a
 * real order and read the key off it - which HPOS otherwise hides, since the
 * custom fields metabox is gone once orders leave the posts table.
 */
final class MetaProbe
{
    public const ACTION = 'ssrc_probe_order';

    private const MAX_VALUE = 120;

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

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

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('Permission denied.', 'skyservers-retragere-contract')], 403);
        }

        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified above.
        $orderId = isset($_POST['order']) ? (int) $_POST['order'] : 0;
        $order   = OrderLookup::byId($orderId);

        if (null === $order) {
            wp_send_json_error(['message' => __('No order found with this number.', 'skyservers-retragere-contract')]);
        }

        $company = (string) $order->get_billing_company();

        $rows = [
            [
                'key'   => 'billing_company',
                'label' => '' === trim($company)
                    ? __('native field, empty on this order — do not add it to the list', 'skyservers-retragere-contract')
                    : __('native field, checked automatically — do not add it to the list', 'skyservers-retragere-contract'),
                'value' => self::shorten($company),
            ],
        ];

        $seen = [];

        foreach ($order->get_meta_data() as $meta) {
            $data  = $meta->get_data();
            $value = isset($data['value']) ? $data['value'] : '';
            $key   = (string) $data['key'];

            $seen[$key] = true;

            if (!is_scalar($value) || '' === trim((string) $value)) {
                continue;
            }

            $rows[] = [
                'key'   => $key,
                'label' => '',
                'value' => self::shorten((string) $value),
            ];
        }

        // Plugins that still write with update_post_meta() land in the posts
        // meta table, which the order object never reads once high performance
        // storage is authoritative. The field exists, just somewhere else - and
        // that is exactly the case worth surfacing here.
        foreach (self::legacyMeta($orderId) as $key => $value) {
            if (isset($seen[$key]) || '' === trim($value)) {
                continue;
            }

            $rows[] = [
                'key'   => $key,
                'label' => __('legacy metadata (wp_postmeta)', 'skyservers-retragere-contract'),
                'value' => self::shorten($value),
            ];
        }

        wp_send_json_success(['rows' => $rows]);
    }

    /**
     * @return array<string,string>
     */
    private static function legacyMeta(int $orderId): array
    {
        $raw = get_post_meta($orderId);

        if (!is_array($raw)) {
            return [];
        }

        $flat = [];

        foreach ($raw as $key => $values) {
            $value = is_array($values) ? reset($values) : $values;

            if (is_scalar($value)) {
                $flat[(string) $key] = (string) $value;
            }
        }

        return $flat;
    }

    private static function shorten(string $value): string
    {
        if (function_exists('mb_strlen') && mb_strlen($value, 'UTF-8') > self::MAX_VALUE) {
            return mb_substr($value, 0, self::MAX_VALUE, 'UTF-8') . '…';
        }

        return $value;
    }
}
