<?php

declare(strict_types=1);

use App\Models\Tenant;
use App\Models\User;
use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Notification;

uses(DatabaseTransactions::class);

beforeEach(function () {
    $this->tenant = Tenant::factory()->create();
});

test('POST /api/auth/forgot для существующего email возвращает unified 200 + создаёт password_resets row + шлёт notification', function () {
    Notification::fake();

    $user = User::factory()->create([
        'tenant_id' => $this->tenant->id,
        'email' => 'real-user@example.ru',
        'password_hash' => Hash::make('old-pass-1234'),
    ]);

    $r = $this->postJson('/api/auth/forgot', [
        'email' => 'real-user@example.ru',
    ]);

    $r->assertOk();
    expect($r->json('message'))->toContain('ссылку для сброса пароля');

    // Token записан в password_resets (config: AUTH_PASSWORD_RESET_TOKEN_TABLE=password_resets).
    $tokenRow = DB::table('password_resets')->where('email', 'real-user@example.ru')->first();
    expect($tokenRow)->not->toBeNull();
    expect($tokenRow->token)->not->toBeEmpty();

    // Notification отправлен.
    Notification::assertSentTo($user, ResetPassword::class);
});

test('POST /api/auth/forgot для НЕсуществующего email возвращает то же 200 (anti-enumeration)', function () {
    Notification::fake();

    $r = $this->postJson('/api/auth/forgot', [
        'email' => 'unknown@example.ru',
    ]);

    $r->assertOk();
    expect($r->json('message'))->toContain('ссылку для сброса пароля');

    // password_resets не должен содержать row для unknown email.
    expect(DB::table('password_resets')->where('email', 'unknown@example.ru')->exists())->toBeFalse();

    Notification::assertNothingSent();
});

test('POST /api/auth/forgot валидация email: 422 при невалидном формате', function () {
    $r = $this->postJson('/api/auth/forgot', [
        'email' => 'not-an-email',
    ]);

    $r->assertStatus(422);
    expect($r->json('errors.email'))->not->toBeEmpty();
});

test('POST /api/auth/forgot валидация email: 422 при пустом значении', function () {
    $r = $this->postJson('/api/auth/forgot', []);

    $r->assertStatus(422);
});

test('POST /api/auth/forgot rate-limit: 5 попыток / 15 мин', function () {
    Notification::fake();

    User::factory()->create([
        'tenant_id' => $this->tenant->id,
        'email' => 'forgot-locked@example.ru',
        'password_hash' => Hash::make('old-pass-1234'),
    ]);

    // 5 успешных запросов (200 каждый — unified ответ).
    for ($i = 1; $i <= 5; $i++) {
        $this->postJson('/api/auth/forgot', [
            'email' => 'forgot-locked@example.ru',
        ])->assertOk();
    }

    // 6-я → 429 + Retry-After.
    $r = $this->postJson('/api/auth/forgot', [
        'email' => 'forgot-locked@example.ru',
    ]);

    $r->assertStatus(429);
    expect($r->headers->get('Retry-After'))->not->toBeNull();
    expect($r->json('retry_after'))->toBeGreaterThan(0);
});

test('POST /api/auth/forgot rate-limit изолирован по email — Bob не блокируется когда Alice заблокирована', function () {
    Notification::fake();

    User::factory()->create([
        'tenant_id' => $this->tenant->id,
        'email' => 'alice-forgot@example.ru',
        'password_hash' => Hash::make('alice-pass-1234'),
    ]);
    User::factory()->create([
        'tenant_id' => $this->tenant->id,
        'email' => 'bob-forgot@example.ru',
        'password_hash' => Hash::make('bob-pass-1234'),
    ]);

    // Alice — 5 раз → заблокирована.
    for ($i = 1; $i <= 5; $i++) {
        $this->postJson('/api/auth/forgot', ['email' => 'alice-forgot@example.ru'])->assertOk();
    }
    $this->postJson('/api/auth/forgot', ['email' => 'alice-forgot@example.ru'])->assertStatus(429);

    // Bob проходит спокойно.
    $this->postJson('/api/auth/forgot', ['email' => 'bob-forgot@example.ru'])->assertOk();
});
