<?php

use App\Enums\AccountStatus;
use App\Enums\AccountType;
use App\Enums\Currency;
use App\Enums\PaymentMethodType;
use App\Enums\RequestStatus;
use App\Enums\UserStatus;
use App\Enums\UserType;
use App\Models\Account;
use App\Models\AppSetting;
use App\Models\DepositRequest;
use App\Models\PaymentMethod;
use App\Models\User;
use App\Models\WithdrawalRequest;

function createAdmin(): User
{
    AppSetting::set('otp_enabled', 'false');
    AppSetting::set('maintenance_mode', 'false');

    return User::factory()->create([
        'status' => UserStatus::Active,
        'type' => UserType::Admin,
        'password' => 'password',
    ]);
}

function createRegularUser(): User
{
    $user = User::factory()->create([
        'status' => UserStatus::Active,
        'type' => UserType::User,
        'password' => 'password',
    ]);

    Account::create([
        'user_id' => $user->id,
        'account_number' => Account::generateAccountNumber(),
        'account_type' => AccountType::Checking,
        'currency' => Currency::USD,
        'balance' => 5000.00,
        'status' => AccountStatus::Active,
    ]);

    return $user;
}

// Access Control
test('non-admin cannot access admin dashboard', function () {
    $user = createRegularUser();

    $this->actingAs($user)
        ->get('/admin/dashboard')
        ->assertForbidden();
});

test('guest cannot access admin dashboard', function () {
    $this->get('/admin/dashboard')->assertRedirect('/login');
});

// Admin Dashboard
test('admin dashboard renders', function () {
    $admin = createAdmin();

    $this->actingAs($admin)
        ->get('/admin/dashboard')
        ->assertOk();
});

// Admin Settings
test('admin settings page renders', function () {
    $admin = createAdmin();

    $this->actingAs($admin)
        ->get('/admin/settings')
        ->assertOk();
});

test('admin can update settings', function () {
    $admin = createAdmin();

    $this->actingAs($admin)
        ->post('/admin/settings', [
            'otp_enabled' => 'true',
            'maintenance_mode' => 'false',
            'transfer_review_threshold' => 5000,
        ])
        ->assertRedirect(route('admin.settings'));
});

// Admin Users
test('admin users index renders', function () {
    $admin = createAdmin();
    createRegularUser();

    $this->actingAs($admin)
        ->get('/admin/users')
        ->assertOk();
});

test('admin can search users', function () {
    $admin = createAdmin();
    User::factory()->create(['first_name' => 'UniqueTestName']);

    $this->actingAs($admin)
        ->get('/admin/users?search=UniqueTestName')
        ->assertOk()
        ->assertSee('UniqueTestName');
});

test('admin can view user details', function () {
    $admin = createAdmin();
    $user = createRegularUser();

    $this->actingAs($admin)
        ->get("/admin/users/{$user->id}")
        ->assertOk();
});

test('admin can edit user', function () {
    $admin = createAdmin();
    $user = createRegularUser();

    $this->actingAs($admin)
        ->get("/admin/users/{$user->id}/edit")
        ->assertOk();
});

test('admin can update user', function () {
    $admin = createAdmin();
    $user = createRegularUser();

    $this->actingAs($admin)
        ->put("/admin/users/{$user->id}", [
            'first_name' => 'NewFirst',
            'last_name' => 'NewLast',
            'email' => $user->email,
            'status' => UserStatus::Active->value,
            'type' => UserType::User->value,
        ])
        ->assertRedirect(route('admin.users.index'));

    expect($user->fresh()->first_name)->toBe('NewFirst');
});

test('admin can delete user', function () {
    $admin = createAdmin();
    $user = createRegularUser();

    $this->actingAs($admin)
        ->delete("/admin/users/{$user->id}")
        ->assertRedirect(route('admin.users.index'));

    $this->assertDatabaseMissing('users', ['id' => $user->id]);
});

test('admin can view user accounts', function () {
    $admin = createAdmin();
    $user = createRegularUser();

    $this->actingAs($admin)
        ->get("/admin/users/{$user->id}/accounts")
        ->assertOk();
});

// Admin Deposits
test('admin deposits page renders', function () {
    $admin = createAdmin();

    $this->actingAs($admin)
        ->get('/admin/deposits')
        ->assertOk();
});

