<?php

declare(strict_types=1);

use App\Jobs\SyncSupplierProjectJob;
use App\Mail\BalanceUnfrozenMail;
use App\Models\PricingTier;
use App\Models\Project;
use App\Models\Tenant;
use App\Services\Billing\BillingTopupService;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Queue;
use Tests\Concerns\SharesSupplierPdo;

uses(DatabaseTransactions::class);
uses(SharesSupplierPdo::class);

beforeEach(function () {
    Queue::fake();
    DB::statement("SELECT set_config('app.current_tenant_id', '0', true)");
    PricingTier::query()->create([
        'tier_no' => 1,
        'leads_in_tier' => 100,
        'price_per_lead_kopecks' => 5000, // 50₽/лид — capacity = balance/50
        'is_active' => true,
        'effective_from' => now(),
    ]);
});

// E (балансовый блок): авто-снятие preflight_blocked_at при пополнении.
// Политика «всё-или-ничего»: хватает на суммарный дневной лимит ВСЕХ активных
// проектов (вкл. заблокированные) → снять блок со всех + синк; не хватает → никого.

it('releases blocked project when topup covers the full order', function () {
    $tenant = Tenant::factory()->withRequisites()->create(['balance_rub' => '0.00']);
    $project = Project::factory()->for($tenant)->create([
        'is_active' => true,
        'daily_limit_target' => 30,
        'preflight_blocked_at' => now(),
    ]);

    // 2000₽ / 50 = 40 capacity >= 30 → снять блок + синк.
    app(BillingTopupService::class)->topup($tenant->id, '2000.00', null);

    expect($project->fresh()->preflight_blocked_at)->toBeNull();
    Queue::assertPushed(SyncSupplierProjectJob::class);
});

it('keeps blocked when topup still insufficient', function () {
    $tenant = Tenant::factory()->withRequisites()->create(['balance_rub' => '0.00']);
    $project = Project::factory()->for($tenant)->create([
        'is_active' => true,
        'daily_limit_target' => 30,
        'preflight_blocked_at' => now(),
    ]);

    // 500₽ / 50 = 10 capacity < 30 → остаётся заблокированным, синка нет.
    app(BillingTopupService::class)->topup($tenant->id, '500.00', null);

    expect($project->fresh()->preflight_blocked_at)->not->toBeNull();
    Queue::assertNotPushed(SyncSupplierProjectJob::class);
});

it('releases all-or-nothing across multiple blocked projects', function () {
    $tenant = Tenant::factory()->withRequisites()->create(['balance_rub' => '0.00']);
    $p1 = Project::factory()->for($tenant)->create([
        'is_active' => true, 'daily_limit_target' => 20, 'preflight_blocked_at' => now(),
    ]);
    $p2 = Project::factory()->for($tenant)->create([
        'is_active' => true, 'daily_limit_target' => 20, 'preflight_blocked_at' => now(),
    ]);

    // required = 20+20 = 40. 1500₽/50 = 30 capacity < 40 → НЕ снимать никого.
    app(BillingTopupService::class)->topup($tenant->id, '1500.00', null);
    expect($p1->fresh()->preflight_blocked_at)->not->toBeNull();
    expect($p2->fresh()->preflight_blocked_at)->not->toBeNull();

    // дополнили до 2000 → capacity 40 >= 40 → снять блок с ОБОИХ.
    app(BillingTopupService::class)->topup($tenant->id, '500.00', null);
    expect($p1->fresh()->preflight_blocked_at)->toBeNull();
    expect($p2->fresh()->preflight_blocked_at)->toBeNull();
});

it('does nothing when tenant has no blocked projects', function () {
    $tenant = Tenant::factory()->withRequisites()->create(['balance_rub' => '0.00']);
    $project = Project::factory()->for($tenant)->create([
        'is_active' => true,
        'daily_limit_target' => 10,
        'preflight_blocked_at' => null,
    ]);

    app(BillingTopupService::class)->topup($tenant->id, '2000.00', null);

    expect($project->fresh()->preflight_blocked_at)->toBeNull();
    Queue::assertNotPushed(SyncSupplierProjectJob::class);
});

// B/D3 (23.06.2026): пополнение снимает ОБА замка вместе — клиентскую заморозку
// (frozen_by_balance_at) и проектный блок (preflight_blocked_at) — политика «всё-или-ничего».

it('unfreezes tenant AND releases blocked project when topup covers the full order', function () {
    Mail::fake();
    $tenant = Tenant::factory()->withRequisites()->create([
        'balance_rub' => '0.00',
        'frozen_by_balance_at' => now()->subDay(),
    ]);
    $project = Project::factory()->for($tenant)->create([
        'is_active' => true,
        'daily_limit_target' => 30,
        'preflight_blocked_at' => now(),
    ]);

    // 2000₽ / 50 = 40 capacity >= 30 → снять оба замка сразу.
    app(BillingTopupService::class)->topup($tenant->id, '2000.00', null);

    expect($tenant->fresh()->frozen_by_balance_at)->toBeNull();
    expect($project->fresh()->preflight_blocked_at)->toBeNull();
    Mail::assertQueued(BalanceUnfrozenMail::class);
});

it('keeps BOTH locks when topup still insufficient', function () {
    Mail::fake();
    $tenant = Tenant::factory()->withRequisites()->create([
        'balance_rub' => '0.00',
        'frozen_by_balance_at' => now()->subDay(),
    ]);
    $project = Project::factory()->for($tenant)->create([
        'is_active' => true,
        'daily_limit_target' => 30,
        'preflight_blocked_at' => now(),
    ]);

    // 500₽ / 50 = 10 capacity < 30 → не трогать ни один замок.
    app(BillingTopupService::class)->topup($tenant->id, '500.00', null);

    expect($tenant->fresh()->frozen_by_balance_at)->not->toBeNull();
    expect($project->fresh()->preflight_blocked_at)->not->toBeNull();
    Mail::assertNotQueued(BalanceUnfrozenMail::class);
});
