<?php

use App\Models\Policy;
use App\Models\ProviderConnection;
use App\Models\ProviderCredential;
use App\Models\ManagedEnvironment;
use App\Services\Graph\GraphClientInterface;
use App\Services\Graph\GraphResponse;
use App\Services\Intune\PolicySnapshotService;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

function tenantWithDefaultMicrosoftConnectionForPolicySnapshot(string $tenantIdentifier): ManagedEnvironment
{
    $tenant = ManagedEnvironment::factory()->create([
        'managed_environment_id' => $tenantIdentifier,
        'app_client_id' => null,
        'app_client_secret' => null,
        'is_current' => true,
    ]);

    $connection = ProviderConnection::factory()->dedicated()->consentGranted()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'provider' => 'microsoft',
        'is_default' => true,
        'consent_status' => 'granted',
        'entra_tenant_id' => $tenantIdentifier,
    ]);

    ProviderCredential::factory()->create([
        'provider_connection_id' => (int) $connection->getKey(),
        'type' => 'client_secret',
        'payload' => [
            'client_id' => 'provider-client-'.$tenant->getKey(),
            'client_secret' => 'provider-secret-'.$tenant->getKey(),
        ],
    ]);

    return $tenant;
}

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

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

    public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse
    {
        $this->requests[] = ['getPolicy', $policyType, $policyId, $options];

        if ($policyType === 'mobileApp') {
            return new GraphResponse(success: true, data: [
                'payload' => [
                    'id' => $policyId,
                    'displayName' => 'Contoso Portal',
                    'publisher' => 'Contoso',
                    'description' => 'Company Portal',
                    '@odata.type' => '#microsoft.graph.win32LobApp',
                    'createdDateTime' => '2025-01-01T00:00:00Z',
                    'lastModifiedDateTime' => '2025-01-02T00:00:00Z',
                    'roleScopeTagIds' => ['0', 'tag-1', 'tag-2'],
                    'installCommandLine' => 'setup.exe /quiet',
                    'largeIcon' => ['type' => 'image/png', 'value' => '...'],
                ],
            ]);
        }

        if ($policyType === 'windowsDriverUpdateProfile') {
            return new GraphResponse(success: true, data: [
                'payload' => [
                    'id' => $policyId,
                    'displayName' => 'Driver Updates A',
                    'description' => 'Drivers rollout policy',
                    '@odata.type' => '#microsoft.graph.windowsDriverUpdateProfile',
                    'approvalType' => 'automatic',
                    'deploymentDeferralInDays' => 7,
                    'deviceReporting' => 12,
                    'newUpdates' => 3,
                    'roleScopeTagIds' => ['0'],
                    'inventorySyncStatus' => [
                        '@odata.type' => '#microsoft.graph.windowsDriverUpdateProfileInventorySyncStatus',
                        'driverInventorySyncState' => 'success',
                        'lastSuccessfulSyncDateTime' => '2026-01-01T00:00:00Z',
                    ],
                ],
            ]);
        }

        return new GraphResponse(success: true, data: [
            'payload' => [
                'id' => $policyId,
                'displayName' => 'Compliance Alpha',
                '@odata.type' => '#microsoft.graph.windows10CompliancePolicy',
            ],
        ]);
    }

    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, $path];

        if (str_contains($path, 'scheduledActionsForRule')) {
            return new GraphResponse(success: true, data: [
                'value' => [
                    [
                        'ruleName' => 'Default rule',
                        'scheduledActionConfigurations' => [
                            [
                                'actionType' => 'notification',
                                'notificationTemplateId' => 'template-123',
                            ],
                        ],
                    ],
                ],
            ]);
        }

        return new GraphResponse(success: true, data: []);
    }
}

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

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

    public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse
    {
        $this->requests[] = ['getPolicy', $policyType, $policyId, $options];

        return new GraphResponse(success: true, data: [
            'payload' => [
                'id' => $policyId,
                'name' => 'Endpoint Security Alpha',
                '@odata.type' => '#microsoft.graph.deviceManagementConfigurationPolicy',
            ],
        ]);
    }

    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, $path, $options];

        if ($method === 'GET' && str_contains($path, 'deviceManagement/configurationPolicies/') && str_ends_with($path, '/settings')) {
            return new GraphResponse(success: true, data: [
                'value' => [
                    [
                        'id' => 'setting-1',
                        'settingInstance' => [
                            '@odata.type' => '#microsoft.graph.deviceManagementConfigurationSimpleSettingInstance',
                            'settingDefinitionId' => 'device_vendor_msft_policy_config_firewall_policy_alpha',
                            'simpleSettingValue' => [
                                'value' => true,
                            ],
                        ],
                    ],
                ],
            ]);
        }

        return new GraphResponse(success: true, data: []);
    }
}

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

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

    public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse
    {
        $this->requests[] = ['getPolicy', $policyType, $policyId, $options];

        if ($policyType === 'deviceEnrollmentNotificationConfiguration') {
            return new GraphResponse(success: true, data: [
                'payload' => [
                    'id' => $policyId,
                    'displayName' => 'Enrollment Notifications',
                    '@odata.type' => '#microsoft.graph.deviceEnrollmentNotificationConfiguration',
                    'priority' => 1,
                    'version' => 1,
                    'platformType' => 'windows',
                    'brandingOptions' => 'none',
                    'templateType' => '0',
                    'notificationMessageTemplateId' => '00000000-0000-0000-0000-000000000000',
                    'notificationTemplates' => [
                        'Email_email-template-1',
                        'Push_push-template-1',
                    ],
                    'deviceEnrollmentConfigurationType' => 'enrollmentNotificationsConfiguration',
                ],
            ]);
        }

        return new GraphResponse(success: true, data: [
            'payload' => [
                'id' => $policyId,
                'displayName' => 'Policy',
                '@odata.type' => '#microsoft.graph.deviceConfiguration',
            ],
        ]);
    }

    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, $path, $options];

        if ($method === 'GET' && str_contains($path, 'deviceManagement/notificationMessageTemplates/email-template-1/localizedNotificationMessages')) {
            return new GraphResponse(success: true, data: [
                'value' => [
                    [
                        'id' => 'email-template-1_en-us',
                        'locale' => 'en-us',
                        'subject' => 'Email Subject',
                        'messageTemplate' => 'Email Body',
                        'isDefault' => true,
                    ],
                ],
            ]);
        }

        if ($method === 'GET' && str_contains($path, 'deviceManagement/notificationMessageTemplates/push-template-1/localizedNotificationMessages')) {
            return new GraphResponse(success: true, data: [
                'value' => [
                    [
                        'id' => 'push-template-1_en-us',
                        'locale' => 'en-us',
                        'subject' => 'Push Subject',
                        'messageTemplate' => 'Push Body',
                        'isDefault' => true,
                    ],
                ],
            ]);
        }

        if ($method === 'GET' && str_contains($path, 'deviceManagement/notificationMessageTemplates/email-template-1')) {
            return new GraphResponse(success: true, data: [
                '@odata.context' => 'https://graph.microsoft.com/beta/$metadata#deviceManagement/notificationMessageTemplates/$entity',
                'id' => 'email-template-1',
                'displayName' => 'Email Template',
                'defaultLocale' => 'en-us',
                'brandingOptions' => 'none',
            ]);
        }

        if ($method === 'GET' && str_contains($path, 'deviceManagement/notificationMessageTemplates/push-template-1')) {
            return new GraphResponse(success: true, data: [
                '@odata.context' => 'https://graph.microsoft.com/beta/$metadata#deviceManagement/notificationMessageTemplates/$entity',
                'id' => 'push-template-1',
                'displayName' => 'Push Template',
                'defaultLocale' => 'en-us',
                'brandingOptions' => 'none',
            ]);
        }

        return new GraphResponse(success: true, data: []);
    }
}

