<?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\Notification;
use Illuminate\Support\Facades\Password;
use PragmaRX\Google2FA\Google2FA;
use Tests\Concerns\SharesSupplierPdo;

// SharesSupplierPdo: регистрация (RegistrationService) пишет users/tenants/
// email_verifications через BYPASSRLS-подключение pgsql_supplier. Без шаринга PDO
// эти записи коммитятся мимо DatabaseTransactions и не откатываются — тест
// перестаёт быть идемпотентным (повторный прогон/«грязная» БД → 422 «email уже
// существует»). Шаринг PDO кладёт supplier-записи в ту же откатываемую транзакцию.
uses(DatabaseTransactions::class, SharesSupplierPdo::class);

/**
 * Reset the Auth manager's default guard and cached guard instances back to
 * the 'web' SessionGuard.
 *
 * Necessary because auth:sanctum middleware calls Auth::shouldUse('sanctum')
 * on every successfully-authenticated request, which permanently changes
 * config('auth.defaults.guard') to 'sanctum' in the shared test application
 * instance. Laravel feature tests reuse the same $this->app between HTTP calls,
 * so this pollution persists across requests. Any subsequent call to
 * Auth::login() (which internally calls Auth::guard()->login()) then resolves
 * to the Sanctum RequestGuard — which has no login() method — and throws a
 * BadMethodCallException.
 *
 * The reset must happen *before* any request whose controller calls Auth::login()
 * without an explicit guard argument (i.e. login and 2fa/verify routes).
 */
function resetAuthToWebGuard(): void
{
    app('auth')->forgetGuards();
    app('auth')->setDefaultDriver('web');
}

it('full auth-flow writes all expected auth_log events', function () {
    Notification::fake();

    $tenant = Tenant::factory()->create();

    // ── Step 1: Register ─────────────────────────────────────────────────────
    $reg = $this->postJson('/api/auth/register', [
        'email' => 'flow-test@example.ru',
        'password' => 'secure-pass-1234',
        'accept_offer' => true,
        'accept_pdn' => true,
        'captcha_token' => 'tok-123', // RegisterRequest требует captcha_token (CAPTCHA_FAKE_PASSES в phpunit.xml пропускает)
    ])->assertStatus(201);

    // ── Step 1b: Confirm email (новый флоу G1/SP1) ───────────────────────────
    // register создаёт pending-пользователя (is_active=false); до подтверждения
    // почты login невозможен. Подтверждаем dev-кодом → активация + register_success.
    $this->postJson('/api/auth/confirm-email', [
        'email' => 'flow-test@example.ru',
        'code' => $reg->json('_dev_plain_code'),
    ])->assertOk();
    // logs: register_success

    $user = User::where('email', 'flow-test@example.ru')->first();
    expect($user)->not->toBeNull();

    // confirm-email уже выполнил Auth::login; выходим, чтобы Step 2 проверил чистый login.
    $this->postJson('/api/auth/logout');
    resetAuthToWebGuard();

    // ── Step 2: Login (no 2FA yet) — establish session auth ──────────────────
    // No prior auth:sanctum request, so no reset needed here.
    $this->postJson('/api/auth/login', [
        'email' => 'flow-test@example.ru',
        'password' => 'secure-pass-1234',
    ])->assertOk();
    // logs: login_success (first direct login, 2FA not yet enabled)

    // ── Step 3: 2FA init (session-authenticated via web guard) ───────────────
    // auth:sanctum middleware → shouldUse('sanctum') → default becomes 'sanctum'
    $this->postJson('/api/2fa/init')->assertOk();
    // logs: 2fa_setup_init
    $secret = session('auth.pending_totp_secret');
    expect($secret)->not->toBeNull();

    // ── Step 4: 2FA confirm ───────────────────────────────────────────────────
    $google2fa = new Google2FA;
    $code = $google2fa->getCurrentOtp($secret);
    // auth:sanctum middleware → shouldUse('sanctum') again
    $this->postJson('/api/2fa/confirm', ['code' => $code])->assertOk();
    // logs: 2fa_setup_confirmed  (totp_enabled now true)

    // ── Step 5: Logout ────────────────────────────────────────────────────────
    // auth:sanctum middleware → shouldUse('sanctum') again
    $this->postJson('/api/auth/logout')->assertOk();
    // logs: logout

    // ── Step 6: Login with 2FA enabled ────────────────────────────────────────
    // auth.defaults.guard is now 'sanctum' from previous auth:sanctum requests.
    // Reset to 'web' so Auth::login() inside AuthController::login() finds the
    // SessionGuard (which implements login()) rather than the RequestGuard.
    resetAuthToWebGuard();

    $this->postJson('/api/auth/login', [
        'email' => 'flow-test@example.ru',
        'password' => 'secure-pass-1234',
    ])->assertOk();
    // requires_2fa=true, pending_user_id stored in session

    // ── Step 7: 2FA verify — completes login ─────────────────────────────────
    // No auth:sanctum request happened since the last reset, so no reset needed.
    $validCode = $google2fa->getCurrentOtp($secret);
    $this->postJson('/api/auth/2fa/verify', ['code' => $validCode])->assertOk();
    // logs: 2fa_verify_success

    // ── Step 8: 2FA disable (session-authenticated from step 7) ──────────────
    // auth:sanctum middleware → shouldUse('sanctum') again
    $this->postJson('/api/2fa/disable', ['password' => 'secure-pass-1234'])->assertOk();
    // logs: 2fa_disabled

    // ── Step 9: Logout ────────────────────────────────────────────────────────
    // auth:sanctum middleware → shouldUse('sanctum') again
    $this->postJson('/api/auth/logout')->assertOk();

    // ── Step 10: Login without 2FA — direct login_success ────────────────────
    // Reset again: auth.defaults.guard is 'sanctum' from Step 8+9 auth:sanctum.
    resetAuthToWebGuard();

    $this->postJson('/api/auth/login', [
        'email' => 'flow-test@example.ru',
        'password' => 'secure-pass-1234',
    ])->assertOk();
    // logs: login_success (direct login, 2FA now disabled)

    // ── Step 11: Forgot password ──────────────────────────────────────────────
    $this->postJson('/api/auth/logout')->assertOk();

    $this->postJson('/api/auth/forgot', [
        'email' => 'flow-test@example.ru',
    ])->assertOk();
    // logs: password_reset_requested

    // ── Step 12: Reset password ───────────────────────────────────────────────
    $token = Password::createToken($user);
    $this->postJson('/api/auth/reset-password', [
        'token' => $token,
        'email' => 'flow-test@example.ru',
        'password' => 'new-secure-pass-5678',
        'password_confirmation' => 'new-secure-pass-5678',
    ])->assertOk();
    // logs: password_reset_completed

    // ── Assert all expected events were recorded for this user ────────────────
    $events = DB::table('auth_log')
        ->where('user_id', $user->id)
        ->pluck('event')
        ->all();

    expect($events)->toContain(
        'register_success',
        '2fa_setup_init',
        '2fa_setup_confirmed',
        'logout',
        'login_success',
        '2fa_verify_success',
        '2fa_disabled',
        'password_reset_requested',
        'password_reset_completed',
    );
});
