<?php

use App\Services\Graph\GraphContractRegistry;
use App\Services\Graph\GraphResponse;

beforeEach(function () {
    config()->set('graph_contracts.types.deviceConfiguration', [
        'resource' => 'deviceManagement/deviceConfigurations',
        'allowed_select' => ['id', 'displayName'],
        'allowed_expand' => ['assignments'],
        'type_family' => [
            '#microsoft.graph.deviceConfiguration',
            '#microsoft.graph.windows10CustomConfiguration',
        ],
    ]);

    config()->set('graph_contracts.types.settingsCatalogPolicy', [
        'resource' => 'deviceManagement/configurationPolicies',
        'allowed_select' => ['id'],
        'allowed_expand' => [],
        'type_family' => ['#microsoft.graph.deviceManagementConfigurationPolicy'],
        'update_whitelist' => ['name', 'description'],
        'update_map' => ['displayName' => 'name'],
        'update_strip_keys' => ['platforms', 'technologies', 'templateReference', 'assignments'],
        'settings_write' => [
            'path_template' => 'deviceManagement/configurationPolicies/{id}/settings/{settingId}',
            'method' => 'PATCH',
        ],
    ]);

    $this->registry = app(GraphContractRegistry::class);
});

it('sanitizes disallowed select and expand values', function () {
    $result = $this->registry->sanitizeQuery('deviceConfiguration', [
        '$select' => ['id', 'displayName', 'unsupported'],
        '$expand' => ['assignments', 'badExpand'],
    ]);
    $query = $result['query'];
    $warnings = $result['warnings'];

    expect($query['$select'])->toBe('id,displayName');
    expect($query['$expand'])->toBe('assignments');
    expect($warnings)->not->toBeEmpty();
});

it('matches derived types within family', function () {
    expect($this->registry->matchesTypeFamily('deviceConfiguration', '#microsoft.graph.windows10CustomConfiguration'))->toBeTrue();
    expect($this->registry->matchesTypeFamily('deviceConfiguration', '#microsoft.graph.androidCompliancePolicy'))->toBeFalse();
});

it('detects capability errors for downgrade', function () {
    $response = new GraphResponse(
        success: false,
        data: [],
        status: 400,
        errors: [['message' => 'Request is invalid due to $select']]
    );

    $shouldDowngrade = $this->registry->shouldDowngradeOnCapabilityError($response, ['$select' => ['displayName']]);

    expect($shouldDowngrade)->toBeTrue();
});

it('provides contract for settings catalog policies', function () {
    $contract = config('graph_contracts.types.settingsCatalogPolicy');

    expect($contract)->not->toBeEmpty();
    expect($contract['resource'])->toBe('deviceManagement/configurationPolicies');
    expect($this->registry->matchesTypeFamily('settingsCatalogPolicy', '#microsoft.graph.deviceManagementConfigurationPolicy'))->toBeTrue();
});

it('sanitizes update payloads for settings catalog policies', function () {
    $payload = [
        'id' => 'scp-1',
        '@odata.type' => '#microsoft.graph.deviceManagementConfigurationPolicy',
        'displayName' => 'Config',
        'Description' => 'desc',
        'version' => 5,
        'createdDateTime' => '2024-01-01T00:00:00Z',
        'settings' => [
            [
                'id' => 'setting-1',
                'settingInstance' => [
                    '@odata.type' => '#microsoft.graph.deviceManagementConfigurationSimpleSettingInstance',
                    'settingDefinitionId' => 'setting_definition',
                    'simpleSettingValue' => ['value' => 'bar'],
                ],
            ],
        ],
        'Platforms' => ['windows'],
        'unknown' => 'drop-me',
    ];

    $sanitized = $this->registry->sanitizeUpdatePayload('settingsCatalogPolicy', $payload);

    expect($sanitized)->toHaveKeys(['name', 'description']);
    expect($sanitized)->not->toHaveKey('id');
    expect($sanitized)->not->toHaveKey('@odata.type');
    expect($sanitized)->not->toHaveKey('version');
    expect($sanitized)->not->toHaveKey('platforms');
    expect($sanitized)->not->toHaveKey('settings');
});