it('hydrates compliance policy scheduled actions into snapshots', function () {
    $client = new PolicySnapshotGraphClient;
    app()->instance(GraphClientInterface::class, $client);

    $tenant = tenantWithDefaultMicrosoftConnectionForPolicySnapshot('tenant-compliance');
    $tenant->makeCurrent();

    $policy = Policy::factory()->create([
        'managed_environment_id' => $tenant->id,
        'external_id' => 'compliance-123',
        'policy_type' => 'deviceCompliancePolicy',
        'display_name' => 'Compliance Alpha',
        'platform' => 'windows',
    ]);

    $service = app(PolicySnapshotService::class);
    $result = $service->fetch($tenant, $policy);

    expect($result)->toHaveKey('payload');
    expect($result['payload'])->toHaveKey('scheduledActionsForRule');
    expect($result['payload']['scheduledActionsForRule'])->toHaveCount(1);
    expect($result['payload']['scheduledActionsForRule'][0]['scheduledActionConfigurations'][0]['notificationTemplateId'])
        ->toBe('template-123');
    expect($result['metadata']['compliance_actions_hydration'])->toBe('complete');
    expect($client->requests[0][0])->toBe('getPolicy');
    expect($client->requests[0][1])->toBe('deviceCompliancePolicy');
    expect($client->requests[0][2])->toBe('compliance-123');
    expect($client->requests[0][3]['expand'] ?? null)
        ->toBe('scheduledActionsForRule($expand=scheduledActionConfigurations)');
});