test('admin can approve deposit', function () {
    $admin = createAdmin();
    $user = createRegularUser();
    $account = $user->accounts->first();

    $pm = PaymentMethod::create([
        'name' => 'Test Method',
        'type' => PaymentMethodType::BankTransfer,
        'instructions' => 'Test',
        'is_active' => true,
    ]);

    $deposit = DepositRequest::create([
        'user_id' => $user->id,
        'account_id' => $account->id,
        'payment_method_id' => $pm->id,
        'amount' => 1000.00,
        'proof_path' => 'test/proof.jpg',
        'status' => RequestStatus::Pending,
    ]);

    $this->actingAs($admin)
        ->post("/admin/deposits/{$deposit->id}/approve")
        ->assertRedirect();

    expect($deposit->fresh()->status)->toBe(RequestStatus::Approved);
    expect((float) $account->fresh()->balance)->toBe(6000.00);
});

test('admin can reject deposit', function () {
    $admin = createAdmin();
    $user = createRegularUser();
    $account = $user->accounts->first();

    $pm = PaymentMethod::create([
        'name' => 'Test Method',
        'type' => PaymentMethodType::BankTransfer,
        'instructions' => 'Test',
        'is_active' => true,
    ]);

    $deposit = DepositRequest::create([
        'user_id' => $user->id,
        'account_id' => $account->id,
        'payment_method_id' => $pm->id,
        'amount' => 500.00,
        'proof_path' => 'test/proof.jpg',
        'status' => RequestStatus::Pending,
    ]);

    $this->actingAs($admin)
        ->post("/admin/deposits/{$deposit->id}/reject", ['admin_note' => 'Invalid proof'])
        ->assertRedirect();

    expect($deposit->fresh()->status)->toBe(RequestStatus::Rejected);
});

// Admin Withdrawals
test('admin withdrawals page renders', function () {
    $admin = createAdmin();

    $this->actingAs($admin)
        ->get('/admin/withdrawals')
        ->assertOk();
});

test('admin can approve withdrawal', function () {
    $admin = createAdmin();
    $user = createRegularUser();
    $account = $user->accounts->first();

    $withdrawal = WithdrawalRequest::create([
        'user_id' => $user->id,
        'account_id' => $account->id,
        'amount' => 500.00,
        'withdrawal_details' => ['bank_name' => 'Chase', 'account_number' => '123'],
        'status' => RequestStatus::Pending,
    ]);

    $this->actingAs($admin)
        ->post("/admin/withdrawals/{$withdrawal->id}/approve")
        ->assertRedirect();

    expect($withdrawal->fresh()->status)->toBe(RequestStatus::Approved);
    expect((float) $account->fresh()->balance)->toBe(4500.00);
});

// Admin Payment Methods
test('admin payment methods page renders', function () {
    $admin = createAdmin();

    $this->actingAs($admin)
        ->get('/admin/payment-methods')
        ->assertOk();
});

test('admin can create payment method page renders', function () {
    $admin = createAdmin();

    $this->actingAs($admin)
        ->get('/admin/payment-methods/create')
        ->assertOk();
});

test('admin can store payment method', function () {
    $admin = createAdmin();

    $this->actingAs($admin)
        ->post('/admin/payment-methods', [
            'name' => 'New Bank Transfer',
            'type' => PaymentMethodType::BankTransfer->value,
            'instructions' => 'Transfer to account 12345',
            'is_active' => true,
        ])
        ->assertRedirect(route('admin.payment-methods.index'));

    $this->assertDatabaseHas('payment_methods', [
        'name' => 'New Bank Transfer',
    ]);
});

test('admin can edit payment method', function () {
    $admin = createAdmin();

    $pm = PaymentMethod::create([
        'name' => 'Edit Me',
        'type' => PaymentMethodType::BankTransfer,
        'instructions' => 'Old',
        'is_active' => true,
    ]);

    $this->actingAs($admin)
        ->get("/admin/payment-methods/{$pm->id}/edit")
        ->assertOk();
});

test('admin can update payment method', function () {
    $admin = createAdmin();

    $pm = PaymentMethod::create([
        'name' => 'Update Me',
        'type' => PaymentMethodType::BankTransfer,
        'instructions' => 'Old',
        'is_active' => true,
    ]);

    $this->actingAs($admin)
        ->put("/admin/payment-methods/{$pm->id}", [
            'name' => 'Updated Name',
            'type' => PaymentMethodType::Crypto->value,
            'instructions' => 'New instructions',
            'is_active' => true,
        ])
        ->assertRedirect(route('admin.payment-methods.index'));

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

// Admin Transactions
test('admin transactions page renders', function () {
    $admin = createAdmin();

    $this->actingAs($admin)
        ->get('/admin/transactions')
        ->assertOk();
});
