<?php

namespace Tests\Feature\Auth;

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

class PasswordResetTest extends TestCase
{
    use RefreshDatabase;

    public function test_forgot_password_with_email_user(): void
    {
        $user = User::factory()->create([
            'email' => 'test@example.com',
            'phone' => null,
        ]);

        $response = $this->postJson('/api/v1/forgot-password', [
            'username' => 'test@example.com',
        ]);

        $response->assertStatus(200)
            ->assertJson([
                'message' => 'If an account exists with this email or phone, a password reset code has been sent.',
            ]);

        // Verify token was created in database
        $this->assertDatabaseHas('password_reset_tokens', [
            'email' => 'test@example.com',
        ]);
    }

    public function test_forgot_password_with_phone_user(): void
    {
        $user = User::factory()->create([
            'email' => null,
            'phone' => '1234567890',
        ]);

        $response = $this->postJson('/api/v1/forgot-password', [
            'username' => '1234567890',
        ]);

        $response->assertStatus(200)
            ->assertJson([
                'message' => 'If an account exists with this email or phone, a password reset code has been sent.',
            ]);

        // Verify token was created with phone: prefix
        $this->assertDatabaseHas('password_reset_tokens', [
            'email' => 'phone:1234567890',
        ]);
    }

    public function test_forgot_password_with_non_existent_user(): void
    {
        $response = $this->postJson('/api/v1/forgot-password', [
            'username' => 'nonexistent@example.com',
        ]);

        // Should still return success to prevent username enumeration
        $response->assertStatus(200)
            ->assertJson([
                'message' => 'If an account exists with this email or phone, a password reset code has been sent.',
            ]);
    }

    public function test_forgot_password_validation_requires_username(): void
    {
        $response = $this->postJson('/api/v1/forgot-password', []);

        $response->assertStatus(422)
            ->assertJsonValidationErrors(['username']);
    }

    public function test_reset_password_with_valid_token(): void
    {
        $user = User::factory()->create([
            'email' => 'test@example.com',
            'password' => Hash::make('oldpassword123'),
        ]);

        // Create a password reset token using the forgot password endpoint
        $token = Password::createToken($user);

        // Create a Sanctum token for the user
        $user->createToken('test-token');

        $response = $this->postJson('/api/v1/reset-password', [
            'username' => 'test@example.com',
            'token' => $token,
            'password' => 'newpassword123',
            'password_confirmation' => 'newpassword123',
        ]);

        $response->assertStatus(200)
            ->assertJson([
                'message' => 'Password has been reset successfully. Please login with your new password.',
            ]);

        // Verify password was updated
        $user->refresh();
        $this->assertTrue(Hash::check('newpassword123', $user->password));

        // Verify all tokens were revoked
        $this->assertDatabaseMissing('personal_access_tokens', [
            'tokenable_id' => $user->id,
        ]);
    }

    public function test_reset_password_with_phone_user(): void
    {
        $user = User::factory()->create([
            'email' => null,
            'phone' => '1234567890',
            'password' => Hash::make('oldpassword123'),
        ]);

        // Create a 6-digit code
        $token = '123456';
        DB::table('password_reset_tokens')->insert([
            'email' => 'phone:1234567890',
            'token' => Hash::make($token),
            'created_at' => now(),
        ]);

        $response = $this->postJson('/api/v1/reset-password', [
            'username' => '1234567890',
            'token' => $token,
            'password' => 'newpassword123',
            'password_confirmation' => 'newpassword123',
        ]);

        $response->assertStatus(200)
            ->assertJson([
                'message' => 'Password has been reset successfully. Please login with your new password.',
            ]);

        // Verify password was updated
        $user->refresh();
        $this->assertTrue(Hash::check('newpassword123', $user->password));
    }

    public function test_reset_password_with_invalid_token(): void
    {
        $user = User::factory()->create([
            'email' => 'test@example.com',
            'password' => Hash::make('oldpassword123'),
        ]);

        $response = $this->postJson('/api/v1/reset-password', [
            'username' => 'test@example.com',
            'token' => 'invalid-token',
            'password' => 'newpassword123',
            'password_confirmation' => 'newpassword123',
        ]);

        $response->assertStatus(400)
            ->assertJson([
                'message' => 'Invalid or expired reset token.',
            ]);

        // Verify password was NOT updated
        $user->refresh();
        $this->assertTrue(Hash::check('oldpassword123', $user->password));
    }

    public function test_reset_password_with_non_existent_user(): void
    {
        $response = $this->postJson('/api/v1/reset-password', [
            'username' => 'nonexistent@example.com',
            'token' => 'some-token',
            'password' => 'newpassword123',
            'password_confirmation' => 'newpassword123',
        ]);

        $response->assertStatus(400)
            ->assertJson([
                'message' => 'Invalid or expired reset token.',
            ]);
    }

    public function test_reset_password_validation_requires_all_fields(): void
    {
        $response = $this->postJson('/api/v1/reset-password', []);

        $response->assertStatus(422)
            ->assertJsonValidationErrors(['token', 'username', 'password']);
    }

    public function test_reset_password_password_must_be_confirmed(): void
    {
        $response = $this->postJson('/api/v1/reset-password', [
            'username' => 'test@example.com',
            'token' => 'some-token',
            'password' => 'newpassword123',
            'password_confirmation' => 'different',
        ]);

        $response->assertStatus(422)
            ->assertJsonValidationErrors(['password']);
    }

    public function test_reset_password_password_must_be_at_least_8_characters(): void
    {
        $response = $this->postJson('/api/v1/reset-password', [
            'username' => 'test@example.com',
            'token' => 'some-token',
            'password' => 'short',
            'password_confirmation' => 'short',
        ]);

        $response->assertStatus(422)
            ->assertJsonValidationErrors(['password']);
    }

    public function test_forgot_password_rate_limiting(): void
    {
        $user = User::factory()->create(['email' => 'test@example.com']);

        // Send 5 requests (limit)
        for ($i = 0; $i < 5; $i++) {
            $response = $this->postJson('/api/v1/forgot-password', [
                'username' => 'test@example.com',
            ]);
            $response->assertStatus(200);
        }

        // 6th request should be rate limited
        $response = $this->postJson('/api/v1/forgot-password', [
            'username' => 'test@example.com',
        ]);

        $response->assertStatus(429);
    }

    public function test_reset_password_rate_limiting(): void
    {
        $user = User::factory()->create(['email' => 'test@example.com']);

        // Send 5 requests (limit)
        for ($i = 0; $i < 5; $i++) {
            $response = $this->postJson('/api/v1/reset-password', [
                'username' => 'test@example.com',
                'token' => 'some-token',
                'password' => 'newpassword123',
                'password_confirmation' => 'newpassword123',
            ]);
            // Will fail with invalid token but that's ok
        }

        // 6th request should be rate limited
        $response = $this->postJson('/api/v1/reset-password', [
            'username' => 'test@example.com',
            'token' => 'some-token',
            'password' => 'newpassword123',
            'password_confirmation' => 'newpassword123',
        ]);

        $response->assertStatus(429);
    }
}
