<?php

use App\Models\PasswordHistory;
use App\Models\User;
use App\Services\Auth\PasswordPolicyService;
use App\Services\SystemConfig\SystemConfigService;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Hash;

beforeEach(function () {
    $ref = new ReflectionClass(SystemConfigService::class);
    $prop = $ref->getProperty('resolvedSettings');
    $prop->setAccessible(true);
    $prop->setValue(null, null);
    Cache::flush();
});

// ── Happy path ────────────────────────────────────────────────────────────────

test('password update succeeds with valid current password', function () {
    $user = User::factory()->create(['password' => Hash::make('current-pass')]);

    $this->actingAs($user)
        ->from('/profile')
        ->put('/password', [
            'current_password'      => 'current-pass',
            'password'              => 'New-Pass-123',
            'password_confirmation' => 'New-Pass-123',
        ])
        ->assertRedirect('/profile')
        ->assertSessionHasNoErrors();

    expect(Hash::check('New-Pass-123', $user->fresh()->password))->toBeTrue();
});

test('password update stamps password_changed_at', function () {
    app(SystemConfigService::class)->update(['password_history_count' => 0]);

    $user = User::factory()->create([
        'password'           => Hash::make('current-pass'),
        'password_changed_at' => null,
    ]);

    $this->actingAs($user)->put('/password', [
        'current_password'      => 'current-pass',
        'password'              => 'New-Pass-456',
        'password_confirmation' => 'New-Pass-456',
    ]);

    expect($user->fresh()->password_changed_at)->not->toBeNull();
});

// ── Validation ────────────────────────────────────────────────────────────────

test('wrong current password is rejected', function () {
    $user = User::factory()->create(['password' => Hash::make('real-pass')]);

    $this->actingAs($user)
        ->from('/profile')
        ->put('/password', [
            'current_password'      => 'wrong-pass',
            'password'              => 'New-Pass-789',
            'password_confirmation' => 'New-Pass-789',
        ])
        ->assertSessionHasErrorsIn('updatePassword', 'current_password');

    expect(Hash::check('real-pass', $user->fresh()->password))->toBeTrue();
});

test('mismatched confirmation is rejected', function () {
    $user = User::factory()->create(['password' => Hash::make('correct')]);

    $this->actingAs($user)
        ->from('/profile')
        ->put('/password', [
            'current_password'      => 'correct',
            'password'              => 'New-Pass-Abc',
            'password_confirmation' => 'Different-Xyz',
        ])
        ->assertSessionHasErrors();
});

// ── History enforcement ───────────────────────────────────────────────────────

test('reusing a recent password is rejected with history enabled', function () {
    app(SystemConfigService::class)->update(['password_history_count' => 3]);

    $oldHash = Hash::make('OldPassword1!');
    $user = User::factory()->create(['password' => Hash::make('current-pass')]);
    PasswordHistory::create(['user_id' => $user->id, 'password' => $oldHash]);

    $this->actingAs($user)
        ->from('/profile')
        ->put('/password', [
            'current_password'      => 'current-pass',
            'password'              => 'OldPassword1!',
            'password_confirmation' => 'OldPassword1!',
        ])
        ->assertSessionHasErrors();
});

test('history check is skipped when history_count is zero', function () {
    app(SystemConfigService::class)->update(['password_history_count' => 0]);

    $oldHash = Hash::make('Recycled-Pass!1');
    $user = User::factory()->create(['password' => Hash::make('current-pass')]);
    PasswordHistory::create(['user_id' => $user->id, 'password' => $oldHash]);

    $this->actingAs($user)
        ->from('/profile')
        ->put('/password', [
            'current_password'      => 'current-pass',
            'password'              => 'Recycled-Pass!1',
            'password_confirmation' => 'Recycled-Pass!1',
        ])
        ->assertSessionHasNoErrors();
});

// ── History recording ─────────────────────────────────────────────────────────

test('successful password change records new entry in password history', function () {
    app(SystemConfigService::class)->update(['password_history_count' => 5]);

    $user = User::factory()->create(['password' => Hash::make('current-pass')]);

    $this->actingAs($user)->put('/password', [
        'current_password'      => 'current-pass',
        'password'              => 'Brand-New-777!',
        'password_confirmation' => 'Brand-New-777!',
    ]);

    expect(PasswordHistory::where('user_id', $user->id)->count())->toBe(1);
});

// ── old password no longer works after update ─────────────────────────────────

test('old password cannot be used to log in after successful update', function () {
    $user = User::factory()->create(['password' => Hash::make('old-pass-456')]);

    $this->actingAs($user)->put('/password', [
        'current_password'      => 'old-pass-456',
        'password'              => 'Brand-New-888!',
        'password_confirmation' => 'Brand-New-888!',
    ]);

    // Old password must not match the new stored hash
    expect(Hash::check('old-pass-456', $user->fresh()->password))->toBeFalse();
    // New password must match
    expect(Hash::check('Brand-New-888!', $user->fresh()->password))->toBeTrue();
});

// ── Guest ─────────────────────────────────────────────────────────────────────

test('guest cannot update password', function () {
    $this->put('/password', [
        'current_password'      => 'x',
        'password'              => 'y',
        'password_confirmation' => 'y',
    ])->assertRedirect('/login');
});

// ── JSON response ─────────────────────────────────────────────────────────────

test('json request receives json success response', function () {
    $user = User::factory()->create(['password' => Hash::make('current-pass')]);

    $this->actingAs($user)
        ->putJson('/password', [
            'current_password'      => 'current-pass',
            'password'              => 'Json-Pass-1!',
            'password_confirmation' => 'Json-Pass-1!',
        ])
        ->assertOk()
        ->assertJsonPath('success', true);
});