it('hydrates configuration policy settings into snapshots', function (string $policyType) {
    $client = new ConfigurationPolicySettingsSnapshotGraphClient;
    app()->instance(GraphClientInterface::class, $client);

    $tenant = tenantWithDefaultMicrosoftConnectionForPolicySnapshot('tenant-endpoint-security');
    $tenant->makeCurrent();

    $policy = Policy::factory()->create([
        'managed_environment_id' => $tenant->id,
        'external_id' => 'esp-123',
        'policy_type' => $policyType,
        'display_name' => 'Endpoint Security Alpha',
        'platform' => 'windows',
    ]);

    $service = app(PolicySnapshotService::class);
    $result = $service->fetch($tenant, $policy);

    expect($result)->toHaveKey('payload');
    expect($result['payload'])->toHaveKey('settings');
    expect($result['payload']['settings'])->toHaveCount(1);
    expect($result['metadata']['settings_hydration'] ?? null)->toBe('complete');

    $paths = collect($client->requests)
        ->filter(fn (array $entry): bool => ($entry[0] ?? null) === 'GET')
        ->map(fn (array $entry): string => (string) ($entry[1] ?? ''))
        ->values();

    expect($paths->contains(fn (string $path): bool => str_contains($path, 'deviceManagement/configurationPolicies/esp-123/settings')))->toBeTrue();
})->with([
    'endpointSecurityPolicy',
    'securityBaselinePolicy',
]);

it('hydrates enrollment notification templates into snapshots', function () {
    $client = new EnrollmentNotificationSnapshotGraphClient;
    app()->instance(GraphClientInterface::class, $client);

    $tenant = tenantWithDefaultMicrosoftConnectionForPolicySnapshot('tenant-enrollment-notifications');
    $tenant->makeCurrent();

    $policy = Policy::factory()->create([
        'managed_environment_id' => $tenant->id,
        'external_id' => 'enroll-notify-123',
        'policy_type' => 'deviceEnrollmentNotificationConfiguration',
        'display_name' => 'Enrollment Notifications',
        'platform' => 'all',
    ]);

    $service = app(PolicySnapshotService::class);
    $result = $service->fetch($tenant, $policy);

    expect($result)->toHaveKey('payload');
    expect($result['payload'])->toHaveKey('notificationTemplateSnapshots');
    expect($result['payload']['notificationTemplateSnapshots'])->toHaveCount(2);
    expect($result['metadata']['enrollment_notification_templates_hydration'] ?? null)->toBe('complete');

    $email = collect($result['payload']['notificationTemplateSnapshots'])->firstWhere('channel', 'Email');
    expect($email)->not->toBeNull()
        ->and($email['template_id'] ?? null)->toBe('email-template-1')
        ->and($email['template']['displayName'] ?? null)->toBe('Email Template')
        ->and($email['localized_notification_messages'][0]['subject'] ?? null)->toBe('Email Subject');

    $push = collect($result['payload']['notificationTemplateSnapshots'])->firstWhere('channel', 'Push');
    expect($push)->not->toBeNull()
        ->and($push['template_id'] ?? null)->toBe('push-template-1')
        ->and($push['template']['displayName'] ?? null)->toBe('Push Template')
        ->and($push['localized_notification_messages'][0]['subject'] ?? null)->toBe('Push Subject');
});

