<?php

declare(strict_types=1);

use App\Jobs\RouteSupplierLeadJob;
use App\Models\Deal;
use App\Models\Project;
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 Carbon\Carbon;
use Database\Seeders\PricingTierSeeder;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Tests\Concerns\SharesSupplierPdo;

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

/**
 * Сквозная проверка потока лида (лид → сделка) для ИЗМЕНЁННОГО и УДАЛЁННОГО проекта.
 * Вопрос владельца: «как идёт поток лидов по удалённым/изменённым проектам?»
 *
 * Инвариант матча: queryCandidates делает INNER JOIN projects ON projects.id = snap.project_id
 * И snap.snapshot_date = сегодня. Отсюда:
 *  - изменён источник (проект ЖИВ): слепок сегодня помнит старый источник → лид доезжает до сделки;
 *  - удалён проект (строки projects нет): INNER JOIN отсекает → лид НЕ падает в сироту, без краша.
 */
beforeEach(function (): void {
    // До 21:00 МСК → activeSnapshotDate = сегодня.
    Carbon::setTestNow(Carbon::parse('2026-06-25 14:00:00', 'Europe/Moscow'));
    $this->seed(PricingTierSeeder::class);
    DB::statement("SELECT set_config('app.current_tenant_id', '0', true)");
    // Новый матч по слепку ВКЛ — иначе хвост по старому источнику теряется (старый pivot-путь).
    DB::table('system_settings')->updateOrInsert(
        ['key' => 'routing_match_by_snapshot'],
        ['value' => 'true', 'type' => 'bool', 'updated_at' => now()],
    );
});

afterEach(fn () => Carbon::setTestNow());

function flowFixture(): array
{
    $tenant = Tenant::factory()->create(['balance_rub' => '10000.00', 'delivered_in_month' => 0]);
    $sp = SupplierProject::factory()->create(['platform' => 'B3', 'signal_type' => 'sms', 'unique_key' => 'Caranga']);
    $project = Project::factory()->create([
        'tenant_id' => $tenant->id,
        'signal_type' => 'sms',
        'signal_identifier' => null,
        'sms_senders' => ['Caranga'],
        'sms_keyword' => null,
        'supplier_b3_project_id' => $sp->id,
        'is_active' => true,
        'daily_limit_target' => 100,
        'effective_daily_limit_today' => 100,
        'delivered_today' => 0,
        'delivery_days_mask' => 127,
        'region_mask' => 255,
    ]);
    linkProjectToSupplier($project, $sp);

    // Слепок на СЕГОДНЯ с источником Caranga (фиксирует завтрашний заказ = сегодняшнюю раздачу).
    DB::table('project_routing_snapshots')->insert([
        'snapshot_date' => Carbon::today('Europe/Moscow')->toDateString(),
        'project_id' => $project->id,
        'tenant_id' => $tenant->id,
        'daily_limit' => 100,
        'delivery_days_mask' => 127,
        'regions' => '{}',
        'signal_type' => 'sms',
        'signal_identifier' => null,
        'sms_senders' => json_encode(['Caranga']),
        'sms_keyword' => null,
        'expected_volume' => 100,
        'delivered_count' => 0,
        'created_at' => now(),
    ]);

    return ['tenant' => $tenant, 'sp' => $sp, 'project' => $project];
}

function routeFlowLead(SupplierProject $sp, string $phone): SupplierLead
{
    $lead = SupplierLead::factory()->create([
        'platform' => 'B3',
        'phone' => $phone,
        'vid' => 700001,
        'supplier_project_id' => $sp->id,
        'raw_payload' => ['vid' => 700001, 'project' => 'B3_Caranga', 'phone' => $phone, 'time' => now()->getTimestamp()],
        'received_at' => now(),
        'source' => 'webhook',
        'processed_at' => null,
    ]);

    (new RouteSupplierLeadJob($lead->id))->handle(
        app(LeadRouter::class),
        app(SupplierProjectResolver::class),
        app(NotificationService::class),
        app(LedgerService::class),
        app(LeadDistributor::class),
        app(RegionTagResolver::class),
    );

    return $lead;
}

it('ИЗМЕНЁН источник: лид по СТАРОМУ источнику доезжает до сделки (проект жив)', function (): void {
    $fix = flowFixture();

    // Клиент сменил live-источник Caranga → NewSender (слепок сегодня всё ещё Caranga).
    DB::table('projects')->where('id', $fix['project']->id)->update(['sms_senders' => json_encode(['NewSender'])]);

    routeFlowLead($fix['sp'], '79161230001');

    DB::statement("SET LOCAL app.current_tenant_id = '{$fix['tenant']->id}'");
    expect(Deal::where('project_id', $fix['project']->id)->where('phone', '79161230001')->count())
        ->toBe(1, 'хвост по старому источнику должен создать сделку у живого проекта');
});

it('УДАЛЁН проект: лид по его источнику НЕ падает в сироту и не роняет раздачу', function (): void {
    $fix = flowFixture();

    // Жёсткое удаление проекта (как ProjectService::delete после grace) — слепок переживает (нет FK).
    DB::table('projects')->where('id', $fix['project']->id)->delete();

    // Раздача не должна бросить исключение (INNER JOIN отсекает удалённый проект).
    routeFlowLead($fix['sp'], '79161230002');

    // Дошли сюда без исключения = раздача не упала. Сделок нет — лид обработан без сироты.
    expect(Deal::where('phone', '79161230002')->count())->toBe(0, 'у удалённого проекта сделка не создаётся');
});
