<?php

namespace SoftminalB2B\Compat;

if (!defined('ABSPATH')) {
    exit;
}

/**
 * Multi-currency compatibility (WOOCS/FOX, Aelia, WooCommerce Multilingual,
 * and any switcher that filters `woocommerce_currency`).
 *
 * B2B Suite resolves every price in the store's BASE currency — a percentage or
 * a fixed base price — and lets the active switcher convert the result at
 * display time, which is how every switcher expects to work. That means the
 * common case needs nothing: the switcher converts B2B's resolved base price on
 * its way to the screen.
 *
 * This class exists for the two places that base-currency assumption leaks:
 *   1. A switcher that converts the base price *before* the resolver reads it
 *      would otherwise poison the resolver's currency-agnostic cache — so the
 *      cache is segmented by active currency whenever a switcher is present.
 *   2. A base-currency threshold (a minimum order value) compared against a
 *      cart total that the switcher has already converted — {@see toActive()}
 *      converts the threshold so the comparison is like-for-like.
 *
 * With no switcher active every method is a cheap pass-through.
 */
class MultiCurrency
{
    private static ?bool $active = null;

    /**
     * Is a currency switcher active? A switcher advertises itself by filtering
     * `woocommerce_currency` away from the stored base, or by one of the known
     * plugin globals/classes being present.
     */
    public static function isActive(): bool
    {
        if (self::$active !== null) {
            return self::$active;
        }

        $detected = self::activeCurrency() !== self::baseCurrency()
            || isset($GLOBALS['WOOCS'])
            || class_exists('WOOCS')
            || class_exists('\\Aelia\\WC\\CurrencySwitcher\\WC_Aelia_CurrencySwitcher')
            || (isset($GLOBALS['woocommerce_wpml']) && !empty($GLOBALS['woocommerce_wpml']->multi_currency));

        return self::$active = (bool) apply_filters('smb2b_multicurrency_active', $detected);
    }

    /** Reset detection (tests, currency switches within a request). */
    public static function flush(): void
    {
        self::$active = null;
    }

    /** The store's base (settings) currency. */
    public static function baseCurrency(): string
    {
        return (string) get_option('woocommerce_currency', '');
    }

    /**
     * The currency in effect for this request. Switchers filter
     * `woocommerce_currency`, so `get_woocommerce_currency()` already reflects
     * the shopper's choice.
     */
    public static function activeCurrency(): string
    {
        return function_exists('get_woocommerce_currency')
            ? (string) get_woocommerce_currency()
            : self::baseCurrency();
    }

    /**
     * A cache-key fragment that keeps one currency's resolved prices from being
     * served in another. Empty (no effect on the key) unless a switcher is
     * active, so a single-currency store's cache is untouched.
     */
    public static function cacheSegment(): string
    {
        return self::isActive() ? 'c:' . self::activeCurrency() : '';
    }

    /**
     * Convert a base-currency amount into the active currency, for comparing a
     * stored threshold against an already-converted cart total. Uses the active
     * switcher's own converter where we know it, then a generic filter seam, and
     * finally returns the amount unchanged when nothing can convert it.
     */
    public static function toActive(float $amount): float
    {
        if (!self::isActive()) {
            return $amount;
        }

        $base   = self::baseCurrency();
        $active = self::activeCurrency();

        if ($active === $base) {
            return $amount;
        }

        // WOOCS / FOX.
        if (isset($GLOBALS['WOOCS']) && is_object($GLOBALS['WOOCS']) && method_exists($GLOBALS['WOOCS'], 'woocs_exchange_value')) {
            return (float) $GLOBALS['WOOCS']->woocs_exchange_value($amount);
        }

        // Aelia Currency Switcher — consuming its own hook, not defining ours.
        if (has_filter('wc_aelia_cs_convert')) {
            return (float) apply_filters('wc_aelia_cs_convert', $amount, $base, $active); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
        }

        // WooCommerce Multilingual (WCML) — consuming its own hook.
        if (has_filter('wcml_raw_price_amount')) {
            return (float) apply_filters('wcml_raw_price_amount', $amount, $active); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
        }

        /**
         * Generic conversion seam for switchers we don't detect natively, and
         * the hook tests drive.
         *
         * @param float  $amount base-currency amount
         * @param string $base   base currency code
         * @param string $active active currency code
         */
        return (float) apply_filters('smb2b_convert_currency', $amount, $base, $active);
    }

    /**
     * Convert an active-currency amount back to the base currency — for keeping
     * a base-currency ledger correct when an order was totalled in the shopper's
     * currency. Derived from the base->active rate so it works with any switcher
     * {@see toActive()} already supports, no separate reverse integration needed.
     */
    public static function toBase(float $amount): float
    {
        if (!self::isActive() || self::activeCurrency() === self::baseCurrency()) {
            return $amount;
        }

        $rate = self::toActive(1.0);

        return $rate > 0 ? $amount / $rate : $amount;
    }
}
