<?php
namespace App\Helpers;

use App\Models\Currency;
use App\Models\SystemSetting;

class CurrencyHelper
{
    /**
     * Resolve which currency code to use for the current request.
     *
     * Priority chain (first non-empty wins):
     *   1. Explicit override passed by the caller (call site can force one)
     *   2. Admin's "Default Currency" from Admin → Settings → General
     *      (the platform-wide setting — this is what most installs want)
     *   3. Authenticated user's per-profile preference, BUT only when
     *      it differs from the platform default. The users.currency_code
     *      column defaults to 'USD' on every row via migration, which
     *      means without this guard EVERY user would silently override
     *      the admin's choice with their auto-defaulted USD — the exact
     *      bug we just fixed.
     *   4. Hardcoded USD as the final-final fallback (brand-new installs
     *      with no settings + no users yet).
     *
     * Cached for the request so a page that renders 50 plan prices
     * doesn't query system_settings 50 times.
     */
    protected static function resolveCurrencyCode(?string $explicit = null): string
    {
        if ($explicit) {
            return $explicit;
        }

        // Resolve admin default first — this is the canonical platform
        // currency. Per-user overrides only kick in below if the user
        // actively set something different.
        static $platformDefault = null;
        if ($platformDefault === null) {
            try {
                $platformDefault = (string) SystemSetting::get('default_currency', 'USD');
                if ($platformDefault === '') {
                    $platformDefault = 'USD';
                }
            } catch (\Throwable $e) {
                // DB not available yet (fresh install) — fall through to USD.
                $platformDefault = 'USD';
            }
        }

        // Honor a user-level preference ONLY when it's different from the
        // platform default AND non-empty. This stops the migration-default
        // 'USD' on every users row from silently overriding the admin's
        // configured currency for everyone.
        // \Auth::user() (vs the auth() helper) is statically resolvable so
        // tools/IDE see the right return type — runtime behaviour identical.
        $userCode = \Illuminate\Support\Facades\Auth::user()?->currency_code ?? null;
        if ($userCode && $userCode !== $platformDefault && $userCode !== 'USD') {
            return (string) $userCode;
        }

        return $platformDefault;
    }

    /**
     * Format amount in user's preferred currency.
     *
     * When the resolved code doesn't have a matching row in `currencies`
     * (admin set the default to e.g. "RON" but never created the row, OR
     * the row exists but the cache layer just returned null), we fall
     * back to printing the CODE itself — "RON 19.00" — instead of the
     * old hardcoded "$19.00" which was the most confusing possible
     * behaviour: showing a USD symbol while admin had picked RON.
     */
    public static function format(float $amount, ?string $currencyCode = null): string
    {
        $code = self::resolveCurrencyCode($currencyCode);
        $currency = Currency::findByCode($code);

        if (!$currency) {
            return $code . ' ' . number_format($amount, 2);
        }

        return $currency->format($amount);
    }

    /**
     * Public accessor for the resolved default currency code.
     * Convenience for views that need to format an already-normalized
     * amount with the platform default currency (e.g. dashboard SUMs
     * which were converted to default-currency units in the query).
     */
    public static function defaultCode(): string
    {
        return self::resolveCurrencyCode();
    }

    /**
     * Format an amount in a SPECIFIC currency, no conversion applied.
     *
     * Use this when the value already lives in a known currency — e.g. a
     * Payment row carries its own `currency` column captured at checkout
     * time, so when listing payments we must format each row with its
     * native currency rather than translating to the viewer's default.
     * Otherwise a USD payment renders as "RON 19" — visually correct
     * symbol but wrong amount semantics.
     */
    public static function formatWith(float $amount, ?string $currencyCode): string
    {
        $code = $currencyCode ?: self::resolveCurrencyCode();
        $currency = Currency::findByCode($code);
        if (!$currency) {
            return $code . ' ' . number_format($amount, 2);
        }
        return $currency->format($amount);
    }

    /**
     * Convert amount from base (USD) to target currency.
     */
    public static function convert(float $amount, ?string $toCurrencyCode = null): float
    {
        $code = self::resolveCurrencyCode($toCurrencyCode);
        $currency = Currency::findByCode($code);

        if (!$currency) return $amount;

        return $currency->convertFromBase($amount);
    }

    /**
     * Format with conversion: convert from USD and display in target currency.
     * This is what the `@currency()` Blade directive calls — used on every
     * plan price, billing row, invoice line, etc. across the platform.
     */
    public static function display(float $amountInUsd, ?string $currencyCode = null): string
    {
        $code = self::resolveCurrencyCode($currencyCode);
        $currency = Currency::findByCode($code);

        // Missing currency row → use the code as the symbol fallback
        // (e.g. "RON 19.00") so the user sees what currency the admin
        // configured instead of a confusing "$" prefix.
        if (!$currency) {
            return $code . ' ' . number_format($amountInUsd, 2);
        }

        $converted = $currency->convertFromBase($amountInUsd);
        return $currency->format($converted);
    }
}
