<?php

declare(strict_types=1);

use App\Jobs\RouteSupplierLeadJob;
use App\Models\BalanceTransaction;
use App\Models\Deal;
use App\Models\LeadCharge;
use App\Models\Project;
use App\Models\Supplier;
use App\Models\SupplierLead;
use App\Models\SupplierProject;
use App\Models\Tenant;
use App\Services\Billing\LedgerService;
use App\Services\LeadDistributor;
use App\Services\LeadRouter;
use App\Services\NotificationService;
use App\Services\RegionTagResolver;
use App\Services\SupplierProjects\SupplierProjectResolver;
use Database\Seeders\PricingTierSeeder;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Tests\Concerns\SharesSupplierPdo;

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

beforeEach(function (): void {
    $this->seed(PricingTierSeeder::class);
    DB::statement("SELECT set_config('app.current_tenant_id', '0', true)");
});

/**
 * Подготовка sharing-flow: N тенантов с указанными балансами, каждый —
 * со своим Project, привязанным к одному supplierProject (платформа B1, site).
 *
 * @param  array<int, array<string, mixed>>  $balances
 * @return array{tenants: array<int, Tenant>, projects: array<int, Project>, lead: SupplierLead, supplier: Supplier}
 */
function prepareSharingFlow(int $tenantsCount, array $balances): array
{
    /** @var array<int, Tenant> $tenants */
    $tenants = [];
    /** @var array<int, Project> $projects */
    $projects = [];

    $supplier = Supplier::where('code', 'b1')->first();
    $supplierProject = SupplierProject::factory()->create([
        'platform' => 'B1',
        'signal_type' => 'site',
        'unique_key' => 'example.com',
        // NB: supplier_projects has NO supplier_id column; LedgerService resolves
        // supplier via platform → suppliers.code mapping.
    ]);

    for ($i = 0; $i < $tenantsCount; $i++) {
        $tenant = Tenant::factory()->create($balances[$i]);
        $project = Project::factory()->create([
            'tenant_id' => $tenant->id,
            'signal_type' => 'site',
            'signal_identifier' => 'example.com',
            'supplier_b1_project_id' => $supplierProject->id,
            'is_active' => true,
            'daily_limit_target' => 10,
            'effective_daily_limit_today' => 10,
            'delivered_today' => 0,
            'delivery_days_mask' => 127,
            'region_mask' => 255,
        ]);
        linkProjectToSupplier($project, $supplierProject);
        createRoutingSnapshotFromProject($project, null, 'site', 'example.com', 10);
        $tenants[] = $tenant;
        $projects[] = $project;
    }

    $vid = random_int(100_000_000, 999_999_999);
    $lead = SupplierLead::factory()->create([
        'vid' => $vid,
        'phone' => '79991234567',
        'raw_payload' => ['vid' => $vid, 'project' => 'B1_example.com', 'phone' => '79991234567', 'time' => time()],
        'supplier_project_id' => $supplierProject->id,
        'received_at' => now(),
    ]);

    return ['tenants' => $tenants, 'projects' => $projects, 'lead' => $lead, 'supplier' => $supplier];
}

function dispatchJob(int $supplierLeadId): void
{
    (new RouteSupplierLeadJob($supplierLeadId))->handle(
        app(LeadRouter::class),
        app(SupplierProjectResolver::class),
        app(NotificationService::class),
        app(LedgerService::class),
        app(LeadDistributor::class),
        app(RegionTagResolver::class),
    );
}

it('charges rub for tenant with balance_leads=0 and balance_rub >= price + writes BalanceTransaction', function (): void {
    $ctx = prepareSharingFlow(1, [['balance_leads' => 0, 'balance_rub' => '1000.00', 'delivered_in_month' => 0]]);

    dispatchJob($ctx['lead']->id);

    $tenant = $ctx['tenants'][0]->fresh();
    expect((string) $tenant->balance_rub)->toBe('500.00');
    expect($tenant->delivered_in_month)->toBe(1);

    $charge = LeadCharge::first();
    expect($charge->charge_source)->toBe('rub');
    expect($charge->price_per_lead_kopecks)->toBe(50000);

    // BalanceTransaction (carry-over M-2 assertion)
    $tx = BalanceTransaction::where('type', BalanceTransaction::TYPE_LEAD_CHARGE)->first();
    expect($tx)->not->toBeNull();
    expect((string) $tx->amount_rub)->toBe('-500.00');
    expect((string) $tx->balance_rub_after)->toBe('500.00');
});

it('writes supplier_lead_costs for each delivered deal copy (gap-fix)', function (): void {
    $ctx = prepareSharingFlow(2, [
        ['balance_leads' => 0, 'balance_rub' => '1000.00', 'delivered_in_month' => 0],
        ['balance_leads' => 0, 'balance_rub' => '1000.00', 'delivered_in_month' => 0],
    ]);

    dispatchJob($ctx['lead']->id);

    $costs = DB::table('supplier_lead_costs')->get();
    expect($costs)->toHaveCount(2);
    foreach ($costs as $cost) {
        expect((int) $cost->supplier_id)->toBe($ctx['supplier']->id);
        expect((string) $cost->cost_rub)->toBe($ctx['supplier']->cost_rub);
    }
});

it('retry idempotency: повторный run не дублирует lead_charges', function (): void {
    $ctx = prepareSharingFlow(1, [['balance_rub' => '100000.00']]);
    $leadId = $ctx['lead']->id;
    $tenantId = $ctx['tenants'][0]->id;

    dispatchJob($leadId);
    dispatchJob($leadId);  // повторный — processed_at guard защищает

    expect(LeadCharge::where('tenant_id', $tenantId)->count())->toBe(1);

    DB::statement("SET LOCAL app.current_tenant_id = '{$tenantId}'");
    expect(Deal::where('tenant_id', $tenantId)->count())->toBe(1);
});
