<?php
declare(strict_types=1);

// Optional private overlay allows red/green without editing the working repository.
$repo = getenv('TERMINAL_TEST_REPO') ?: dirname(__DIR__);
$loader = require $repo . '/vendor/autoload.php';
if ($overlay = getenv('TERMINAL_SOURCE_OVERLAY')) {
    $loader->setPsr4('app\\', $overlay . '/app');
    $loader->setPsr4('cores\\', $overlay . '/cores');
}
if (!function_exists('app_name')) { function app_name(): string { return 'terminal'; } }
if (!function_exists('getStoreId')) { function getStoreId(): int { return 10001; } }

$assertions = 0;
$assert = static function ($actual, $expected, string $message) use (&$assertions): void {
    ++$assertions;
    if ($actual !== $expected) throw new RuntimeException($message);
};
$nutrition = [
    'energy','carbohydrate','protein','fat','sfa','mufa','pufa','cholesterol',
    'va','renieratene','retinol','vb1','vb2','vpp','vc','ve','folic_acid',
    'biotin','pantothenic_acid','choline','potassium','sodium','calcium',
    'magnesium','iron','zinc','selenium','iodine','phosphorus','cuprum',
    'manganese','purine','dietary_fiber','moisture','ash','gi','gl',
];
foreach ([\app\terminal\model\dishes\Foods::class => $nutrition,
    \app\terminal\model\dishes\Dishes::class => array_merge(['weight','raw_flag_weight'], $nutrition)] as $class => $fields) {
    foreach ([[1.25,'1.25'],[0,'0.00'],[-1.25,'-1.25'],[null,null],
        ['12.50','12.50'],['99999999.99','99999999.99'],
        ['9007199254740993.01','9007199254740993.01']] as [$raw,$expected]) {
        $data = array_fill_keys($fields, $raw) + ['id'=>9007199254740993,
            'category'=>3,'is_delete'=>0,'float_probe'=>1.2345,'name'=>'test'];
        $model = new $class($data);
        $row = $model->toArray();
        foreach ($fields as $field) {
            $assert($row[$field], $expected, $class . ' ' . $field . ' serialized DECIMAL type/value');
            $assert($model[$field], $raw, $class . ' ' . $field . ' must not mutate raw model data');
        }
        $assert($row['id'], 9007199254740993, 'Identifier must remain exact integer');
        $assert($row['category'], 3, 'Integer category must not stringify');
        $assert($row['float_probe'], 1.2345, 'Non-whitelisted float must not change');
        $assert(json_decode(json_encode($model), true)['energy'], $expected, 'JSON must use the same DECIMAL contract');
    }
    $empty = new $class(['id'=>1]);
    $assert(array_key_exists('energy', $empty->toArray()), false, 'Do not synthesize absent columns');
}

$dish = new \app\terminal\model\dishes\Dishes(['id'=>1,'weight'=>12.5]);
$food = new \app\terminal\model\dishes\Foods(['id'=>2,'name'=>'fixture','energy'=>3.5]);
$pivot = new \think\model\Pivot(['dishes_id'=>1,'foods_id'=>2,
    'foods_weight'=>2.5,'foods_ratio'=>0,'ssb'=>1.2345]);
$food->setRelation('pivot', $pivot);
$dish->setRelation('foods', new \think\model\Collection([$food]));
$row = $dish->toArray();
$assert($row['foods'][0]['pivot']['foods_weight'], '2.50', 'Nested foods_weight must retain two-place string');
$assert($row['foods'][0]['pivot']['foods_ratio'], '0.00', 'Nested foods_ratio must retain two-place string');
$assert($row['foods'][0]['pivot']['foods_id'], 2, 'Nested pivot ID must not stringify');
$assert($row['foods'][0]['pivot']['ssb'], 1.2345, 'Unobserved pivot fields remain unchanged');
$assert($pivot['foods_weight'], 2.5, 'Nested raw pivot must not mutate');
$pivot->setAttr('foods_weight', null);
$assert($dish->toArray()['foods'][0]['pivot']['foods_weight'], null, 'Nested null must remain null');

echo 'TERMINAL_CATALOG_DECIMAL_CONTRACT_PASS assertions=' . $assertions . "\n";