it('filters mobile app snapshots to metadata-only keys', function () {
    $client = new PolicySnapshotGraphClient;
    app()->instance(GraphClientInterface::class, $client);

    $tenant = tenantWithDefaultMicrosoftConnectionForPolicySnapshot('tenant-apps');
    $tenant->makeCurrent();

    $policy = Policy::factory()->create([
        'managed_environment_id' => $tenant->id,
        'external_id' => 'app-123',
        'policy_type' => 'mobileApp',
        'display_name' => 'Contoso Portal',
        'platform' => 'all',
    ]);

    $service = app(PolicySnapshotService::class);
    $result = $service->fetch($tenant, $policy);

    expect($result['payload'])->toHaveKeys([
        'id',
        'displayName',
        'publisher',
        'description',
        '@odata.type',
        'createdDateTime',
        'lastModifiedDateTime',
        'roleScopeTagIds',
    ]);
    expect($result['payload']['roleScopeTagIds'])->toBe(['0', 'tag-1', 'tag-2']);
    expect($result['payload'])->not->toHaveKey('installCommandLine');
    expect($result['payload'])->not->toHaveKey('largeIcon');
    expect($client->requests[0][0])->toBe('getPolicy');
    expect($client->requests[0][1])->toBe('mobileApp');
    expect($client->requests[0][2])->toBe('app-123');
    expect($client->requests[0][3]['select'] ?? null)->toBeArray();
    expect($client->requests[0][3]['select'])->toContain('displayName');
    expect($client->requests[0][3]['select'])->toContain('roleScopeTagIds');
    expect($client->requests[0][3]['select'])->not->toContain('@odata.type');
});

it('captures windows driver update profile snapshots with full payload', function () {
    $client = new PolicySnapshotGraphClient;
    app()->instance(GraphClientInterface::class, $client);

    $tenant = tenantWithDefaultMicrosoftConnectionForPolicySnapshot('tenant-driver');
    $tenant->makeCurrent();

    $policy = Policy::factory()->create([
        'managed_environment_id' => $tenant->id,
        'external_id' => 'wdp-123',
        'policy_type' => 'windowsDriverUpdateProfile',
        'display_name' => 'Driver Updates A',
        'platform' => 'windows',
    ]);

    $service = app(PolicySnapshotService::class);
    $result = $service->fetch($tenant, $policy);

    expect($result)->toHaveKey('payload');
    expect($result['payload']['approvalType'] ?? null)->toBe('automatic');
    expect($result['payload']['deploymentDeferralInDays'] ?? null)->toBe(7);
    expect($result['payload']['deviceReporting'] ?? null)->toBe(12);
    expect($result['payload']['newUpdates'] ?? null)->toBe(3);
    expect($result['payload']['inventorySyncStatus']['driverInventorySyncState'] ?? null)->toBe('success');

    expect($client->requests[0][0])->toBe('getPolicy');
    expect($client->requests[0][1])->toBe('windowsDriverUpdateProfile');
    expect($client->requests[0][2])->toBe('wdp-123');
});

