<?php

use App\Models\Tenant;
use App\Models\TenantPermission;
use App\Services\Graph\GraphClientInterface;
use App\Services\Graph\GraphResponse;
use App\Services\Intune\TenantPermissionService;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);
function requiredPermissions(): array
{
    $service = app(TenantPermissionService::class);
    $required = $service->getRequiredPermissions();

    if (empty($required)) {
        test()->markTestSkipped('No required permissions configured.');
    }

    return $required;
}

it('returns ok when all permissions exist', function () {
    // Mock GraphClient to return all permissions as granted
    $this->mock(GraphClientInterface::class, function ($mock) {
        $mock->shouldReceive('getServicePrincipalPermissions')
            ->andReturn(new GraphResponse(true, [
                'value' => collect(config('intune_permissions.permissions', []))
                    ->map(fn ($p) => ['value' => $p['key']])
                    ->toArray(),
            ]));
    });

    $tenant = Tenant::factory()->create([
        'tenant_id' => 'tenant-ok',
        'name' => 'Tenant OK',
    ]);

    ensureDefaultProviderConnection($tenant, 'microsoft');

    foreach (requiredPermissions() as $permission) {
        TenantPermission::create([
            'tenant_id' => $tenant->id,
            'permission_key' => $permission['key'],
            'status' => 'granted',
        ]);
    }

    $result = app(TenantPermissionService::class)->compare($tenant);

    expect($result['overall_status'])->toBe('granted');
    expect(TenantPermission::where('tenant_id', $tenant->id)->where('status', 'granted')->count())
        ->toBe(count(requiredPermissions()));
});

it('marks missing permissions when not granted', function () {
    $permissions = requiredPermissions();

    // Mock GraphClient to return only first permission as granted
    $this->mock(GraphClientInterface::class, function ($mock) use ($permissions) {
        $mock->shouldReceive('getServicePrincipalPermissions')
            ->andReturn(new GraphResponse(true, [
                'permissions' => [$permissions[0]['key']],
            ]));
    });

    $tenant = Tenant::factory()->create([
        'tenant_id' => 'tenant-missing',
        'name' => 'Tenant Missing',
    ]);

    ensureDefaultProviderConnection($tenant, 'microsoft');

    $first = $permissions[0]['key'];
    TenantPermission::create([
        'tenant_id' => $tenant->id,
        'permission_key' => $first,
        'status' => 'ok',
    ]);

    // Use liveCheck=true to trigger Graph API call
    $result = app(TenantPermissionService::class)->compare($tenant, null, true, true);

    expect($result['overall_status'])->toBe('missing');
    $missingKey = $permissions[1]['key'] ?? null;

    if ($missingKey) {
        $this->assertDatabaseHas('tenant_permissions', [
            'tenant_id' => $tenant->id,
            'permission_key' => $missingKey,
            'status' => 'missing',
        ]);
    }
});

it('reports error statuses from graph comparison', function () {
    // Mock GraphClient to return an error
    $this->mock(GraphClientInterface::class, function ($mock) {
        $mock->shouldReceive('getServicePrincipalPermissions')
            ->andReturn(new GraphResponse(false, [], 500, ['Graph API error']));
    });

    $tenant = Tenant::factory()->create([
        'tenant_id' => 'tenant-error',
        'name' => 'Tenant Error',
    ]);

    ensureDefaultProviderConnection($tenant, 'microsoft');

    $permissions = requiredPermissions();
    $first = $permissions[0]['key'];

    $result = app(TenantPermissionService::class)->compare($tenant, [
        $first => [
            'status' => 'error',
            'details' => ['message' => 'forbidden'],
        ],
    ]);

    expect($result['overall_status'])->toBe('error');

    $this->assertDatabaseHas('tenant_permissions', [
        'tenant_id' => $tenant->id,
        'permission_key' => $first,
        'status' => 'error',
    ]);
});

it('ignores configured stub permissions when requested', function () {
    $originalPermissions = config('intune_permissions.permissions');
    $originalStub = config('intune_permissions.granted_stub');

    config()->set('intune_permissions.permissions', [
        [
            'key' => 'DeviceManagementRBAC.ReadWrite.All',
            'type' => 'application',
            'description' => null,
            'features' => [],
        ],
    ]);
    config()->set('intune_permissions.granted_stub', ['DeviceManagementRBAC.ReadWrite.All']);

    $tenant = Tenant::factory()->create();

    ensureDefaultProviderConnection($tenant, 'microsoft');

    TenantPermission::create([
        'tenant_id' => $tenant->id,
        'permission_key' => 'DeviceManagementRBAC.ReadWrite.All',
        'status' => 'granted',
        'details' => ['source' => 'configured'],
    ]);

    $result = app(TenantPermissionService::class)->compare($tenant, persist: false, useConfiguredStub: false);

    expect($result['overall_status'])->toBe('missing');
    expect($result['permissions'][0]['status'])->toBe('missing');

    config()->set('intune_permissions.permissions', $originalPermissions);
    config()->set('intune_permissions.granted_stub', $originalStub);
});

it('persists permissions with workspace_id even when model events are disabled', function () {
    $tenant = Tenant::factory()->create();

    ensureDefaultProviderConnection($tenant, 'microsoft');

    TenantPermission::withoutEvents(function () use ($tenant): void {
        app(TenantPermissionService::class)->compare($tenant);
    });

    $this->assertDatabaseHas('tenant_permissions', [
        'tenant_id' => $tenant->id,
        'workspace_id' => $tenant->workspace_id,
    ]);
});

it('persists permissions when the tenant instance does not have workspace_id loaded', function () {
    $tenant = Tenant::factory()->create();

    ensureDefaultProviderConnection($tenant, 'microsoft');

    $tenantWithoutWorkspaceId = Tenant::query()
        ->select(['id', 'tenant_id', 'external_id', 'name', 'status', 'environment'])
        ->findOrFail((int) $tenant->getKey());

    expect($tenantWithoutWorkspaceId->getAttribute('workspace_id'))->toBeNull();

    app(TenantPermissionService::class)->compare($tenantWithoutWorkspaceId);

    $this->assertDatabaseHas('tenant_permissions', [
        'tenant_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
    ]);
});

it('does not persist when tenant workspace_id is missing', function () {
    $tenant = Tenant::withoutEvents(function (): Tenant {
        return Tenant::create([
            'tenant_id' => 'tenant-no-workspace',
            'external_id' => 'tenant-no-workspace',
            'name' => 'Tenant No Workspace',
            'status' => Tenant::STATUS_ACTIVE,
            'environment' => 'other',
            'workspace_id' => null,
        ]);
    });

    app(TenantPermissionService::class)->compare($tenant, persist: true);

    expect(TenantPermission::query()->where('tenant_id', (int) $tenant->getKey())->count())->toBe(0);
});
