<?php

declare(strict_types=1);

/**
 * Verification tests — ScenarioX1X3_SubstitutionJournalTest (Task 12, Phase 1 imitation).
 *
 * PROVING tests against existing production routing code.
 * Differences vs plan → FINDINGS captured in test output and comments.
 * NO production code is modified. Only this one file is created.
 *
 * Plan: docs/superpowers/plans/2026-06-03-portal-client-imitation-phase1.md
 *       → "Task 12: X1 — подмена региона на шаге 3 + журнал; X3 — сводка источника"
 * Spec: §6.5 X1/X3 + §7 п.30/41
 *
 * ── KEY PRODUCTION CODE FACTS (verified from reading prod code, NOT guessing) ────
 *
 * RouteSupplierLeadJob::createDealCopyForProject() lines 433–453:
 *   $dealSubjectCode = ($routingStep < 3)
 *       ? $resolution->subjectCode
 *       : (pickSubstituteRegion($snapshot->regions ?? '{}') ?? $resolution->subjectCode);
 *   Deal::create([
 *       'subject_code'     => $dealSubjectCode,         // substituted (client's) on step 3
 *       'city'             => CODE_TO_NAME[$resolution->subjectCode] ?? null,  // REAL lead region ALWAYS
 *       'region_substituted' => ($routingStep === 3),   // flag
 *   ]);
 *
 * RouteSupplierLeadJob::logRegionResolution() lines 558–595:
 *   $substituted = ($routingStep === 3 && $first !== null)
 *       ? (pickSubstituteRegion($first->snapshot_regions ?? '{}') ?? $resolution->subjectCode)
 *       : null;
 *   INSERT lead_region_resolution_log {
 *       actual_subject_code     => $resolution->actualSubjectCode  // real resolved code
 *       substituted_subject_code=> $substituted                    // client's code on step 3, else null
 *       routing_step            => $routingStep                    // step of FIRST selected project
 *       subject_code_resolved   => $resolution->subjectCode        // real resolved code (same as actual)
 *   }
 *
 * LeadRouter: phase 3 fires ONLY when combined(phase1+phase2) is EMPTY.
 *   To force step 3: the ONLY eligible client must have an exact region DIFFERENT
 *   from the lead's resolved region, AND no all-RF client (regions='{}').
 *   → phase 1 empty (exact mismatch), phase 2 empty (no '{}' client), phase 3 fires.
 *
 * pickSubstituteRegion() picks the FIRST int from the PG INT[]-literal '{R_client}'.
 * snapshot.regions column holds the client's subscribed regions.
 *
 * RegionResolution.actualSubjectCode = subjectCode at construction (RegionResolution::make()).
 * They are equal at the resolver stage; substitution is a RouteSupplierLeadJob concern only.
 *
 * X3 region_source values:
 *   'dadata'   — FakeDaData stub with qc=0
 *   'rossvyaz' — FakeDaData stubThrows + seeded phone_ranges row matching the phone
 *   'tag'      — DaData disabled OR qc=2/7 + valid tag string (FINDING: qc=2 with valid tag → 'tag')
 *   'unknown'  — DaData disabled/fails + no phone_range + empty/null tag
 *
 * X3 uses direct LeadRegionResolver::resolve() calls (not full routing) to
 * produce multiple resolution log rows cheaply via separate SupplierLead rows.
 */

use App\Jobs\RouteSupplierLeadJob;
use App\Models\Project;
use App\Models\SupplierLead;
use App\Models\SupplierProject;
use App\Models\Tenant;
use App\Services\DaData\DaDataPhoneClient;
use App\Services\LeadRegionResolver;
use App\Services\LeadRouter;
use App\Support\RussianRegions;
use Database\Seeders\PricingTierSeeder;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Random\Engine\Mt19937;
use Random\Randomizer;
use Tests\Concerns\SharesSupplierPdo;
use Tests\Support\Imitation\FakeDaDataPhoneClient;
use Tests\Support\Imitation\LeadInjector;
use Tests\Support\Imitation\SnapshotForge;

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

