<?php

namespace Tests\Feature\Auth;

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

class PasswordResetOtpTest extends TestCase
{
    use RefreshDatabase;

    public function test_forgot_password_sends_otp_using_brevo_when_api_key_is_configured(): void
    {
        Http::fake([
            'https://api.brevo.com/v3/smtp/email' => Http::response(['messageId' => 'message-id'], 201),
        ]);

        app(SettingsService::class)->set('email', 'brevo_api_key', 'brevo-secret-key', 'password', encrypted: true);
        User::factory()->create([
            'email' => 'candidate@example.com',
            'name' => 'Remote Candidate',
        ]);

        $response = $this->post(route('password.email'), [
            'email' => 'candidate@example.com',
        ]);

        $response
            ->assertRedirect(route('password.otp.verify'))
            ->assertSessionHas('status', 'If this email exists, a password reset OTP has been sent.');

        $this->assertDatabaseHas('password_reset_otps', [
            'email' => 'candidate@example.com',
            'consumed_at' => null,
        ]);

        Http::assertSent(fn ($request): bool => $request->url() === 'https://api.brevo.com/v3/smtp/email'
            && $request->hasHeader('api-key', 'brevo-secret-key')
            && $request['to'][0]['email'] === 'candidate@example.com'
            && str_contains((string) $request['textContent'], 'password reset OTP')
            && str_contains((string) $request['htmlContent'], 'Reset your password securely.')
            && str_contains((string) $request['htmlContent'], 'Your OTP'));
    }

    public function test_forgot_password_can_return_json_for_auth_modal_flow(): void
    {
        Http::fake([
            'https://api.brevo.com/v3/smtp/email' => Http::response(['messageId' => 'message-id'], 201),
        ]);

        app(SettingsService::class)->set('email', 'brevo_api_key', 'brevo-secret-key', 'password', encrypted: true);
        User::factory()->create(['email' => 'candidate@example.com']);

        $response = $this->postJson(route('password.email'), [
            'email' => 'candidate@example.com',
        ]);

        $response
            ->assertOk()
            ->assertJsonPath('message', 'If this email exists, a password reset OTP has been sent.')
            ->assertJsonPath('email', 'candidate@example.com');
    }

    public function test_forgot_password_warns_when_email_does_not_exist(): void
    {
        $response = $this->postJson(route('password.email'), [
            'email' => 'missing@example.com',
        ]);

        $response
            ->assertStatus(422)
            ->assertJsonPath('message', 'No account was found for this email address.')
            ->assertJsonValidationErrors('email');

        $this->assertDatabaseCount('password_reset_otps', 0);
    }

    public function test_user_can_reset_password_after_valid_otp_verification(): void
    {
        $user = User::factory()->create([
            'email' => 'candidate@example.com',
            'password' => Hash::make('OldSecret123'),
            'password_set_at' => now()->subDay(),
        ]);

        PasswordResetOtp::query()->create([
            'email' => 'candidate@example.com',
            'otp_hash' => Hash::make('123456'),
            'expires_at' => now()->addMinutes(10),
        ]);

        $response = $this->post(route('password.otp.reset'), [
            'email' => 'candidate@example.com',
            'otp' => '123456',
            'password' => 'NewSecret123',
            'password_confirmation' => 'NewSecret123',
        ]);

        $response
            ->assertRedirect(route('login'))
            ->assertSessionHas('status', 'Your password has been reset. You can now login.');

        $this->assertTrue(Hash::check('NewSecret123', $user->fresh()->password));
        $this->assertNotNull($user->fresh()->password_set_at);
        $this->assertNotNull(PasswordResetOtp::query()->firstOrFail()->consumed_at);
    }

    public function test_password_reset_can_return_json_for_auth_modal_flow(): void
    {
        $user = User::factory()->create([
            'email' => 'candidate@example.com',
            'password' => Hash::make('OldSecret123'),
            'password_set_at' => now()->subDay(),
        ]);

        PasswordResetOtp::query()->create([
            'email' => 'candidate@example.com',
            'otp_hash' => Hash::make('123456'),
            'expires_at' => now()->addMinutes(10),
        ]);

        $response = $this->postJson(route('password.otp.reset'), [
            'email' => 'candidate@example.com',
            'otp' => '123456',
            'password' => 'NewSecret123',
            'password_confirmation' => 'NewSecret123',
        ]);

        $response
            ->assertOk()
            ->assertJsonPath('message', 'Your password has been reset. You can now login.')
            ->assertJsonPath('login_url', route('login'));

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