<?php

namespace Tests\Feature\Auth;

use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Http;
use Tests\TestCase;

class ForcedPasswordChangeTest extends TestCase
{
    use RefreshDatabase;

    public function test_login_redirects_to_dashboard_when_password_is_still_fresh(): void
    {
        $user = User::factory()->create([
            'password' => Hash::make('secret123'),
            'password_changed_at' => now()->subMonths(2),
        ]);

        Http::fake([
            'www.google.com/recaptcha/*' => Http::response(['success' => true], 200),
        ]);

        $response = $this->post(route('login.post'), [
            'email' => $user->email,
            'password' => 'secret123',
            'g-recaptcha-response' => 'token-valid',
        ]);

        $response->assertRedirect(route('dashboard'));
        $this->assertAuthenticatedAs($user);
    }

    public function test_login_redirects_to_required_password_change_when_password_date_is_empty(): void
    {
        $user = User::factory()->create([
            'password' => Hash::make('secret123'),
            'password_changed_at' => null,
        ]);

        Http::fake([
            'www.google.com/recaptcha/*' => Http::response(['success' => true], 200),
        ]);

        $response = $this->post(route('login.post'), [
            'email' => $user->email,
            'password' => 'secret123',
            'g-recaptcha-response' => 'token-valid',
        ]);

        $response->assertRedirect(route('password.force.edit'));
        $this->assertAuthenticatedAs($user);
    }

    public function test_expired_user_is_redirected_back_to_required_password_change_from_protected_pages(): void
    {
        $user = User::factory()->create([
            'password_changed_at' => now()->subMonths(3)->subSecond(),
        ]);

        $this->actingAs($user)->get(route('dashboard'))
            ->assertRedirect(route('password.force.edit'));

        $this->actingAs($user)->get(route('profile.index'))
            ->assertRedirect(route('password.force.edit'));

        $this->actingAs($user)->get(route('password.force.edit'))
            ->assertOk()
            ->assertSee('Wajib Ganti Password')
            ->assertSee('Demi keamanan akun, Anda perlu mengganti password setiap 3 bulan sekali.');
    }

    public function test_expired_user_can_logout_without_changing_password(): void
    {
        $user = User::factory()->create([
            'password_changed_at' => now()->subMonths(3)->subSecond(),
        ]);

        $this->actingAs($user)->post(route('logout'))
            ->assertRedirect(route('login'));

        $this->assertGuest();
    }

    public function test_required_password_change_fails_when_password_is_missing(): void
    {
        $user = User::factory()->create([
            'password' => Hash::make('OldPass123!'),
            'password_changed_at' => now()->subMonths(3)->subSecond(),
        ]);

        $response = $this->from(route('password.force.edit'))
            ->actingAs($user)
            ->put(route('password.force.update'), [
                'password' => '',
                'password_confirmation' => '',
            ]);

        $response->assertRedirect(route('password.force.edit'));
        $response->assertSessionHasErrors('password');

        $freshUser = $user->fresh();
        $this->assertTrue(Hash::check('OldPass123!', $freshUser->password));
        $this->assertTrue($freshUser->mustChangePassword());
    }

    public function test_required_password_change_fails_when_confirmation_does_not_match(): void
    {
        $user = User::factory()->create([
            'password' => Hash::make('OldPass123!'),
            'password_changed_at' => now()->subMonths(3)->subSecond(),
        ]);

        $response = $this->from(route('password.force.edit'))
            ->actingAs($user)
            ->put(route('password.force.update'), [
                'password' => 'NewPass123!',
                'password_confirmation' => 'Different123!',
            ]);

        $response->assertRedirect(route('password.force.edit'));
        $response->assertSessionHasErrors('password');

        $freshUser = $user->fresh();
        $this->assertTrue(Hash::check('OldPass123!', $freshUser->password));
        $this->assertTrue($freshUser->mustChangePassword());
    }

    public function test_required_password_change_fails_when_password_is_too_weak(): void
    {
        $user = User::factory()->create([
            'password' => Hash::make('OldPass123!'),
            'password_changed_at' => now()->subMonths(3)->subSecond(),
        ]);

        $response = $this->from(route('password.force.edit'))
            ->actingAs($user)
            ->put(route('password.force.update'), [
                'password' => 'password',
                'password_confirmation' => 'password',
            ]);

        $response->assertRedirect(route('password.force.edit'));
        $response->assertSessionHasErrors('password');

        $freshUser = $user->fresh();
        $this->assertTrue(Hash::check('OldPass123!', $freshUser->password));
        $this->assertTrue($freshUser->mustChangePassword());
    }

    public function test_required_password_change_fails_when_new_password_matches_old_password(): void
    {
        $user = User::factory()->create([
            'password' => Hash::make('OldPass123!'),
            'password_changed_at' => now()->subMonths(3)->subSecond(),
        ]);

        $response = $this->from(route('password.force.edit'))
            ->actingAs($user)
            ->put(route('password.force.update'), [
                'password' => 'OldPass123!',
                'password_confirmation' => 'OldPass123!',
            ]);

        $response->assertRedirect(route('password.force.edit'));
        $response->assertSessionHasErrors([
            'password' => 'Password baru tidak boleh sama dengan password lama.',
        ]);

        $freshUser = $user->fresh();
        $this->assertTrue(Hash::check('OldPass123!', $freshUser->password));
        $this->assertTrue($freshUser->mustChangePassword());
    }

    public function test_required_password_change_saves_new_password_and_allows_dashboard_access(): void
    {
        $this->travelTo(now()->setTime(10, 0));

        $user = User::factory()->create([
            'password' => Hash::make('OldPass123!'),
            'password_changed_at' => now()->subMonths(3)->subSecond(),
        ]);

        $response = $this->actingAs($user)->put(route('password.force.update'), [
            'password' => 'NewPass123!',
            'password_confirmation' => 'NewPass123!',
        ]);

        $response->assertRedirect(route('dashboard'));

        $freshUser = $user->fresh();
        $this->assertTrue(Hash::check('NewPass123!', $freshUser->password));
        $this->assertTrue($freshUser->password_changed_at->equalTo(now()));
        $this->assertFalse($freshUser->mustChangePassword());

        $this->actingAs($freshUser)->get(route('dashboard'))->assertOk();
    }
}
