<?php

use App\Models\ManagedEnvironment;
use App\Services\Graph\GraphClientInterface;
use App\Services\Graph\GraphResponse;
use App\Services\Intune\FoundationSnapshotService;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

class FoundationSnapshotGraphClient implements GraphClientInterface
{
    public array $requests = [];

    /**
     * @param  array<int, GraphResponse>  $responses
     */
    public function __construct(private array $responses) {}

    public function listPolicies(string $policyType, array $options = []): GraphResponse
    {
        return new GraphResponse(success: true, data: []);
    }

    public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse
    {
        return new GraphResponse(success: true, data: []);
    }

    public function getOrganization(array $options = []): GraphResponse
    {
        return new GraphResponse(success: true, data: []);
    }

    public function applyPolicy(string $policyType, string $policyId, array $payload, array $options = []): GraphResponse
    {
        return new GraphResponse(success: true, data: []);
    }

    public function getServicePrincipalPermissions(array $options = []): GraphResponse
    {
        return new GraphResponse(success: true, data: []);
    }

    public function request(string $method, string $path, array $options = []): GraphResponse
    {
        $this->requests[] = [
            'method' => $method,
            'path' => $path,
            'options' => $options,
        ];

        return array_shift($this->responses) ?? new GraphResponse(success: true, data: []);
    }
}

it('returns a failure when the foundation contract is missing', function () {
    $tenant = ManagedEnvironment::factory()->create();

    ensureDefaultProviderConnection($tenant, 'microsoft');

    $client = new FoundationSnapshotGraphClient([]);
    app()->instance(GraphClientInterface::class, $client);

    $service = app(FoundationSnapshotService::class);
    $result = $service->fetchAll($tenant, 'unknownFoundation');

    expect($result['items'])->toBeEmpty();
    expect($result['failures'])->toHaveCount(1);
    expect($result['failures'][0]['foundation_type'])->toBe('unknownFoundation');
});

it('hydrates foundation snapshots across pages with metadata', function () {
    config()->set('graph_contracts.types.assignmentFilter', [
        'resource' => 'deviceManagement/assignmentFilters',
        'allowed_select' => ['id', 'displayName'],
    ]);

    $tenant = ManagedEnvironment::factory()->create([
        'managed_environment_id' => 'tenant-123',
        'app_client_id' => 'client-123',
        'app_client_secret' => 'secret-123',
    ]);

    ensureDefaultProviderConnection($tenant, 'microsoft');

    $responses = [
        new GraphResponse(
            success: true,
            data: [
                'value' => [
                    ['id' => 'filter-1', 'displayName' => 'Filter One'],
                ],
                '@odata.nextLink' => 'https://graph.microsoft.com/beta/deviceManagement/assignmentFilters?$skiptoken=abc',
            ],
        ),
        new GraphResponse(
            success: true,
            data: [
                'value' => [
                    ['id' => 'filter-2', 'displayName' => 'Filter Two'],
                ],
            ],
        ),
    ];

    $client = new FoundationSnapshotGraphClient($responses);
    app()->instance(GraphClientInterface::class, $client);

    $service = app(FoundationSnapshotService::class);
    $result = $service->fetchAll($tenant, 'assignmentFilter');

    expect($result['items'])->toHaveCount(2);
    expect($result['items'][0]['source_id'])->toBe('filter-1');
    expect($result['items'][0]['metadata']['displayName'])->toBe('Filter One');
    expect($result['items'][0]['metadata']['kind'])->toBe('assignmentFilter');
    expect($result['items'][1]['source_id'])->toBe('filter-2');

    expect($client->requests[0]['path'])->toBe('deviceManagement/assignmentFilters');
    expect($client->requests[0]['options']['query']['$select'])->toBe('id,displayName');
    expect($client->requests[1]['path'])->toBe('deviceManagement/assignmentFilters?$skiptoken=abc');
    expect($client->requests[1]['options']['query'])->toBe([]);
});

it('hydrates RBAC assignment snapshots with contract-driven expand across pages', function () {
    config()->set('graph_contracts.types.intuneRoleAssignment', [
        'resource' => 'deviceManagement/roleAssignments',
        'allowed_select' => ['id', 'displayName', 'description', 'members', 'scopeMembers', 'resourceScopes', 'scopeType'],
        'allowed_expand' => ['roleDefinition($select=id,displayName,description,isBuiltIn)'],
    ]);

    $tenant = ManagedEnvironment::factory()->create([
        'managed_environment_id' => 'tenant-rbac-123',
        'app_client_id' => 'client-rbac-123',
        'app_client_secret' => 'secret-rbac-123',
    ]);

    ensureDefaultProviderConnection($tenant, 'microsoft');

    $responses = [
        new GraphResponse(
            success: true,
            data: [
                'value' => [
                    [
                        'id' => 'assignment-1',
                        'displayName' => 'Help Desk Assignment',
                        'members' => ['group-1'],
                        'scopeMembers' => ['scope-member-1'],
                        'resourceScopes' => ['scope-1'],
                        'roleDefinition' => [
                            'id' => 'role-definition-1',
                            'displayName' => 'Help Desk Operator',
                        ],
                    ],
                ],
                '@odata.nextLink' => 'https://graph.microsoft.com/beta/deviceManagement/roleAssignments?$skiptoken=rbac',
            ],
        ),
        new GraphResponse(
            success: true,
            data: [
                'value' => [
                    [
                        'id' => 'assignment-2',
                        'displayName' => 'Operations Assignment',
                        'members' => ['group-2'],
                        'resourceScopes' => ['scope-2'],
                    ],
                ],
            ],
        ),
    ];

    $client = new FoundationSnapshotGraphClient($responses);
    app()->instance(GraphClientInterface::class, $client);

    $service = app(FoundationSnapshotService::class);
    $result = $service->fetchAll($tenant, 'intuneRoleAssignment');

    expect($result['items'])->toHaveCount(2);
    expect($result['items'][0]['source_id'])->toBe('assignment-1');
    expect($result['items'][0]['metadata']['kind'])->toBe('intuneRoleAssignment');
    expect($result['items'][0]['metadata']['graph']['resource'])->toBe('deviceManagement/roleAssignments');
    expect($result['items'][0]['payload']['roleDefinition']['displayName'])->toBe('Help Desk Operator');
    expect($result['items'][1]['source_id'])->toBe('assignment-2');

    expect($client->requests[0]['path'])->toBe('deviceManagement/roleAssignments');
    expect($client->requests[0]['options']['query']['$select'])->toBe('id,displayName,description,members,scopeMembers,resourceScopes,scopeType');
    expect($client->requests[0]['options']['query']['$expand'])->toBe('roleDefinition($select=id,displayName,description,isBuiltIn)');
    expect($client->requests[1]['path'])->toBe('deviceManagement/roleAssignments?$skiptoken=rbac');
    expect($client->requests[1]['options']['query'])->toBe([]);
});