// ── SUBJECT CODE CONSTANTS (порядковые, НЕ ГИБДД) ────────────────────────────
/** RussianRegions::CODE_TO_NAME[29] = 'Красноярский край' — real lead region */
const X1_LEAD_REGION = 29;
/** RussianRegions::CODE_TO_NAME[37] = 'Белгородская область' — client's subscribed region */
const X1_CLIENT_REGION = 37;
/** RussianRegions::CODE_TO_NAME[82] = 'Москва' */
const X1_MOSCOW = 82;

/** Domains for X1 and X3 tests (B1 site signal, unique per scenario to avoid snapshot collisions) */
const X1_DOMAIN = 'scenario-x1-substitution.ru';
const X3_DOMAIN = 'scenario-x3-source-breakdown.ru';

/** Deterministic seed for LeadRouter */
const X1_SEED = 42;

// ── SHARED beforeEach ──────────────────────────────────────────────────────────
beforeEach(function (): void {
    // Pricing tiers required by LedgerService::chargeForDelivery.
    $this->seed(PricingTierSeeder::class);

    // Global RLS bypass for seeding (tenant context = 0).
    DB::statement("SELECT set_config('app.current_tenant_id', '0', true)");

    // DaData config defaults — individual tests override as needed.
    config([
        'services.dadata.enabled' => true,
        'services.dadata.api_key' => 'fake-key',
        'services.dadata.secret' => 'fake-secret',
        'services.dadata.daily_cap_rub' => 1_000_000,
    ]);

    // Deterministic LeadRouter — seeded Mt19937. With 1 candidate, weightedPick
    // always returns it (pool ≤ cap=3 so no lottery needed), but seeded for stability.
    app()->instance(LeadRouter::class, new LeadRouter(new Randomizer(new Mt19937(X1_SEED))));
});

// ══════════════════════════════════════════════════════════════════════════════
// SCENARIO X1 — step-3 substitution: subject_code, city, journal actual/substituted
// ══════════════════════════════════════════════════════════════════════════════

/**
 * X1: One client subscribed to R_client (Белгородская обл., code 37).
 *     No all-RF client. Lead resolved to R_lead (Красноярский край, code 29) via DaData qc=0.
 *
 * Cascade:
 *   Phase 1 exact: 29 NOT in client's regions={37} → empty.
 *   Phase 2 all-RF: no '{}' client → empty.
 *   Phase 3 fallback: client eligible (any region) → routing_step=3.
 *
 * Expected (prod spec §3.10 + §7 п.30/41):
 *   deals.subject_code   = R_client = 37   (substituted to client's region)
 *   deals.city           = name(R_lead)    = 'Красноярский край' (REAL lead region)
 *   deals.region_substituted = true
 *   lead_region_resolution_log.actual_subject_code    = R_lead = 29
 *   lead_region_resolution_log.substituted_subject_code = R_client = 37
 *   lead_region_resolution_log.subject_code_resolved  = R_lead = 29
 *   lead_region_resolution_log.routing_step = 3
 */
