<?php

declare(strict_types=1);

use App\Jobs\ProcessWebhookJob;
use App\Mail\NewLeadNotification;
use App\Models\Deal;
use App\Models\Tenant;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Mail;

/**
 * Тесты email-уведомления о новом лиде (ТЗ §18.5, событие new_lead).
 *
 * Проверяет интеграцию NotificationService → ProcessWebhookJob: после успешного
 * chargeNewLead все активные user'ы тенанта с notification_preferences.new_lead.email=true
 * получают NewLeadNotification. Mail::fake() перехватывает реальную отправку.
 *
 * Schema-default: notification_preferences.new_lead.email=false → по умолчанию
 * никто не получает emails. Тесты явно ставят email=true для нужных user'ов.
 */
uses(DatabaseTransactions::class);

beforeEach(function () {
    Mail::fake();
});

/**
 * @return array<string, mixed>
 */
function newLeadPayload(int $vid = 200, ?int $time = null): array
{
    return [
        'vid' => $vid,
        'project' => 'B2_Caranga',
        'tag' => 'Caranga',
        'phone' => '79001234567',
        'phones' => ['79001234567'],
        'time' => $time ?? time(),
    ];
}

/**
 * @param  array<string, mixed>  $newLeadPrefs
 */
function makeUserWithPrefs(Tenant $tenant, string $email, array $newLeadPrefs): User
{
    return User::factory()->create([
        'tenant_id' => $tenant->id,
        'email' => $email,
        'notification_preferences' => [
            'new_lead' => $newLeadPrefs,
            'reminder' => ['inapp' => true, 'push' => true, 'email' => true],
            'low_balance' => ['email' => true],
            'zero_balance' => ['email' => true],
            'topup_success' => ['email' => true],
            'invoice_paid' => ['email' => true],
            'new_device_login' => ['email' => true],
            'marketing' => ['email' => false],
        ],
    ]);
}

test('webhook: NewLeadNotification отправляется user\'ам с email=true', function () {
    $tenant = Tenant::factory()->create(['balance_leads' => 10]);
    $userOn = makeUserWithPrefs($tenant, 'on@example.ru', ['inapp' => true, 'push' => true, 'email' => true]);

    (new ProcessWebhookJob($tenant->id, newLeadPayload()))->handle();

    Mail::assertSent(NewLeadNotification::class, 1);
    Mail::assertSent(NewLeadNotification::class, function (NewLeadNotification $mail) use ($userOn): bool {
        return $mail->manager->id === $userOn->id
            && $mail->hasTo('on@example.ru');
    });
});

test('webhook: user с email=false НЕ получает', function () {
    $tenant = Tenant::factory()->create(['balance_leads' => 10]);
    makeUserWithPrefs($tenant, 'off@example.ru', ['inapp' => true, 'push' => true, 'email' => false]);

    (new ProcessWebhookJob($tenant->id, newLeadPayload()))->handle();

    Mail::assertNothingSent();
});

test('webhook: schema-default не шлёт (new_lead.email=false по дефолту)', function () {
    $tenant = Tenant::factory()->create(['balance_leads' => 10]);
    // Не передаём notification_preferences — берётся schema DEFAULT.
    User::factory()->create([
        'tenant_id' => $tenant->id,
        'email' => 'default@example.ru',
    ]);

    (new ProcessWebhookJob($tenant->id, newLeadPayload()))->handle();

    Mail::assertNothingSent();
});

test('webhook: рассылается всем активным user\'ам с email=true', function () {
    $tenant = Tenant::factory()->create(['balance_leads' => 10]);
    makeUserWithPrefs($tenant, 'a@example.ru', ['email' => true]);
    makeUserWithPrefs($tenant, 'b@example.ru', ['email' => true]);
    makeUserWithPrefs($tenant, 'c@example.ru', ['email' => false]);

    (new ProcessWebhookJob($tenant->id, newLeadPayload()))->handle();

    Mail::assertSent(NewLeadNotification::class, 2);
    Mail::assertSent(fn (NewLeadNotification $mail) => $mail->hasTo('a@example.ru'));
    Mail::assertSent(fn (NewLeadNotification $mail) => $mail->hasTo('b@example.ru'));
    Mail::assertNotSent(fn (NewLeadNotification $mail) => $mail->hasTo('c@example.ru'));
});