test('falls back to metadata-only snapshot when mamAppConfiguration returns 500', function () {
    $client = Mockery::mock(\App\Services\Graph\GraphClientInterface::class);
    $client->shouldReceive('getPolicy')
        ->once()
        ->andThrow(new \App\Services\Graph\GraphException('InternalServerError: upstream', 500));

    app()->instance(\App\Services\Graph\GraphClientInterface::class, $client);

    $tenant = tenantWithDefaultMicrosoftConnectionForPolicySnapshot('tenant-mam-fallback');

    $policy = Policy::factory()->create([
        'managed_environment_id' => $tenant->id,
        'external_id' => 'A_fallback-policy',
        'policy_type' => 'mamAppConfiguration',
        'display_name' => 'MAM Config Alpha',
        'platform' => 'iOS',
    ]);

    $service = app(\App\Services\Intune\PolicySnapshotService::class);
    $result = $service->fetch($tenant, $policy);

    expect($result)->toHaveKey('payload');
    expect($result)->toHaveKey('metadata');
    expect($result)->toHaveKey('warnings');
    expect($result['payload']['id'])->toBe('A_fallback-policy');
    expect($result['payload']['displayName'])->toBe('MAM Config Alpha');
    expect($result['payload']['@odata.type'])->toBe('#microsoft.graph.targetedManagedAppConfiguration');
    expect($result['payload']['platform'])->toBe('iOS');
    expect($result['metadata']['source'])->toBe('metadata_only');
    expect($result['metadata']['original_status'])->toBe(500);
    expect($result['warnings'])->toHaveCount(1);
    expect($result['warnings'][0])->toContain('Snapshot captured from local metadata only');
    expect($result['warnings'][0])->toContain('Restore preview available, full restore not possible');
});

test('does not fallback to metadata for non-5xx errors', function () {
    $client = Mockery::mock(\App\Services\Graph\GraphClientInterface::class);
    $client->shouldReceive('getPolicy')
        ->once()
        ->andThrow(new \App\Services\Graph\GraphException('NotFound', 404));

    app()->instance(\App\Services\Graph\GraphClientInterface::class, $client);

    $tenant = tenantWithDefaultMicrosoftConnectionForPolicySnapshot('tenant-404');

    $policy = Policy::factory()->create([
        'managed_environment_id' => $tenant->id,
        'external_id' => 'A_missing',
        'policy_type' => 'mamAppConfiguration',
        'display_name' => 'Missing Policy',
        'platform' => 'iOS',
    ]);

    $service = app(\App\Services\Intune\PolicySnapshotService::class);
    $result = $service->fetch($tenant, $policy);

    expect($result)->toHaveKey('failure');
    expect($result['failure']['status'])->toBe(404);
    expect($result['failure']['reason'])->toContain('NotFound');
});

