<?php

namespace Tests\Feature;

use App\Models\Applicant;
use App\Models\User;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Password;
use Tests\TestCase;

class AuthenticationTest extends TestCase
{
    use RefreshDatabase;

    protected function setUp(): void
    {
        parent::setUp();
        $this->seedPlatform();
    }

    public function test_registration_creates_a_user_and_applicant_profile(): void
    {
        Event::fake([Registered::class]);

        $response = $this->post('/applicant/register', [
            'first_name' => 'Amina',
            'last_name' => 'Nakato',
            'email' => 'amina@example.test',
            'phone' => '0771234567',
            'password' => 'Password123',
            'password_confirmation' => 'Password123',
            'terms' => '1',
        ]);

        $response->assertRedirect(route('verification.notice'));

        $user = User::where('email', 'amina@example.test')->first();

        $this->assertNotNull($user);
        $this->assertAuthenticatedAs($user);
        $this->assertDatabaseHas('applicants', [
            'user_id' => $user->id,
            'first_name' => 'Amina',
            'last_name' => 'Nakato',
        ]);

        Event::assertDispatched(Registered::class);
    }

    public function test_registration_rejects_a_weak_password(): void
    {
        $this->post('/applicant/register', [
            'first_name' => 'Amina',
            'last_name' => 'Nakato',
            'email' => 'weak@example.test',
            'phone' => '0771234567',
            'password' => 'short',
            'password_confirmation' => 'short',
            'terms' => '1',
        ])->assertSessionHasErrors('password');

        $this->assertDatabaseMissing('users', ['email' => 'weak@example.test']);
    }

    public function test_registration_requires_a_unique_email(): void
    {
        $this->applicant()->user->update(['email' => 'taken@example.test']);

        $this->post('/applicant/register', [
            'first_name' => 'Someone',
            'last_name' => 'Else',
            'email' => 'taken@example.test',
            'phone' => '0771234567',
            'password' => 'Password123',
            'password_confirmation' => 'Password123',
            'terms' => '1',
        ])->assertSessionHasErrors('email');
    }

    public function test_an_applicant_can_log_in_and_is_sent_to_their_dashboard(): void
    {
        $applicant = $this->applicant();
        $applicant->user->update(['password' => Hash::make('Password123')]);

        $this->post('/applicant/login', [
            'email' => $applicant->user->email,
            'password' => 'Password123',
        ])->assertRedirect(route('applicant.dashboard'));

        $this->assertAuthenticatedAs($applicant->user);
    }

    public function test_staff_are_sent_to_the_admin_dashboard(): void
    {
        $user = $this->staff('admissions-officer');
        $user->update(['password' => Hash::make('Password123')]);

        $this->post('/applicant/login', [
            'email' => $user->email,
            'password' => 'Password123',
        ])->assertRedirect(route('admin.dashboard'));
    }

    public function test_login_fails_with_the_wrong_password(): void
    {
        $applicant = $this->applicant();

        $this->post('/applicant/login', [
            'email' => $applicant->user->email,
            'password' => 'not-the-password',
        ])->assertSessionHasErrors('email');

        $this->assertGuest();
    }

    public function test_a_failed_login_is_written_to_the_audit_log(): void
    {
        $this->post('/applicant/login', [
            'email' => 'nobody@example.test',
            'password' => 'whatever',
        ]);

        $this->assertDatabaseHas('audit_logs', ['action' => 'auth.login_failed']);
    }

    public function test_a_suspended_account_cannot_reach_the_portal(): void
    {
        $applicant = $this->applicant();
        $applicant->user->forceFill(['status' => 'suspended'])->save();

        $this->actingAs($applicant->user)
            ->get(route('applicant.dashboard'))
            ->assertRedirect(route('login'));

        $this->assertGuest();
    }

    public function test_an_unverified_applicant_is_asked_to_verify_their_email(): void
    {
        $applicant = $this->applicant();
        $applicant->user->forceFill(['email_verified_at' => null])->save();

        $this->actingAs($applicant->user)
            ->get(route('applicant.dashboard'))
            ->assertRedirect(route('verification.notice'));
    }

    public function test_a_password_reset_link_can_be_requested(): void
    {
        Notification::fake();

        $applicant = $this->applicant();

        $this->post('/forgot-password', ['email' => $applicant->user->email])
            ->assertSessionHas('status');

        Notification::assertSentTo($applicant->user, ResetPassword::class);
    }

    public function test_the_password_reset_form_does_not_reveal_whether_an_account_exists(): void
    {
        Notification::fake();

        $this->post('/forgot-password', ['email' => 'unknown@example.test'])
            ->assertSessionHas('status')
            ->assertSessionHasNoErrors();

        Notification::assertNothingSent();
    }

    public function test_a_password_can_be_reset_with_a_valid_token(): void
    {
        $applicant = $this->applicant();
        $token = Password::createToken($applicant->user);

        $this->post('/reset-password', [
            'token' => $token,
            'email' => $applicant->user->email,
            'password' => 'BrandNew123',
            'password_confirmation' => 'BrandNew123',
        ])->assertRedirect(route('login'));

        $this->assertTrue(Hash::check('BrandNew123', $applicant->user->fresh()->password));
    }

    public function test_a_signed_in_user_can_log_out(): void
    {
        $applicant = $this->applicant();

        $this->actingAs($applicant->user)
            ->post('/logout')
            ->assertRedirect(route('home'));

        $this->assertGuest();
    }
}