it('X1: step-3 fallback substitutes subject_code to client region, preserves real region in city + journal', function (): void {
    // ── ARRANGE ───────────────────────────────────────────────────────────────

    // One supplier (B1 site).
    $supplier = SupplierProject::factory()->create([
        'platform' => 'B1',
        'signal_type' => 'site',
        'unique_key' => X1_DOMAIN,
    ]);

    // One client: subscribed to R_client=37 (Белгородская обл.) ONLY.
    // No all-RF client → phases 1+2 both empty → phase 3 fires.
    $tenant = Tenant::factory()->create([
        'balance_rub' => '99999.00',
        'frozen_by_balance_at' => null,
    ]);
    $project = Project::factory()->create([
        'tenant_id' => $tenant->id,
        'is_active' => true,
        'signal_type' => 'site',
        'signal_identifier' => X1_DOMAIN,
        'daily_limit_target' => 10,
        'effective_daily_limit_today' => null,
        'delivered_today' => 0,
        'delivered_in_month' => 0,
        'delivery_days_mask' => 127,
        'preflight_blocked_at' => null,
        'regions' => [X1_CLIENT_REGION],  // {37}
    ]);
    linkProjectToSupplier($project, $supplier);

    $activeDate = SnapshotForge::activeDate();
    createRoutingSnapshotFromProject(
        project: $project,
        date: $activeDate,
        signalType: 'site',
        signalIdentifier: X1_DOMAIN,
        dailyLimit: 10,
        regions: '{'.X1_CLIENT_REGION.'}',  // '{37}'
    );

    // Lead resolves to R_lead=29 (Красноярский край) via DaData qc=0.
    // DaData region string must EXACTLY match RussianRegions::CODE_TO_NAME[29]
    // for DaDataRegionMap::toSubjectCode() to return code 29.
    $leadPhone = '79292900001';
    $leadRegionName = RussianRegions::CODE_TO_NAME[X1_LEAD_REGION]; // 'Красноярский край'

    $fakeDaData = new FakeDaDataPhoneClient;
    $fakeDaData->stub($leadPhone, qc: 0, region: $leadRegionName, provider: 'МТС');
    app()->instance(DaDataPhoneClient::class, $fakeDaData);

    // ── ACT ───────────────────────────────────────────────────────────────────

    $injector = new LeadInjector;
    $lead = $injector->site(
        domain: X1_DOMAIN,
        phone: $leadPhone,
        tag: null,
        platform: 'B1',
        vid: 9_120_001_001,
    );

    // ── ASSERT ────────────────────────────────────────────────────────────────

    // Retrieve the deal for our tenant.
    $deal = DB::connection('pgsql_supplier')
        ->table('deals')
        ->where('tenant_id', $tenant->id)
        ->first();

    $logRow = DB::connection('pgsql_supplier')
        ->table('lead_region_resolution_log')
        ->where('supplier_lead_id', $lead->id)
        ->first();

    // Diagnostic output.
    fwrite(STDOUT, PHP_EOL.'=== X1 SUBSTITUTION ==='.PHP_EOL);
    fwrite(STDOUT, "Lead phone:                {$leadPhone}".PHP_EOL);
    fwrite(STDOUT, 'R_lead (real resolved):    '.X1_LEAD_REGION." ({$leadRegionName})".PHP_EOL);
    fwrite(STDOUT, 'R_client (client region):  '.X1_CLIENT_REGION.' ('.RussianRegions::CODE_TO_NAME[X1_CLIENT_REGION].')'.PHP_EOL);
    fwrite(STDOUT, 'deal found:                '.($deal !== null ? 'YES' : 'NO').PHP_EOL);
    if ($deal !== null) {
        fwrite(STDOUT, "deals.subject_code:        {$deal->subject_code}".PHP_EOL);
        fwrite(STDOUT, "deals.city:                {$deal->city}".PHP_EOL);
        fwrite(STDOUT, "deals.region_substituted:  {$deal->region_substituted}".PHP_EOL);
    }
    if ($logRow !== null) {
        fwrite(STDOUT, "log.routing_step:                 {$logRow->routing_step}".PHP_EOL);
        fwrite(STDOUT, "log.subject_code_resolved:        {$logRow->subject_code_resolved}".PHP_EOL);
        fwrite(STDOUT, "log.actual_subject_code:          {$logRow->actual_subject_code}".PHP_EOL);
        fwrite(STDOUT, "log.substituted_subject_code:     {$logRow->substituted_subject_code}".PHP_EOL);
        fwrite(STDOUT, "log.region_source:                {$logRow->region_source}".PHP_EOL);
    } else {
        fwrite(STDOUT, 'log row: NOT FOUND'.PHP_EOL);
    }
    fwrite(STDOUT, '=== END X1 ==='.PHP_EOL.PHP_EOL);

    // A deal must have been created.
    expect($deal)->not->toBeNull(
        'FINDING: No deal was created for the tenant. '.
        'The phase-3 fallback (any region) may not be reaching this client, '.
        'or the snapshot is missing.'
    );

    if ($deal !== null) {
        // deals.subject_code must be R_client (substituted to client's region on step 3).
        expect((int) $deal->subject_code)->toBe(X1_CLIENT_REGION,
            'FINDING: deals.subject_code should be '.X1_CLIENT_REGION.' (R_client, Белгородская обл.) '.
            'because routing_step=3 substitutes subject_code to the first code in snapshot.regions. '.
            'Got: '.$deal->subject_code.'. '.
            'If got R_lead='.X1_LEAD_REGION.': substitution is not firing (routingStep capture or pickSubstituteRegion failed). '.
            'If got null: snapshot.regions may not be picked up correctly.'
        );

        // deals.city must be the name of R_lead (real resolved region), NOT R_client.
        // Code §3.10 comment: «Город» = человекочитаемое имя НАСТОЯЩЕГО региона лида.
        expect($deal->city)->toBe($leadRegionName,
            'FINDING: deals.city should be "'.$leadRegionName.'" (name of R_lead='.X1_LEAD_REGION.', real lead region). '.
            'Got: "'.$deal->city.'". '.
            'city is ALWAYS set from $resolution->subjectCode name, NOT from $dealSubjectCode. '.
            'If city = "'.RussianRegions::CODE_TO_NAME[X1_CLIENT_REGION].'": '.
            'prod code erroneously uses $dealSubjectCode for city.'
        );

        // deals.region_substituted must be true.
        // PostgreSQL boolean comes back as string '1'/'0'/'t'/'f' or bool depending on driver.
        $regionSubstituted = filter_var($deal->region_substituted, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
        if ($regionSubstituted === null) {
            // Raw value from DB — accept truthy string/int representations.
            $regionSubstituted = in_array($deal->region_substituted, [true, 1, '1', 't', 'true'], true);
        }

        expect($regionSubstituted)->toBeTrue(
            'FINDING: deals.region_substituted should be TRUE on routing_step=3. '.
            'Got raw value: "'.$deal->region_substituted.'". '.
            'Check RouteSupplierLeadJob line: \'region_substituted\' => $routingStep === 3.'
        );
    }

    // lead_region_resolution_log must have a row.
    expect($logRow)->not->toBeNull(
        'FINDING: lead_region_resolution_log has no row for this lead. '.
        'logRegionResolution() may have failed silently (fail-safe wrapper suppresses exceptions). '.
        'Check for partition missing (received_at date not matching any partition).'
    );

    if ($logRow !== null) {
        // routing_step = 3.
        expect((int) $logRow->routing_step)->toBe(3,
            'FINDING: log.routing_step should be 3 (phase-3 fallback). '.
            'Got: '.$logRow->routing_step.'. '.
            'If 1: exact match fired (snapshot.regions may be wrong). '.
            'If 2: all-RF match fired (client has regions=\'{}\' or snapshot is wrong).'
        );

        // subject_code_resolved = R_lead (the actual resolved code, not substituted).
        expect((int) $logRow->subject_code_resolved)->toBe(X1_LEAD_REGION,
            'FINDING: log.subject_code_resolved should be '.X1_LEAD_REGION.' (R_lead, real resolved). '.
            'Got: '.$logRow->subject_code_resolved.'.'
        );

        // actual_subject_code = R_lead.
        // RegionResolution.actualSubjectCode = subjectCode at construction time (real resolved).
        expect((int) $logRow->actual_subject_code)->toBe(X1_LEAD_REGION,
            'FINDING: log.actual_subject_code should be '.X1_LEAD_REGION.' (R_lead, real lead region). '.
            'Got: '.$logRow->actual_subject_code.'. '.
            'actualSubjectCode is set equal to subjectCode in RegionResolution::make().'
        );

        // substituted_subject_code = R_client (the first code from snapshot.regions).
        // pickSubstituteRegion('{37}') → 37 = X1_CLIENT_REGION.
        expect($logRow->substituted_subject_code)->not->toBeNull(
            'FINDING: log.substituted_subject_code should be '.X1_CLIENT_REGION.' (R_client) on step 3. '.
            'Got null. '.
            'logRegionResolution() computes substituted only when routingStep===3 AND $first!==null. '.
            'Check that $first->snapshot_regions attribute is present (set by LeadRouter SQL SELECT).'
        );

        if ($logRow->substituted_subject_code !== null) {
            expect((int) $logRow->substituted_subject_code)->toBe(X1_CLIENT_REGION,
                'FINDING: log.substituted_subject_code should be '.X1_CLIENT_REGION.' (R_client=Белгородская обл.). '.
                'Got: '.$logRow->substituted_subject_code.'. '.
                'pickSubstituteRegion() parses PG INT[]-literal \'{37}\' → [37] → first=37.'
            );
        }
    }
})->group('imitation');

// ══════════════════════════════════════════════════════════════════════════════
// SCENARIO X3 — region_source breakdown (dadata / rossvyaz / tag / unknown)
// ══════════════════════════════════════════════════════════════════════════════

/**
 * X3: Inject 4 leads with different region_source values, aggregate
 * lead_region_resolution_log.region_source counts, assert they match.
 *
 * To avoid full routing overhead and snapshot complexity, X3 uses
 * LeadRegionResolver::resolve() directly on SupplierLead rows,
 * then reads region_source from the updated supplier_leads columns.
 * The resolver writes region_source to supplier_leads.region_source
 * (RouteSupplierLeadJob lines 159-164); the log is written by
 * logRegionResolution() after routing. For X3 we inject via full
 * LeadInjector (which fires RouteSupplierLeadJob) so logRegionResolution()
 * also runs; however, without a client snapshot the routing loop produces
 * no deals (no selected projects → logRegionResolution called with empty $selected).
 *
 * FINDING note on log.routing_step when $selected is empty:
 *   logRegionResolution() line 561: $first = $selected->first() → null.
 *   So $routingStep = null, $substituted = null.
 *   This is correct/expected for X3's source-breakdown scenario.
 *
 * Source classification (from LeadRegionResolver code):
 *   'dadata'   — DaData enabled, qc=0 (good quality, map returns a code).
 *   'rossvyaz' — DaData disabled OR throws/qc=1, phone_ranges row seeded for phone.
 *   'tag'      — DaData disabled/fails/qc=2, valid tag string (maps to a region code).
 *   'unknown'  — DaData disabled/fails, no phone_range match, empty/null tag.
 *
 * We inject 1 lead per source type (4 total), then read supplier_leads.region_source.
 * Counts: dadata=1, rossvyaz=1, tag=1, unknown=1.
 *
 * X3 uses a dedicated supplier + NO snapshot → selected=empty → no deals created.
 * This avoids the routing infrastructure (no client setup, no snapshot needed).
 * The region resolver still runs and writes region_source to supplier_leads.
 */
it('X3: leads with dadata/rossvyaz/tag/unknown sources produce correct region_source counts in supplier_leads', function (): void {
    // ── ARRANGE ───────────────────────────────────────────────────────────────

    // Supplier for X3 — NO project linked, NO snapshot → routing produces 0 deals.
    // This means RouteSupplierLeadJob still runs LeadRegionResolver, updates
    // supplier_leads.region_source, then calls logRegionResolution (with empty selected).
    $supplier = SupplierProject::factory()->create([
        'platform' => 'B1',
        'signal_type' => 'site',
        'unique_key' => X3_DOMAIN,
    ]);

    // ── Lead 1: source = 'dadata' ──────────────────────────────────────────────
    // DaData returns qc=0 + valid region name → DaDataRegionMap maps to a code.
    $phone1 = '79310000001';
    $region1Name = RussianRegions::CODE_TO_NAME[X1_MOSCOW]; // 'Москва'

    $fake1 = new FakeDaDataPhoneClient;
    $fake1->stub($phone1, qc: 0, region: $region1Name, provider: 'МТС');
    app()->instance(DaDataPhoneClient::class, $fake1);

    config(['services.dadata.enabled' => true]);

    $injector = new LeadInjector;
    $lead1 = $injector->site(domain: X3_DOMAIN, phone: $phone1, tag: null, platform: 'B1', vid: 9_130_003_001);

    // ── Lead 2: source = 'rossvyaz' ────────────────────────────────────────────
    // DaData throws → falls through to Россвязь lookup. Seed a phone_ranges row
    // covering phone2's DEF+subscriber range, mapping to subject_code=X1_MOSCOW.
    // Phone format: 7{defCode}{subscriber} = 7{931}{0000002} → DEF=931, sub=0000002.
    $phone2 = '79310000002';
    // DEF=931, subscriber=0000002. seed range from=0 to=9999999 covering it.
    $importId2 = DB::table('phone_ranges_imports')->insertGetId([
        'imported_at' => now(),
        'source_url' => 'test://x3-rossvyaz',
        'rows_inserted' => 1,
        'rows_updated' => 0,
        'checksum_sha256' => hash('sha256', 'x3-rossvyaz-931'),
        'status' => 'completed',
        'completed_at' => now(),
    ]);
    DB::table('phone_ranges')->insert([
        'def_code' => 931,
        'from_num' => 0,
        'to_num' => 9999999,
        'operator' => 'test-op-x3',
        'region' => RussianRegions::CODE_TO_NAME[X1_MOSCOW],
        'region_normalized' => null,
        'subject_code' => X1_MOSCOW,
        'imported_at' => now(),
        'import_id' => $importId2,
    ]);

    $fake2 = new FakeDaDataPhoneClient;
    $fake2->stubThrows($phone2);  // DaData throws → cascade falls to Россвязь
    app()->instance(DaDataPhoneClient::class, $fake2);
    config(['services.dadata.enabled' => true]);

    $lead2 = $injector->site(domain: X3_DOMAIN, phone: $phone2, tag: null, platform: 'B1', vid: 9_130_003_002);

    // ── Lead 3: source = 'tag' ─────────────────────────────────────────────────
    // DaData disabled → no HTTP call. No phone_range seeded for this phone.
    // Tag = region name that RegionTagResolver recognises as a valid region code.
    // RegionTagResolver maps tag text to a subject code. Use 'Москва' (maps to 82).
    // FINDING note: qc=2 path calls tagFallback() which only returns 'tag' if tagCode != null.
    // With DaData disabled, resolver falls directly to tag/rossvyaz cascade.
    $phone3 = '79310000003';
    config(['services.dadata.enabled' => false]);
    // No DaData stub needed — disabled path skips the HTTP call entirely.

    $lead3 = $injector->site(domain: X3_DOMAIN, phone: $phone3, tag: 'Москва', platform: 'B1', vid: 9_130_003_003);

    // ── Lead 4: source = 'unknown' ─────────────────────────────────────────────
    // DaData disabled. No phone_ranges row for this DEF. Empty tag.
    // → no resolution possible → source='unknown'.
    $phone4 = '79880000004';  // DEF=988, NOT seeded in phone_ranges
    config(['services.dadata.enabled' => false]);

    $lead4 = $injector->site(domain: X3_DOMAIN, phone: $phone4, tag: null, platform: 'B1', vid: 9_130_003_004);

    // ── READ region_source from supplier_leads ────────────────────────────────
    // RouteSupplierLeadJob updates supplier_leads.region_source after resolver runs.
    $leadIds = [$lead1->id, $lead2->id, $lead3->id, $lead4->id];

    $rows = DB::table('supplier_leads')
        ->whereIn('id', $leadIds)
        ->get(['id', 'region_source', 'resolved_subject_code', 'phone'])
        ->keyBy('id');

    $source1 = $rows[$lead1->id]->region_source ?? 'MISSING';
    $source2 = $rows[$lead2->id]->region_source ?? 'MISSING';
    $source3 = $rows[$lead3->id]->region_source ?? 'MISSING';
    $source4 = $rows[$lead4->id]->region_source ?? 'MISSING';

    fwrite(STDOUT, PHP_EOL.'=== X3 SOURCE BREAKDOWN ==='.PHP_EOL);
    fwrite(STDOUT, "Lead1 (dadata   expected) region_source: {$source1} | resolved: ".($rows[$lead1->id]->resolved_subject_code ?? 'null').PHP_EOL);
    fwrite(STDOUT, "Lead2 (rossvyaz expected) region_source: {$source2} | resolved: ".($rows[$lead2->id]->resolved_subject_code ?? 'null').PHP_EOL);
    fwrite(STDOUT, "Lead3 (tag      expected) region_source: {$source3} | resolved: ".($rows[$lead3->id]->resolved_subject_code ?? 'null').PHP_EOL);
    fwrite(STDOUT, "Lead4 (unknown  expected) region_source: {$source4} | resolved: ".($rows[$lead4->id]->resolved_subject_code ?? 'null').PHP_EOL);

    // Aggregate counts from supplier_leads.
    $actualSources = array_map(fn ($id) => $rows[$id]->region_source ?? 'MISSING', $leadIds);
    $counts = array_count_values($actualSources);
    fwrite(STDOUT, 'Source counts: '.json_encode($counts, JSON_UNESCAPED_UNICODE).PHP_EOL);
    fwrite(STDOUT, '=== END X3 ==='.PHP_EOL.PHP_EOL);

    // ── ASSERT ────────────────────────────────────────────────────────────────

    // Lead 1: expect 'dadata'.
    expect($source1)->toBe('dadata',
        "FINDING: Lead1 (phone={$phone1}, qc=0, valid region from DaData) should be 'dadata'. ".
        "Got: '{$source1}'. ".
        "If 'rossvyaz': DaData response was not mapped (DaDataRegionMap may not find '{$region1Name}'). ".
        "If 'unknown': DaData is disabled or threw despite stub."
    );

    // Lead 2: expect 'rossvyaz'.
    expect($source2)->toBe('rossvyaz',
        "FINDING: Lead2 (phone={$phone2}, DaData throws, phone_ranges seeded DEF=931→Москва) should be 'rossvyaz'. ".
        "Got: '{$source2}'. ".
        "If 'unknown': Россвязь lookup didn't match (check DEF extraction: phone 7{DEF}{7-digit} = 7|931|0000002 → DEF=931). ".
        "If 'dadata': FakeDaDataPhoneClient stubThrows didn't fire (stub registration issue)."
    );

    // Lead 3: expect 'tag'.
    expect($source3)->toBe('tag',
        "FINDING: Lead3 (phone={$phone3}, DaData disabled, tag='Москва') should be 'tag'. ".
        "Got: '{$source3}'. ".
        'KNOWN FINDING (G5 test suite): with DaData disabled, LeadRegionResolver falls through '.
        'Россвязь first. If no phone_ranges row for DEF=931 with this phone, then tagFallback() '.
        "is called. tagFallback() returns 'tag' only when tagCode!=null (valid tag→region mapping). ".
        "'Москва' should map to code 82 via RegionTagResolver. ".
        "If 'unknown': tag 'Москва' did not map to a region code in RegionTagResolver."
    );

    // Lead 4: expect 'unknown'.
    expect($source4)->toBe('unknown',
        "FINDING: Lead4 (phone={$phone4}, DaData disabled, no phone_range for DEF=988, empty tag) should be 'unknown'. ".
        "Got: '{$source4}'. ".
        "If 'rossvyaz': there is an unexpected phone_ranges row covering DEF=988. ".
        "If 'tag': empty/null tag somehow resolved to a code (check RegionTagResolver null-tag handling)."
    );

    // Aggregate assertion: 4 leads → exactly these 4 sources.
    $expectedCounts = ['dadata' => 1, 'rossvyaz' => 1, 'tag' => 1, 'unknown' => 1];
    expect($counts)->toEqual($expectedCounts,
        'FINDING: The aggregate source counts do not match expected {dadata:1, rossvyaz:1, tag:1, unknown:1}. '.
        'Got: '.json_encode($counts).'. See individual source assertions above for details.'
    );
})->group('imitation');
