<?php

use App\Services\Graph\GraphContractRegistry;

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

it('preserves @odata.type with actual choiceSettingValue data structure', function () {
    $settings = [
        [
            'id' => '0',
            'settingInstance' => [
                '@odata.type' => '#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance',
                'choiceSettingValue' => [
                    'value' => 'device_vendor_msft_passportforwork_biometrics_usebiometrics_true',
                    'children' => [],
                    'settingValueTemplateReference' => null,
                ],
                'settingDefinitionId' => 'device_vendor_msft_passportforwork_biometrics_usebiometrics',
                'auditRuleInformation' => null,
                'settingInstanceTemplateReference' => null,
            ],
        ],
    ];

    $sanitized = $this->registry->sanitizeSettingsApplyPayload('settingsCatalogPolicy', $settings);

    expect($sanitized)->toBeArray();
    expect(count($sanitized))->toBe(1);

    // Top-level id should be stripped
    expect(array_key_exists('id', $sanitized[0]))->toBeFalse();

    // settingInstance should exist
    expect(isset($sanitized[0]['settingInstance']))->toBeTrue();

    // @odata.type should be preserved inside settingInstance
    expect(isset($sanitized[0]['settingInstance']['@odata.type']))->toBeTrue();
    expect($sanitized[0]['settingInstance']['@odata.type'])->toBe('#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance');

    // choiceSettingValue should still exist
    expect(isset($sanitized[0]['settingInstance']['choiceSettingValue']))->toBeTrue();
    expect($sanitized[0]['settingInstance']['choiceSettingValue']['value'])->toBe('device_vendor_msft_passportforwork_biometrics_usebiometrics_true');

    // Null values should be preserved (Graph might need them)
    expect(array_key_exists('settingValueTemplateReference', $sanitized[0]['settingInstance']['choiceSettingValue']))->toBeTrue();
});

it('exposes autopilot assignments paths', function () {
    $contract = $this->registry->get('windowsAutopilotDeploymentProfile');

    expect($contract)->not->toBeEmpty();
    expect($contract['assignments_list_path'] ?? null)
        ->toBe('/deviceManagement/windowsAutopilotDeploymentProfiles/{id}/assignments');
    expect($contract['assignments_create_path'] ?? null)
        ->toBe('/deviceManagement/windowsAutopilotDeploymentProfiles/{id}/assignments');
    expect($contract['assignments_delete_path'] ?? null)
        ->toBe('/deviceManagement/windowsAutopilotDeploymentProfiles/{id}/assignments/{assignmentId}');
    expect($this->registry->matchesTypeFamily(
        'windowsAutopilotDeploymentProfile',
        '#microsoft.graph.azureADWindowsAutopilotDeploymentProfile'
    ))->toBeTrue();
    expect($this->registry->matchesTypeFamily(
        'windowsAutopilotDeploymentProfile',
        '#microsoft.graph.activeDirectoryWindowsAutopilotDeploymentProfile'
    ))->toBeTrue();
});

it('sanitizes autopilot update payload by stripping odata and assignments', function () {
    $payload = [
        '@odata.type' => '#microsoft.graph.azureADWindowsAutopilotDeploymentProfile',
        'id' => 'profile-1',
        'displayName' => 'Autopilot Profile',
        'assignments' => [['id' => 'assignment-1']],
        'managementServiceAppId' => 'service-app',
        'outOfBoxExperienceSetting' => ['deviceUsageType' => 'shared'],
        'hardwareHashExtractionEnabled' => true,
        'locale' => 'de-DE',
    ];

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

    expect($sanitized)->toHaveKey('displayName');
    expect($sanitized)->toHaveKey('@odata.type');
    expect($sanitized)->not->toHaveKey('id');
    expect($sanitized)->not->toHaveKey('assignments');
    expect($sanitized)->not->toHaveKey('managementServiceAppId');
    expect($sanitized)->not->toHaveKey('outOfBoxExperienceSetting');
    expect($sanitized)->not->toHaveKey('hardwareHashExtractionEnabled');
    expect($sanitized)->not->toHaveKey('locale');
});

it('exposes compliance policy expand for scheduled actions', function () {
    $contract = $this->registry->get('deviceCompliancePolicy');

    expect($contract)->not->toBeEmpty();
    expect($contract['allowed_expand'] ?? [])
        ->toContain('scheduledActionsForRule($expand=scheduledActionConfigurations)');
});

it('exposes mobile app assignment endpoints and type family', function () {
    $contract = $this->registry->get('mobileApp');

    expect($contract)->not->toBeEmpty();
    expect($contract['allowed_select'] ?? [])->toContain('roleScopeTagIds');
    expect($contract['assignments_list_path'] ?? null)
        ->toBe('/deviceAppManagement/mobileApps/{id}/assignments');
    expect($contract['assignments_create_path'] ?? null)
        ->toBe('/deviceAppManagement/mobileApps/{id}/assign');
    expect($contract['assignments_payload_key'] ?? null)
        ->toBe('mobileAppAssignments');
    expect($this->registry->matchesTypeFamily('mobileApp', '#microsoft.graph.win32LobApp'))->toBeTrue();
    expect($this->registry->matchesTypeFamily('mobileApp', '#microsoft.graph.iosVppApp'))->toBeTrue();
});

it('exposes app protection assignment endpoints and type family', function () {
    $contract = $this->registry->get('appProtectionPolicy');

    expect($contract)->not->toBeEmpty();
    expect($contract['assignments_list_path'] ?? null)
        ->toBe('/deviceAppManagement/managedAppPolicies/{id}/assignments');
    expect($contract['assignments_create_path'] ?? null)
        ->toBe('/deviceAppManagement/managedAppPolicies/{id}/assign');
    expect($this->registry->matchesTypeFamily('appProtectionPolicy', '#microsoft.graph.iosManagedAppProtection'))->toBeTrue();
    expect($this->registry->matchesTypeFamily('appProtectionPolicy', '#microsoft.graph.androidManagedAppProtection'))->toBeTrue();
    expect($this->registry->matchesTypeFamily('appProtectionPolicy', '#microsoft.graph.targetedManagedAppConfiguration'))->toBeFalse();
});

it('omits role scope tags from assignment filter selects', function () {
    $contract = $this->registry->get('assignmentFilter');

    expect($contract)->not->toBeEmpty();
    expect($contract['allowed_select'] ?? [])
        ->not->toContain('roleScopeTagIds');
});
