<?php

declare(strict_types=1);

use App\Models\Tenant;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;

uses(DatabaseTransactions::class);

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

test('login_success пишет запись в auth_log', function () {
    User::factory()->create([
        'tenant_id' => $this->tenant->id,
        'email' => 'log-success@example.ru',
        'password_hash' => Hash::make('right-password'),
    ]);

    $this->postJson('/api/auth/login', [
        'email' => 'log-success@example.ru',
        'password' => 'right-password',
    ])->assertOk();

    $row = DB::table('auth_log')
        ->where('email', 'log-success@example.ru')
        ->where('event', 'login_success')
        ->first();

    expect($row)->not->toBeNull();
    expect($row->actor_type)->toBe('tenant_user');
    expect($row->tenant_id)->toBe($this->tenant->id);
});

test('login_failed (wrong password) пишет в auth_log с failure_reason=invalid_password', function () {
    User::factory()->create([
        'tenant_id' => $this->tenant->id,
        'email' => 'log-fail@example.ru',
        'password_hash' => Hash::make('right-password'),
    ]);

    $this->postJson('/api/auth/login', [
        'email' => 'log-fail@example.ru',
        'password' => 'wrong-pass-attempt',
    ])->assertStatus(422);

    $row = DB::table('auth_log')
        ->where('email', 'log-fail@example.ru')
        ->where('event', 'login_failed')
        ->first();

    expect($row)->not->toBeNull();
    expect($row->failure_reason)->toBe('invalid_password');
});

test('login_failed для unknown email пишет с failure_reason=unknown_email', function () {
    $this->postJson('/api/auth/login', [
        'email' => 'nobody@example.ru',
        'password' => 'wrong-pass-attempt',
    ])->assertStatus(422);

    $row = DB::table('auth_log')
        ->where('email', 'nobody@example.ru')
        ->where('event', 'login_failed')
        ->first();

    expect($row)->not->toBeNull();
    expect($row->failure_reason)->toBe('unknown_email');
    expect($row->user_id)->toBeNull();
});

test('IP-lockout: после 10 login_failed с одного IP за час — следующий → 429', function () {
    User::factory()->create([
        'tenant_id' => $this->tenant->id,
        'email' => 'ip-lockout-victim@example.ru',
        'password_hash' => Hash::make('right-password'),
    ]);

    // Эмулируем 10 неудачных попыток с разных email с одного IP
    // (через прямой INSERT в auth_log, чтобы не триггерить email-rate-limit).
    for ($i = 0; $i < 10; $i++) {
        DB::table('auth_log')->insert([
            'actor_type' => 'tenant_user',
            'event' => 'login_failed',
            'email' => "victim{$i}@example.ru",
            'ip_address' => '127.0.0.1',
            'failure_reason' => 'unknown_email',
            'created_at' => now()->subMinutes(5),
        ]);
    }

    // Следующий login (даже с правильным паролем) → 429.
    $r = $this->postJson('/api/auth/login', [
        'email' => 'ip-lockout-victim@example.ru',
        'password' => 'right-password',
    ]);

    $r->assertStatus(429);
    expect($r->json('message'))->toContain('с этого IP');
    expect($r->headers->get('Retry-After'))->toBe('3600');
});

test('IP-lockout не срабатывает на 9 неудач (под порогом)', function () {
    User::factory()->create([
        'tenant_id' => $this->tenant->id,
        'email' => 'ok@example.ru',
        'password_hash' => Hash::make('right-password'),
    ]);

    for ($i = 0; $i < 9; $i++) {
        DB::table('auth_log')->insert([
            'actor_type' => 'tenant_user',
            'event' => 'login_failed',
            'email' => "any{$i}@example.ru",
            'ip_address' => '127.0.0.1',
            'failure_reason' => 'unknown_email',
            'created_at' => now()->subMinutes(5),
        ]);
    }

    $this->postJson('/api/auth/login', [
        'email' => 'ok@example.ru',
        'password' => 'right-password',
    ])->assertOk();
});

test('IP-lockout: окно 1 час — старые записи (>1ч) не блокируют', function () {
    User::factory()->create([
        'tenant_id' => $this->tenant->id,
        'email' => 'old-fails@example.ru',
        'password_hash' => Hash::make('right-password'),
    ]);

    // 15 неудач, но старше часа → не учитываются.
    for ($i = 0; $i < 15; $i++) {
        DB::table('auth_log')->insert([
            'actor_type' => 'tenant_user',
            'event' => 'login_failed',
            'email' => "old{$i}@example.ru",
            'ip_address' => '127.0.0.1',
            'failure_reason' => 'unknown_email',
            'created_at' => now()->subHours(2),
        ]);
    }

    $this->postJson('/api/auth/login', [
        'email' => 'old-fails@example.ru',
        'password' => 'right-password',
    ])->assertOk();
});
