<?php

declare(strict_types=1);

use App\Mail\ReminderDueNotification;
use App\Models\InAppNotification;
use App\Models\Reminder;
use App\Models\Tenant;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Mail;

uses(DatabaseTransactions::class);

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

function dueReminder(int $tenantId, int $userId, array $overrides = []): Reminder
{
    return Reminder::create(array_merge([
        'tenant_id' => $tenantId,
        'deal_id' => 42,
        'text' => 'Перезвонить',
        'remind_at' => Carbon::now()->subMinute(),  // due
        'created_by' => $userId,
        'is_sent' => false,
    ], $overrides));
}

test('dispatch-due: due-reminder → email + inapp + is_sent=true', function () {
    $tenant = Tenant::factory()->create();
    $user = User::factory()->create([
        'tenant_id' => $tenant->id,
        'notification_preferences' => [
            'reminder' => ['inapp' => true, 'push' => true, 'email' => true],
        ],
    ]);
    $reminder = dueReminder($tenant->id, $user->id);

    $this->artisan('reminders:dispatch-due')->assertSuccessful();

    Mail::assertSent(ReminderDueNotification::class, 1);
    Mail::assertSent(fn (ReminderDueNotification $m) => $m->reminder->id === $reminder->id && $m->hasTo($user->email));

    expect(InAppNotification::query()->where('event', 'reminder')->count())->toBe(1);

    $reminder->refresh();
    expect($reminder->is_sent)->toBeTrue();
    expect($reminder->sent_at)->not->toBeNull();
});

test('dispatch-due: future-reminder skip', function () {
    $tenant = Tenant::factory()->create();
    $user = User::factory()->create(['tenant_id' => $tenant->id]);
    Reminder::create([
        'tenant_id' => $tenant->id,
        'deal_id' => 42,
        'text' => 'Future',
        'remind_at' => Carbon::now()->addHour(),  // future
        'created_by' => $user->id,
        'is_sent' => false,
    ]);

    $this->artisan('reminders:dispatch-due')->assertSuccessful();
    Mail::assertNothingSent();
});

test('dispatch-due: completed reminder skip', function () {
    $tenant = Tenant::factory()->create();
    $user = User::factory()->create(['tenant_id' => $tenant->id]);
    dueReminder($tenant->id, $user->id, ['completed_at' => Carbon::now()->subMinute()]);

    $this->artisan('reminders:dispatch-due')->assertSuccessful();
    Mail::assertNothingSent();
});

test('dispatch-due: уже sent skip (is_sent=true)', function () {
    $tenant = Tenant::factory()->create();
    $user = User::factory()->create(['tenant_id' => $tenant->id]);
    dueReminder($tenant->id, $user->id, ['is_sent' => true, 'sent_at' => Carbon::now()->subMinute()]);

    $this->artisan('reminders:dispatch-due')->assertSuccessful();
    Mail::assertNothingSent();
});

test('dispatch-due: assignee получает (вместо created_by)', function () {
    $tenant = Tenant::factory()->create();
    $creator = User::factory()->create(['tenant_id' => $tenant->id, 'email' => 'creator@example.ru']);
    $assignee = User::factory()->create([
        'tenant_id' => $tenant->id,
        'email' => 'assignee@example.ru',
        'notification_preferences' => [
            'reminder' => ['inapp' => false, 'push' => false, 'email' => true],
        ],
    ]);

    Reminder::create([
        'tenant_id' => $tenant->id,
        'deal_id' => 42,
        'text' => 'Перезвонить',
        'remind_at' => Carbon::now()->subMinute(),
        'created_by' => $creator->id,
        'assignee_id' => $assignee->id,
        'is_sent' => false,
    ]);

    $this->artisan('reminders:dispatch-due')->assertSuccessful();

    Mail::assertSent(fn (ReminderDueNotification $m) => $m->hasTo('assignee@example.ru'));
    Mail::assertNotSent(fn (ReminderDueNotification $m) => $m->hasTo('creator@example.ru'));
});