test('webhook: inactive user с email=true НЕ получает (is_active=false)', function () {
    $tenant = Tenant::factory()->create(['balance_leads' => 10]);
    User::factory()->inactive()->create([
        'tenant_id' => $tenant->id,
        'email' => 'inactive@example.ru',
        'notification_preferences' => [
            'new_lead' => ['email' => true],
        ],
    ]);

    (new ProcessWebhookJob($tenant->id, newLeadPayload()))->handle();

    Mail::assertNothingSent();
});

test('webhook: soft-deleted user НЕ получает', function () {
    $tenant = Tenant::factory()->create(['balance_leads' => 10]);
    $user = makeUserWithPrefs($tenant, 'deleted@example.ru', ['email' => true]);
    $user->delete();

    (new ProcessWebhookJob($tenant->id, newLeadPayload()))->handle();

    Mail::assertNothingSent();
});

test('webhook: user другого тенанта НЕ получает (изоляция)', function () {
    $tenantA = Tenant::factory()->create(['balance_leads' => 10]);
    $tenantB = Tenant::factory()->create(['balance_leads' => 10]);
    makeUserWithPrefs($tenantA, 'a@example.ru', ['email' => true]);
    makeUserWithPrefs($tenantB, 'b@example.ru', ['email' => true]);

    (new ProcessWebhookJob($tenantA->id, newLeadPayload()))->handle();

    Mail::assertSent(NewLeadNotification::class, 1);
    Mail::assertSent(fn (NewLeadNotification $mail) => $mail->hasTo('a@example.ru'));
    Mail::assertNotSent(fn (NewLeadNotification $mail) => $mail->hasTo('b@example.ru'));
});

test('webhook: дубль-сделка (Биз-19) НЕ шлёт повторное уведомление', function () {
    $tenant = Tenant::factory()->create(['balance_leads' => 10]);
    makeUserWithPrefs($tenant, 'on@example.ru', ['email' => true]);

    // Первая сделка — master.
    (new ProcessWebhookJob($tenant->id, newLeadPayload(vid: 1)))->handle();
    Mail::assertSent(NewLeadNotification::class, 1);

    // Вторая сделка с тем же phone в окне 24 ч — дубль, баланс НЕ списывается,
    // chargeNewLead НЕ вызывается, уведомление НЕ шлётся.
    (new ProcessWebhookJob($tenant->id, newLeadPayload(vid: 2)))->handle();
    Mail::assertSent(NewLeadNotification::class, 1); // всё ещё одно
});

test('webhook: повторный vid (idempotent UPDATE) НЕ шлёт повторное уведомление', function () {
    $tenant = Tenant::factory()->create(['balance_leads' => 10]);
    makeUserWithPrefs($tenant, 'on@example.ru', ['email' => true]);

    (new ProcessWebhookJob($tenant->id, newLeadPayload(vid: 100)))->handle();
    Mail::assertSent(NewLeadNotification::class, 1);

    // Повторный webhook с тем же vid — UPDATE, не INSERT. wasRecentlyCreated=false → return.
    (new ProcessWebhookJob($tenant->id, newLeadPayload(vid: 100)))->handle();
    Mail::assertSent(NewLeadNotification::class, 1);
});

test('webhook: balance=0 (RejectedDealsLog) НЕ шлёт NewLeadNotification', function () {
    $tenant = Tenant::factory()->create(['balance_leads' => 0]);
    makeUserWithPrefs($tenant, 'on@example.ru', ['email' => true]);

    (new ProcessWebhookJob($tenant->id, newLeadPayload()))->handle();

    // chargeNewLead НЕ вызывается при balance=0 — NewLeadNotification не шлётся.
    // (ZeroBalanceNotification ШЛЁТСЯ — это покрывается отдельным тестом.)
    Mail::assertNotSent(NewLeadNotification::class);
    expect(Deal::query()->where('tenant_id', $tenant->id)->count())->toBe(0);
});

test('NewLeadNotification: subject содержит project_name', function () {
    $tenant = Tenant::factory()->create(['balance_leads' => 10]);
    makeUserWithPrefs($tenant, 'on@example.ru', ['email' => true]);

    (new ProcessWebhookJob($tenant->id, newLeadPayload()))->handle();

    Mail::assertSent(NewLeadNotification::class, function (NewLeadNotification $mail): bool {
        return str_contains($mail->envelope()->subject, 'Caranga');
    });
});
