<?php

declare(strict_types=1);

use App\Jobs\External\RefreshExternalBalancesJob;
use App\Services\External\BalanceProvider;
use App\Services\External\BalanceReading;
use App\Services\External\DadataBalanceProvider;
use App\Services\External\SupplierBalanceProvider;
use App\Services\External\YandexCloudBalanceProvider;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Tests\Concerns\SharesSupplierPdo;

uses(DatabaseTransactions::class, SharesSupplierPdo::class); // запись идёт через pgsql_supplier

/** Стаб-провайдер с заранее заданным результатом. */
function fakeProvider(string $key, BalanceReading $reading): BalanceProvider
{
    return new class($key, $reading) implements BalanceProvider
    {
        public function __construct(private string $key, private BalanceReading $reading) {}

        public function serviceKey(): string
        {
            return $this->key;
        }

        public function fetch(): BalanceReading
        {
            return $this->reading;
        }
    };
}

it('пишет балансы трёх сервисов + считает светофор', function () {
    config()->set('services.yandex_cloud.red_floor_rub', 1000);
    config()->set('services.yandex_cloud.amber_floor_rub', 5000);

    app()->instance(DadataBalanceProvider::class, fakeProvider('dadata', BalanceReading::ok('dadata', 4500, 'RUB', 100)));
    app()->instance(SupplierBalanceProvider::class, fakeProvider('supplier', BalanceReading::ok('supplier', 50000, 'RUB', null)));
    app()->instance(YandexCloudBalanceProvider::class, fakeProvider('yandex_cloud', BalanceReading::ok('yandex_cloud', -540.48, 'RUB', 600)));

    (new RefreshExternalBalancesJob)->handle();

    $rows = DB::connection('pgsql_supplier')->table('external_service_balances')->get()->keyBy('service_key');
    expect($rows)->toHaveCount(3);
    expect((float) $rows['yandex_cloud']->balance_amount)->toBe(-540.48);
    expect($rows['yandex_cloud']->light)->toBe('red'); // минус < red_floor
    expect((bool) $rows['yandex_cloud']->ok)->toBeTrue();
    expect($rows['dadata']->ok)->toBeTruthy();
});

it('повторный запуск обновляет строки, а не падает на PK (свежий builder/итерация)', function () {
    app()->instance(DadataBalanceProvider::class, fakeProvider('dadata', BalanceReading::ok('dadata', 4500, 'RUB', 100)));
    app()->instance(SupplierBalanceProvider::class, fakeProvider('supplier', BalanceReading::fail('supplier', 'таймаут')));
    app()->instance(YandexCloudBalanceProvider::class, fakeProvider('yandex_cloud', BalanceReading::ok('yandex_cloud', 42000, 'RUB', 600)));

    (new RefreshExternalBalancesJob)->handle();
    (new RefreshExternalBalancesJob)->handle(); // второй прогон не должен бросить UniqueConstraint

    $rows = DB::connection('pgsql_supplier')->table('external_service_balances')->get();
    expect($rows)->toHaveCount(3); // строк по-прежнему 3, без дублей
});

it('упавший провайдер не роняет джобу и сохраняет ошибку, остальные пишутся', function () {
    app()->instance(DadataBalanceProvider::class, fakeProvider('dadata', BalanceReading::fail('dadata', 'HTTP 403')));
    app()->instance(SupplierBalanceProvider::class, fakeProvider('supplier', BalanceReading::ok('supplier', 50000, 'RUB', null)));
    app()->instance(YandexCloudBalanceProvider::class, fakeProvider('yandex_cloud', BalanceReading::ok('yandex_cloud', 42000, 'RUB', 600)));

    (new RefreshExternalBalancesJob)->handle();

    $rows = DB::connection('pgsql_supplier')->table('external_service_balances')->get()->keyBy('service_key');
    expect((bool) $rows['dadata']->ok)->toBeFalse();
    expect($rows['dadata']->error)->toContain('403');
    expect((bool) $rows['supplier']->ok)->toBeTrue();
    expect((bool) $rows['yandex_cloud']->ok)->toBeTrue();
});
