<?php
/**
 * Decides whether an order was placed by a company.
 *
 * @package SkyServers\Retragere
 */

declare(strict_types=1);

namespace SkyServers\Retragere\Wc;

use SkyServers\Retragere\Wp\Settings;
use WC_Order;

/**
 * The statutory right of withdrawal belongs to consumers. Offering it to
 * companies anyway is a commercial choice, which is why blocking is opt in.
 *
 * There is no per plugin integration here on purpose. Chasing SmartBill, Oblio,
 * FGO and the next five invoicing plugins is a losing race. WooCommerce has a
 * native company field, the shop knows its own setup, and developers get a
 * filter. That covers the ground without pretending to know every checkout.
 */
final class B2bDetector
{
    public const SIGNAL_NONE    = 'none';
    public const SIGNAL_COMPANY = 'company';
    public const SIGNAL_META    = 'meta';
    public const SIGNAL_FILTER  = 'filter';

    /**
     * @param WC_Order $order
     */
    public static function isBusiness($order): bool
    {
        return self::SIGNAL_NONE !== self::signal($order);
    }

    /**
     * Returns which signal identified the order as a company order, so the
     * admin can see why a request was classified the way it was.
     *
     * @param WC_Order $order
     */
    public static function signal($order): string
    {
        $signal = self::SIGNAL_NONE;

        if ('' !== trim((string) $order->get_billing_company())) {
            $signal = self::SIGNAL_COMPANY;
        }

        if (self::SIGNAL_NONE === $signal) {
            foreach (self::metaKeys() as $key) {
                if ('' !== trim(self::readMeta($order, $key))) {
                    $signal = self::SIGNAL_META;
                    break;
                }
            }
        }

        $filtered = (bool) apply_filters('ssrc_order_is_b2b', self::SIGNAL_NONE !== $signal, $order);

        if ($filtered && self::SIGNAL_NONE === $signal) {
            return self::SIGNAL_FILTER;
        }

        if (!$filtered) {
            return self::SIGNAL_NONE;
        }

        return $signal;
    }

    /**
     * Reads a field from wherever it actually lives.
     *
     * With high performance order storage active, plugins that still write via
     * update_post_meta() leave their data in the posts meta table, invisible to
     * the order object. Checking both is the difference between detecting a
     * company order and silently missing it.
     *
     * @param WC_Order $order
     */
    private static function readMeta($order, string $key): string
    {
        $value = $order->get_meta($key);

        if (is_scalar($value) && '' !== trim((string) $value)) {
            return (string) $value;
        }

        $legacy = get_post_meta((int) $order->get_id(), $key, true);

        return is_scalar($legacy) ? (string) $legacy : '';
    }

    /**
     * @return string[]
     */
    public static function metaKeys(): array
    {
        $raw = (string) Settings::get('b2b_meta_keys');

        if ('' === trim($raw)) {
            return [];
        }

        $keys = preg_split('/[\r\n,]+/', $raw);
        $keys = is_array($keys) ? $keys : [];

        return array_values(array_filter(array_map('trim', $keys)));
    }
}