test('falls back to metadata-only when graph client returns failed response for mamAppConfiguration', function () {
    $client = Mockery::mock(\App\Services\Graph\GraphClientInterface::class);
    $client->shouldReceive('getPolicy')
        ->once()
        ->andReturn(new \App\Services\Graph\GraphResponse(
            success: false,
            data: [
                'error' => [
                    'code' => 'InternalServerError',
                    'message' => 'Upstream MAM failure',
                ],
            ],
            status: 500,
            errors: [['code' => 'InternalServerError', 'message' => 'Upstream MAM failure']],
            meta: [
                'client_request_id' => 'client-req-1',
                'request_id' => 'req-1',
            ],
        ));

    app()->instance(\App\Services\Graph\GraphClientInterface::class, $client);

    $tenant = tenantWithDefaultMicrosoftConnectionForPolicySnapshot('tenant-mam-fallback-response');

    $policy = Policy::factory()->create([
        'managed_environment_id' => $tenant->id,
        'external_id' => 'A_resp_fallback',
        'policy_type' => 'mamAppConfiguration',
        'display_name' => 'MAM Config Response',
        'platform' => 'iOS',
    ]);

    $service = app(\App\Services\Intune\PolicySnapshotService::class);
    $result = $service->fetch($tenant, $policy);

    expect($result)->toHaveKey('payload');
    expect($result['metadata']['source'])->toBe('metadata_only');
    expect($result['metadata']['original_status'])->toBe(500);
    expect($result['metadata']['original_failure'])->toContain('InternalServerError');
});

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

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

    public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse
    {
        $this->requests[] = ['getPolicy', $policyType, $policyId, $options];

        return new GraphResponse(success: true, data: [
            'payload' => [
                'id' => $policyId,
                '@odata.type' => '#microsoft.graph.windowsUpdateForBusinessConfiguration',
                'displayName' => 'Ring A',
            ],
        ]);
    }

    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, $path];

        if ($method === 'GET' && $path === 'deviceManagement/deviceConfigurations/policy-wuring/microsoft.graph.windowsUpdateForBusinessConfiguration') {
            return new GraphResponse(success: true, data: [
                'automaticUpdateMode' => 'autoInstallAtMaintenanceTime',
                'featureUpdatesDeferralPeriodInDays' => 14,
            ]);
        }

        return new GraphResponse(success: true, data: []);
    }
}

it('hydrates windows update ring snapshots via derived type cast endpoint', function () {
    $client = new WindowsUpdateRingSnapshotGraphClient;
    app()->instance(GraphClientInterface::class, $client);

    $tenant = tenantWithDefaultMicrosoftConnectionForPolicySnapshot('tenant-wuring');
    $tenant->makeCurrent();

    $policy = Policy::factory()->create([
        'managed_environment_id' => $tenant->id,
        'external_id' => 'policy-wuring',
        'policy_type' => 'windowsUpdateRing',
        'display_name' => 'Ring A',
        'platform' => 'windows',
    ]);

    $service = app(PolicySnapshotService::class);
    $result = $service->fetch($tenant, $policy);

    expect($result)->toHaveKey('payload');
    expect($result['payload']['@odata.type'])->toBe('#microsoft.graph.windowsUpdateForBusinessConfiguration');
    expect($result['payload']['automaticUpdateMode'])->toBe('autoInstallAtMaintenanceTime');
    expect($result['payload']['featureUpdatesDeferralPeriodInDays'])->toBe(14);
    expect($result['metadata']['properties_hydration'] ?? null)->toBe('complete');
});

class FailedSnapshotGraphClient implements GraphClientInterface
{
    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: false,
            data: [],
            status: 500,
            errors: [],
            warnings: [],
            meta: [
                'error_code' => 'InternalServerError',
                'error_message' => 'An internal server error has occurred',
                'request_id' => 'req-123',
                'client_request_id' => 'client-456',
            ],
        );
    }

    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
    {
        return new GraphResponse(success: true, data: []);
    }
}

it('returns actionable reasons when graph snapshot fails', function () {
    app()->instance(GraphClientInterface::class, new FailedSnapshotGraphClient);

    $tenant = tenantWithDefaultMicrosoftConnectionForPolicySnapshot('tenant-failure');
    $tenant->makeCurrent();

    $policy = Policy::factory()->create([
        'managed_environment_id' => $tenant->id,
        'external_id' => 'mam-123',
        'policy_type' => 'deviceCompliancePolicy',
        'display_name' => 'Compliance Config',
        'platform' => 'mobile',
    ]);

    $service = app(PolicySnapshotService::class);
    $result = $service->fetch($tenant, $policy);

    expect($result)->toHaveKey('failure');
    expect($result['failure']['status'])->toBe(500);
    expect($result['failure']['reason'])->toContain('InternalServerError');
    expect($result['failure']['reason'])->toContain('An internal server error has occurred');
    expect($result['failure']['reason'])->toContain('client_request_id=client-456');
    expect($result['failure']['reason'])->toContain('request_id=req-123');
});
