<?php
declare(strict_types=1);
namespace Aen\MoneyWorkshop;

use Brick\Math\RoundingMode;
use Brick\Money\AllocationMode;
use Brick\Money\Context\DefaultContext;
use Brick\Money\CurrencyDisplay;
use Brick\Money\Money;
use Brick\Money\SplitMode;

final class Experiments
{
    private static function amount(Money $m): string { return (string) $m->getAmount(); }
    private static function amounts(array $items): array { return array_map(self::amount(...), $items); }
    private static function exception(callable $f): string { try { $f(); } catch (\Throwable $e) { return $e::class; } throw new \RuntimeException('Expected exception was not raised.'); }
    public static function run(): array
    {
        $first = Money::of('0.10', 'RUB');
        $round = [];
        foreach ([RoundingMode::HalfUp, RoundingMode::HalfEven, RoundingMode::Down, RoundingMode::Ceiling, RoundingMode::Floor] as $mode) {
            $round[$mode->name] = ['positive' => self::amount(Money::of('2.345', 'RUB', roundingMode: $mode)), 'negative' => self::amount(Money::of('-2.345', 'RUB', roundingMode: $mode))];
        }
        $allocate = [];
        foreach (AllocationMode::cases() as $mode) { $allocate[$mode->name] = self::amounts(Money::of('1.00', 'RUB')->allocate([2, 3, 1], $mode)); }
        $cart = Cart::calculate(json_decode(file_get_contents(__DIR__ . '/../fixtures/cart.json'), true, 512, JSON_THROW_ON_ERROR));
        $roundEarly = Money::of('10.00', 'RUB')->dividedBy(3, RoundingMode::HalfUp)->multipliedBy(3);
        $roundLate = Money::of('10.00', 'RUB')->toRational()->dividedBy(3)->multipliedBy(3)->toContext(new DefaultContext(), RoundingMode::HalfUp);
        // The same locale format can use different Unicode space characters across ICU builds.
        $format = fn(string $locale): string => str_replace(["\u{00a0}", "\u{202f}"], ' ', Money::of('1234.50', 'RUB')->formatToLocale($locale, CurrencyDisplay::Code));
        return ['schemaVersion' => 1, 'engine' => 'PHP + Brick\\Money 0.15.2', 'kind' => 'recorded-real-php-output', 'scenarios' => [
            'exact' => ['input' => ['0.10','0.20'], 'float17' => sprintf('%.17f', 0.1 + 0.2), 'sum' => self::amount($first->plus('0.20')), 'originalUnchanged' => self::amount($first)],
            'minor' => ['minorString' => '9007199254740993', 'RUB' => self::amount(Money::ofMinor('9007199254740993','RUB')), 'JPY' => self::amount(Money::ofMinor('1234','JPY')), 'KWD' => self::amount(Money::ofMinor('12345','KWD'))],
            'rounding' => ['input' => '2.345 / -2.345', 'modes' => $round, 'withoutMode' => self::exception(fn()=>Money::of('2.345','RUB'))],
            'currency' => ['sameCurrency' => (string) Money::of('12.00','RUB')->plus(Money::of('8.00','RUB')), 'mismatch' => self::exception(fn()=>Money::of('12.00','RUB')->plus(Money::of('8.00','USD')))],
            'allocation' => ['amount' => '1.00','ratios' => [2,3,1], 'allocation' => $allocate, 'splitToFirst' => self::amounts(Money::of('1.00','RUB')->split(3, SplitMode::ToFirst)), 'splitSeparate' => self::amounts(Money::of('1.00','RUB')->split(3, SplitMode::Separate))],
            'cart' => $cart,
            'rational' => ['earlyRounding' => self::amount($roundEarly), 'roundOnceAtEnd' => self::amount($roundLate), 'explanation' => '10 / 3 * 3'],
            'exchange' => ['source' => 'USD 100.00', 'demoRate' => '91.2345', 'target' => (string) Money::of('100.00','USD')->convertedTo('RUB','91.2345',roundingMode: RoundingMode::HalfUp), 'rateSource' => 'fixed educational value, not a live market rate'],
            'formatting' => ['machine' => ['currency'=>'RUB','amount'=>'1234.50','amountMinor'=>'123450'], 'ru_RU' => $format('ru_RU'), 'en_US' => $format('en_US'), 'spaceNormalization' => 'NBSP and narrow NBSP replaced with ASCII space for portable recording']
        ]];
    }
}
