<?php

declare(strict_types=1);

use App\Models\User;
use App\Models\Setting;
use App\Services\SettingService;
use Inertia\Testing\AssertableInertia as Assert;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

beforeEach(function () {
    $this->seed();
});

test('settings can be retrieved, updated, and cached', function () {
    $service = app(SettingService::class);

    // Initial state
    $service->set('test_key', 'initial_value');
    expect($service->get('test_key'))->toBe('initial_value');

    // Verify it updates in DB
    $this->assertDatabaseHas('settings', [
        'key' => 'test_key',
        'value' => 'initial_value',
    ]);

    // Update value
    $service->set('test_key', 'new_value');
    expect($service->get('test_key'))->toBe('new_value');

    // Test caching: direct DB update won't affect cached value
    Setting::where('key', 'test_key')->update(['value' => 'direct_db_value']);
    expect($service->get('test_key'))->toBe('new_value');

    // Clear cache, it should load the direct DB update
    $service->clearCache();
    expect($service->get('test_key'))->toBe('direct_db_value');
});

test('RBAC roles, permissions, and super admin gate are configured correctly', function () {
    // Check seeded roles
    expect(Role::where('name', 'Super Admin')->exists())->toBeTrue();
    expect(Role::where('name', 'Admin')->exists())->toBeTrue();
    expect(Role::where('name', 'Manager')->exists())->toBeTrue();
    expect(Role::where('name', 'User')->exists())->toBeTrue();

    // Check seeded permissions
    expect(Permission::where('name', 'manage settings')->exists())->toBeTrue();

    // Test Super Admin bypasses all gates
    $superAdmin = User::where('email', 'superadmin@example.com')->first();
    expect($superAdmin)->not->toBeNull();
    expect($superAdmin->hasRole('Super Admin'))->toBeTrue();
    expect($superAdmin->can('non-existent permission'))->toBeTrue();

    // Test Regular User does not bypass all gates
    $regularUser = User::where('email', 'user@example.com')->first();
    expect($regularUser)->not->toBeNull();
    expect($regularUser->hasRole('User'))->toBeTrue();
    expect($regularUser->can('non-existent permission'))->toBeFalse();
    expect($regularUser->can('view dashboard'))->toBeTrue();
});

test('Inertia shares settings and authenticated user roles/permissions', function () {
    $user = User::where('email', 'admin@example.com')->first();
    expect($user)->not->toBeNull();

    $this->actingAs($user)
        ->get(route('dashboard'))
        ->assertInertia(fn (Assert $page) => $page
            ->has('settings', fn (Assert $settings) => $settings
                ->where('site_name', 'CafePOS')
                ->where('site_logo', '/images/logo.png')
                ->where('site_favicon', '/favicon.ico')
                ->where('site_description', 'Enterprise Web-Based POS & Inventory System for Coffee Shops.')
                ->etc()
            )
            ->has('auth.user', fn (Assert $authUser) => $authUser
                ->where('email', 'admin@example.com')
                ->has('roles')
                ->has('permissions')
                ->where('roles.0', 'Admin')
                ->whereContains('permissions', 'manage settings')
                ->etc()
            )
        );
});

test('authorized users can update settings and upload branding assets', function () {
    \Illuminate\Support\Facades\Storage::fake('public');
    
    $admin = User::where('email', 'admin@example.com')->first();
    expect($admin)->not->toBeNull();

    $logo = \Illuminate\Http\UploadedFile::fake()->image('logo.png');
    $favicon = \Illuminate\Http\UploadedFile::fake()->image('favicon.ico');

    $this->actingAs($admin)
        ->post(route('settings.update'), [
            'site_name' => 'Updated Brand Name',
            'site_description' => 'Updated site description info.',
            'site_logo' => $logo,
            'site_favicon' => $favicon,
        ])
        ->assertRedirect();

    $settingService = app(SettingService::class);
    expect($settingService->get('site_name'))->toBe('Updated Brand Name');
    expect($settingService->get('site_description'))->toBe('Updated site description info.');

    $logoUrl = $settingService->get('site_logo');
    $faviconUrl = $settingService->get('site_favicon');

    expect($logoUrl)->not->toBeNull();
    expect($faviconUrl)->not->toBeNull();

    // Extract path to assert storage existence
    $logoPath = preg_replace('/^\/storage\//', '', parse_url($logoUrl, PHP_URL_PATH));
    $faviconPath = preg_replace('/^\/storage\//', '', parse_url($faviconUrl, PHP_URL_PATH));

    \Illuminate\Support\Facades\Storage::disk('public')->assertExists($logoPath);
    \Illuminate\Support\Facades\Storage::disk('public')->assertExists($faviconPath);

    // Now, upload a new logo to test deletion of the old one
    $newLogo = \Illuminate\Http\UploadedFile::fake()->image('new_logo.png');

    $this->actingAs($admin)
        ->post(route('settings.update'), [
            'site_name' => 'Updated Brand Name',
            'site_description' => 'Updated site description info.',
            'site_logo' => $newLogo,
        ])
        ->assertRedirect();

    // The old logo file should be deleted from storage
    \Illuminate\Support\Facades\Storage::disk('public')->assertMissing($logoPath);

    $newLogoUrl = $settingService->get('site_logo');
    $newLogoPath = preg_replace('/^\/storage\//', '', parse_url($newLogoUrl, PHP_URL_PATH));
    \Illuminate\Support\Facades\Storage::disk('public')->assertExists($newLogoPath);
});

test('unauthorized users cannot access settings page or submit updates', function () {
    $regularUser = User::where('email', 'user@example.com')->first();
    expect($regularUser)->not->toBeNull();

    // Cannot view settings page
    $this->actingAs($regularUser)
        ->get(route('settings.edit'))
        ->assertStatus(403);

    // Cannot submit update settings
    $this->actingAs($regularUser)
        ->post(route('settings.update'), [
            'site_name' => 'Malicious Hack',
        ])
        ->assertStatus(403);
});

test('/admin route redirects to dashboard', function () {
    $user = User::factory()->create();

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

