<?php

namespace Tests\Feature\Modules;

use App\Models\Role;
use App\Models\User;
use App\Models\WalletLedgerEntry;
use App\Services\Permission\PermissionRegistry;
use App\Support\InstallationState;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\File;
use Tests\TestCase;

class ModuleInstallWalletTest extends TestCase
{
    use RefreshDatabase;

    private const FINANCE_UUID = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';

    protected function setUp(): void
    {
        parent::setUp();

        File::put(app(InstallationState::class)->lockPath(), now()->toIso8601String());

        config([
            'product_owner.license_key' => 'FREE-TEST-0000-0000',
            'product_owner.installation_id' => '00000000-0000-0000-0000-000000000001',
        ]);
        Cache::put('license.valid', true, now()->addHour());

        $this->seed(\Database\Seeders\RoleAndPermissionSeeder::class);
    }

    public function test_paid_module_install_debits_wallet_and_records_local_ledger(): void
    {
        $user = User::factory()->create();
        $user->assignRole(config('permissions.super_admin_role', 'Super Admin'));

        $this->actingAs($user)
            ->post(route('marketplace.install', self::FINANCE_UUID))
            ->assertRedirect(route('marketplace.index'))
            ->assertSessionHas('success');

        $this->assertDatabaseHas('installed_modules', [
            'module_uuid' => self::FINANCE_UUID,
            'name' => 'Finance',
        ]);

        $this->assertDatabaseHas('wallet_ledger_entries', [
            'type' => 'debit',
            'amount' => 49.00,
            'transaction_type' => 'module_purchase',
            'module_uuid' => self::FINANCE_UUID,
        ]);

        $entry = WalletLedgerEntry::query()->first();
        $this->assertNotNull($entry);
        $this->assertSame(1000.00, (float) $entry->opening_balance);
        $this->assertSame(951.00, (float) $entry->closing_balance);
        $this->assertNotEmpty($entry->reference_number);
    }

    public function test_module_install_grants_permissions_to_super_admin_role(): void
    {
        $superAdmin = Role::findByName(config('permissions.super_admin_role', 'Super Admin'), 'web');
        $superAdmin->syncPermissions(['STAFF_VIEW']);

        $user = User::factory()->create();
        $user->assignRole($superAdmin);

        $this->actingAs($user)
            ->post(route('marketplace.install', self::FINANCE_UUID))
            ->assertRedirect(route('marketplace.index'));

        $superAdmin->refresh();

        foreach (array_keys(config('permissions.modules.Finance', [])) as $permission) {
            $this->assertTrue(
                $superAdmin->hasPermissionTo($permission),
                "Expected Super Admin to have permission [{$permission}] after module install."
            );
        }
    }

    public function test_module_install_registers_permissions_in_roles_matrix(): void
    {
        $user = User::factory()->create();
        $user->assignRole(config('permissions.super_admin_role', 'Super Admin'));

        $this->actingAs($user)->post(route('marketplace.install', self::FINANCE_UUID));

        $matrix = app(PermissionRegistry::class)->matrixForRole(
            Role::findByName(config('permissions.super_admin_role', 'Super Admin'), 'web')
        );

        $this->assertArrayHasKey('Finance', $matrix);
        $this->assertNotEmpty($matrix['Finance']);
    }

    public function test_reinstalling_module_does_not_double_charge_wallet(): void
    {
        $user = User::factory()->create();
        $user->assignRole(config('permissions.super_admin_role', 'Super Admin'));

        $this->actingAs($user)->post(route('marketplace.install', self::FINANCE_UUID));

        $this->assertSame(1, WalletLedgerEntry::query()->count());

        $this->actingAs($user)
            ->post(route('marketplace.install', self::FINANCE_UUID))
            ->assertRedirect()
            ->assertSessionHas('error');

        $this->assertSame(1, WalletLedgerEntry::query()->count());
    }
}
