<?php

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

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

    Role::findOrCreate('User', 'web');
});

// ── deleteAccount ─────────────────────────────────────────────────────────────

test('delete account succeeds when correct password is supplied', function () {
    $user = User::factory()->create(['password' => Hash::make('secret')]);
    $token = $user->createToken('app')->plainTextToken;

    $this->withHeader('Authorization', "Bearer {$token}")
        ->deleteJson('/api/v1/profile/delete', ['password' => 'secret'])
        ->assertOk()
        ->assertJsonPath('status', 'success');

    expect(User::find($user->id))->toBeNull();
});

test('delete account is rejected with wrong password', function () {
    $user = User::factory()->create(['password' => Hash::make('secret')]);
    $token = $user->createToken('app')->plainTextToken;

    $this->withHeader('Authorization', "Bearer {$token}")
        ->deleteJson('/api/v1/profile/delete', ['password' => 'wrong'])
        ->assertStatus(422);

    expect(User::find($user->id))->not->toBeNull();
});

test('delete account requires password field', function () {
    $user = User::factory()->create();
    $token = $user->createToken('app')->plainTextToken;

    $this->withHeader('Authorization', "Bearer {$token}")
        ->deleteJson('/api/v1/profile/delete', [])
        ->assertStatus(422);
});

test('delete account is not accessible to guests', function () {
    $this->deleteJson('/api/v1/profile/delete', ['password' => 'x'])
        ->assertUnauthorized();
});

test('delete account also revokes all user tokens', function () {
    $user = User::factory()->create(['password' => Hash::make('secret')]);
    $user->createToken('device-a');
    $user->createToken('device-b');
    $token = $user->createToken('device-c')->plainTextToken;

    $this->withHeader('Authorization', "Bearer {$token}")
        ->deleteJson('/api/v1/profile/delete', ['password' => 'secret'])
        ->assertOk();

    expect($user->tokens()->count())->toBe(0);
});

// ── updatePassword (API) ──────────────────────────────────────────────────────

test('api password update succeeds with valid credentials', function () {
    $user = User::factory()->create(['password' => Hash::make('old-pass')]);
    $token = $user->createToken('app')->plainTextToken;

    $this->withHeader('Authorization', "Bearer {$token}")
        ->postJson('/api/v1/profile/password', [
            'current_password'      => 'old-pass',
            'password'              => 'New-Api-Pass1',
            'password_confirmation' => 'New-Api-Pass1',
        ])
        ->assertOk()
        ->assertJsonPath('status', 'success');

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

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

    $this->withHeader('Authorization', "Bearer {$token}")
        ->postJson('/api/v1/profile/password', [
            'current_password'      => 'not-real',
            'password'              => 'New-Api-Pass2',
            'password_confirmation' => 'New-Api-Pass2',
        ])
        ->assertStatus(422);
});

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

    $user = User::factory()->create(['password' => Hash::make('current')]);
    PasswordHistory::create(['user_id' => $user->id, 'password' => Hash::make('Recycled-Api!1')]);
    $token = $user->createToken('app')->plainTextToken;

    $this->withHeader('Authorization', "Bearer {$token}")
        ->postJson('/api/v1/profile/password', [
            'current_password'      => 'current',
            'password'              => 'Recycled-Api!1',
            'password_confirmation' => 'Recycled-Api!1',
        ])
        ->assertStatus(422)
        ->assertJsonPath('status', 'error');
});

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

    $user = User::factory()->create(['password' => Hash::make('old')]);
    $token = $user->createToken('app')->plainTextToken;

    $this->withHeader('Authorization', "Bearer {$token}")
        ->postJson('/api/v1/profile/password', [
            'current_password'      => 'old',
            'password'              => 'Api-NewPass-99',
            'password_confirmation' => 'Api-NewPass-99',
        ]);

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

// ── register with PasswordPolicyService ──────────────────────────────────────

test('register enforces min length from password policy setting', function () {
    app(SystemConfigService::class)->update(['password_min_length' => 12]);

    $this->postJson('/api/v1/register', [
        'name'     => 'Test',
        'email'    => 'policy@example.com',
        'password' => 'short',
    ])->assertStatus(422);
});

test('register succeeds when password meets policy min length', function () {
    app(SystemConfigService::class)->update(['password_min_length' => 6]);

    $this->postJson('/api/v1/register', [
        'name'     => 'Policy User',
        'email'    => 'policy2@example.com',
        'password' => 'longenough',
    ])->assertStatus(201);
});

test('register enforces numeric requirement when setting is enabled', function () {
    app(SystemConfigService::class)->update([
        'password_require_numeric' => true,
        'password_min_length'      => 6,
    ]);

    $this->postJson('/api/v1/register', [
        'name'     => 'No Digits',
        'email'    => 'nodigits@example.com',
        'password' => 'NoDigitsHere',
    ])->assertStatus(422);
});

// ── updateProfile ─────────────────────────────────────────────────────────────

test('authenticated user can update their name via api', function () {
    $user = User::factory()->create();
    $token = $user->createToken('app')->plainTextToken;

    $this->withHeader('Authorization', "Bearer {$token}")
        ->postJson('/api/v1/profile/update', [
            'name'  => 'Updated Name',
            'email' => $user->email,
        ])
        ->assertOk()
        ->assertJsonPath('status', 'success');

    expect($user->fresh()->name)->toBe('Updated Name');
});

test('profile update rejects name longer than 255 chars', function () {
    $user = User::factory()->create();
    $token = $user->createToken('app')->plainTextToken;

    $this->withHeader('Authorization', "Bearer {$token}")
        ->postJson('/api/v1/profile/update', [
            'name'  => str_repeat('a', 256),
            'email' => $user->email,
        ])
        ->assertStatus(422);
});
