<?php

declare(strict_types=1);

use App\Jobs\RouteSupplierLeadJob;
use App\Models\SupplierLead;
use App\Models\SystemSetting;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Bus;

uses(DatabaseTransactions::class);

beforeEach(function () {
    SystemSetting::query()->where('key', 'supplier_webhook_secret')->update(['value' => 'test-secret-32chars-aaaaaaaaaaaaaa']);
    SystemSetting::query()->where('key', 'supplier_ip_allowlist')->update(['value' => '[]']);
});

it('returns 404 for invalid secret', function () {
    $response = $this->postJson('/api/webhook/supplier/wrong-secret', [
        'vid' => 1, 'project' => 'B1_test.ru', 'phone' => '79991234567', 'time' => time(),
    ]);
    $response->assertStatus(404);
});

it('returns 404 if IP not in allowlist (when allowlist non-empty)', function () {
    SystemSetting::query()->where('key', 'supplier_ip_allowlist')
        ->update(['value' => '["1.2.3.4", "10.0.0.0/24"]']);

    $response = $this->withServerVariables(['REMOTE_ADDR' => '5.6.7.8'])
        ->postJson('/api/webhook/supplier/test-secret-32chars-aaaaaaaaaaaaaa', [
            'vid' => 1, 'project' => 'B1_test.ru', 'phone' => '79991234567', 'time' => time(),
        ]);
    $response->assertStatus(404);
});

it('passes IP allowlist when IP matches CIDR', function () {
    SystemSetting::query()->where('key', 'supplier_ip_allowlist')
        ->update(['value' => '["10.0.0.0/24"]']);
    Bus::fake();

    $response = $this->withServerVariables(['REMOTE_ADDR' => '10.0.0.50'])
        ->postJson('/api/webhook/supplier/test-secret-32chars-aaaaaaaaaaaaaa', [
            'vid' => 1, 'project' => 'B1_test.ru', 'phone' => '79991234567', 'time' => time(),
        ]);
    $response->assertStatus(202);
});

it('inserts supplier_lead row + dispatches RouteSupplierLeadJob', function () {
    Bus::fake();

    $response = $this->postJson('/api/webhook/supplier/test-secret-32chars-aaaaaaaaaaaaaa', [
        'vid' => 432176649,
        'project' => 'B1_vashinvestor.ru',
        'tag' => 'Ваш инвестор',
        'phone' => '79991234567',
        'phones' => ['79991234567'],
        'time' => time(),
    ]);

    $response->assertStatus(202);
    expect(SupplierLead::where('vid', 432176649)->exists())->toBeTrue();
    Bus::assertDispatched(RouteSupplierLeadJob::class);
});

it('returns 200 OK on duplicate vid (idempotency)', function () {
    SupplierLead::factory()->create(['vid' => 12345]);
    Bus::fake();

    $response = $this->postJson('/api/webhook/supplier/test-secret-32chars-aaaaaaaaaaaaaa', [
        'vid' => 12345,
        'project' => 'B1_test.ru',
        'phone' => '79991234567',
        'time' => time(),
    ]);

    $response->assertStatus(200);
    expect(SupplierLead::where('vid', 12345)->count())->toBe(1);
    Bus::assertNotDispatched(RouteSupplierLeadJob::class);
});

it('rejects invalid payload (missing vid) with 422', function () {
    $response = $this->postJson('/api/webhook/supplier/test-secret-32chars-aaaaaaaaaaaaaa', [
        'project' => 'B1_test.ru', 'phone' => '79991234567', 'time' => time(),
    ]);
    $response->assertStatus(422)->assertJsonValidationErrors('vid');
});

it('rejects invalid phone format (not 7XXXXXXXXXX) with 422', function () {
    $response = $this->postJson('/api/webhook/supplier/test-secret-32chars-aaaaaaaaaaaaaa', [
        'vid' => 1, 'project' => 'B1_test.ru', 'phone' => '89991234567', 'time' => time(),
    ]);
    $response->assertStatus(422);
});

it('accepts project without B[123]_ prefix as DIRECT (Phase 3)', function () {
    // Фаза 3: требование префикса B1_/B2_/B3_ снято намеренно — проект без префикса
    // принимается (202) и маршрутизируется как platform=DIRECT, чтобы не терять заявки.
    $response = $this->postJson('/api/webhook/supplier/test-secret-32chars-aaaaaaaaaaaaaa', [
        'vid' => 1, 'project' => 'invalid_format', 'phone' => '79991234567', 'time' => time(),
    ]);
    $response->assertStatus(202);
});

it('blocks empty IP allowlist в production env (Plan 2.6 fix #ii)', function () {
    // beforeEach уже выставил secret valid + allowlist '[]'.
    // На production env пустой allowlist должен fail-closed → 404.
    app()->detectEnvironment(fn () => 'production');
    Bus::fake();

    $response = $this->postJson('/api/webhook/supplier/test-secret-32chars-aaaaaaaaaaaaaa', [
        'vid' => 1, 'project' => 'B1_test.ru', 'phone' => '79991234567', 'time' => time(),
    ]);

    $response->assertStatus(404);
});

it('allows empty IP allowlist в testing env (Plan 2.6 fix #ii — fail-open для dev)', function () {
    // beforeEach уже выставил allowlist '[]'. Testing env (default) — пропускает.
    Bus::fake();

    $response = $this->postJson('/api/webhook/supplier/test-secret-32chars-aaaaaaaaaaaaaa', [
        'vid' => 999, 'project' => 'B1_test.ru', 'phone' => '79991234567', 'time' => time(),
    ]);

    $response->assertStatus(202);
});

it('rejects timestamp older than 24h (Plan 2.6 fix #iii — partition guard)', function () {
    $response = $this->postJson('/api/webhook/supplier/test-secret-32chars-aaaaaaaaaaaaaa', [
        'vid' => 100001,
        'project' => 'B1_old-time.ru',
        'phone' => '79991234567',
        'time' => now()->subDays(2)->getTimestamp(),
    ]);

    $response->assertStatus(422)->assertJsonValidationErrors('time');
});

it('rejects timestamp more than 24h in future (Plan 2.6 fix #iii — partition guard)', function () {
    $response = $this->postJson('/api/webhook/supplier/test-secret-32chars-aaaaaaaaaaaaaa', [
        'vid' => 100002,
        'project' => 'B1_future-time.ru',
        'phone' => '79991234567',
        'time' => now()->addDays(2)->getTimestamp(),
    ]);

    $response->assertStatus(422)->assertJsonValidationErrors('time');
});

it('accepts timestamp within ±24h window (Plan 2.6 fix #iii — partition guard)', function () {
    Bus::fake();

    $response = $this->postJson('/api/webhook/supplier/test-secret-32chars-aaaaaaaaaaaaaa', [
        'vid' => 100003,
        'project' => 'B1_valid-time.ru',
        'phone' => '79991234567',
        'time' => now()->subHours(6)->getTimestamp(),
    ]);

    $response->assertStatus(202);
});