test('dispatch-due: deactivated user — НЕ получает', function () {
    $tenant = Tenant::factory()->create();
    $user = User::factory()->inactive()->create(['tenant_id' => $tenant->id]);
    $reminder = dueReminder($tenant->id, $user->id);

    $this->artisan('reminders:dispatch-due')->assertSuccessful();

    Mail::assertNothingSent();
    expect(InAppNotification::query()->count())->toBe(0);

    // Reminder всё равно помечается is_sent=true (чтобы не пытаться слать снова).
    $reminder->refresh();
    expect($reminder->is_sent)->toBeTrue();
});

test('dispatch-due: prefs.reminder.email=false — только inapp', function () {
    $tenant = Tenant::factory()->create();
    $user = User::factory()->create([
        'tenant_id' => $tenant->id,
        'notification_preferences' => [
            'reminder' => ['inapp' => true, 'push' => false, 'email' => false],
        ],
    ]);
    dueReminder($tenant->id, $user->id);

    $this->artisan('reminders:dispatch-due')->assertSuccessful();

    Mail::assertNothingSent();
    expect(InAppNotification::query()->where('event', 'reminder')->count())->toBe(1);
});

test('dispatch-due --dry-run: не шлёт + не помечает is_sent', function () {
    $tenant = Tenant::factory()->create();
    $user = User::factory()->create(['tenant_id' => $tenant->id]);
    $reminder = dueReminder($tenant->id, $user->id);

    $this->artisan('reminders:dispatch-due', ['--dry-run' => true])->assertSuccessful();

    Mail::assertNothingSent();
    $reminder->refresh();
    expect($reminder->is_sent)->toBeFalse();
});

test('dispatch-due: 3 due-reminder → 3 sent', function () {
    $tenant = Tenant::factory()->create();
    $user = User::factory()->create([
        'tenant_id' => $tenant->id,
        'notification_preferences' => [
            'reminder' => ['inapp' => false, 'push' => false, 'email' => true],
        ],
    ]);
    dueReminder($tenant->id, $user->id, ['deal_id' => 1]);
    dueReminder($tenant->id, $user->id, ['deal_id' => 2]);
    dueReminder($tenant->id, $user->id, ['deal_id' => 3]);

    $this->artisan('reminders:dispatch-due')->assertSuccessful();

    Mail::assertSent(ReminderDueNotification::class, 3);
});

test('dispatch-due: --limit=1 ограничивает выдачу', function () {
    $tenant = Tenant::factory()->create();
    $user = User::factory()->create(['tenant_id' => $tenant->id]);
    dueReminder($tenant->id, $user->id, ['deal_id' => 1]);
    dueReminder($tenant->id, $user->id, ['deal_id' => 2]);

    $this->artisan('reminders:dispatch-due', ['--limit' => 1])->assertSuccessful();

    Mail::assertSent(ReminderDueNotification::class, 1);
});

test('dispatch-due: разные tenant\'ы изолируются (RLS)', function () {
    $tenantA = Tenant::factory()->create();
    $userA = User::factory()->create([
        'tenant_id' => $tenantA->id,
        'email' => 'a@example.ru',
        'notification_preferences' => ['reminder' => ['inapp' => true, 'push' => false, 'email' => true]],
    ]);
    $tenantB = Tenant::factory()->create();
    $userB = User::factory()->create([
        'tenant_id' => $tenantB->id,
        'email' => 'b@example.ru',
        'notification_preferences' => ['reminder' => ['inapp' => true, 'push' => false, 'email' => true]],
    ]);

    dueReminder($tenantA->id, $userA->id);
    dueReminder($tenantB->id, $userB->id);

    $this->artisan('reminders:dispatch-due')->assertSuccessful();

    Mail::assertSent(ReminderDueNotification::class, 2);
    expect(InAppNotification::query()->where('tenant_id', $tenantA->id)->count())->toBe(1);
    expect(InAppNotification::query()->where('tenant_id', $tenantB->id)->count())->toBe(1);
});
