<?php

use App\Models\BackupItem;
use App\Models\BackupSet;
use App\Models\Policy;
use App\Models\PolicyVersion;
use App\Models\ProviderConnection;
use App\Models\ProviderCredential;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Services\Graph\GraphClientInterface;
use App\Services\Graph\GraphResponse;
use App\Services\Intune\RestoreService;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

if (! function_exists('makeTenantWithDefaultProviderConnection')) {
    function makeTenantWithDefaultProviderConnection(array $attributes = []): ManagedEnvironment
    {
        $tenant = ManagedEnvironment::create(array_merge([
            'managed_environment_id' => 'tenant-1',
            'name' => 'ManagedEnvironment One',
            'metadata' => [],
        ], $attributes));

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

        ProviderCredential::factory()->create([
            'provider_connection_id' => $connection->id,
        ]);

        return $tenant;
    }
}

test('restore execution applies windows update ring and records audit log', function () {
    $client = new class implements GraphClientInterface
    {
        public array $applied = [];

        public array $requests = [];

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

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

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

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

            return new GraphResponse(true, []);
        }

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

            return new GraphResponse(true, []);
        }

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

    app()->instance(GraphClientInterface::class, $client);

    $tenant = makeTenantWithDefaultProviderConnection();

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

    $backupSet = BackupSet::create([
        'managed_environment_id' => $tenant->id,
        'name' => 'Backup',
        'status' => 'completed',
        'item_count' => 1,
    ]);

    $backupPayload = [
        'id' => 'policy-wuring',
        '@odata.type' => '#microsoft.graph.windowsUpdateForBusinessConfiguration',
        'automaticUpdateMode' => 'autoInstallAtMaintenanceTime',
        'featureUpdatesDeferralPeriodInDays' => 14,
        'version' => 7,
        'qualityUpdatesPauseStartDate' => '2025-01-01T00:00:00Z',
        'featureUpdatesPauseStartDate' => '2025-01-02T00:00:00Z',
        'qualityUpdatesWillBeRolledBack' => false,
        'featureUpdatesWillBeRolledBack' => false,
        'roleScopeTagIds' => ['0'],
    ];

    $backupItem = BackupItem::create([
        'managed_environment_id' => $tenant->id,
        'backup_set_id' => $backupSet->id,
        'policy_id' => $policy->id,
        'policy_identifier' => $policy->external_id,
        'policy_type' => $policy->policy_type,
        'platform' => $policy->platform,
        'payload' => $backupPayload,
    ]);

    $user = User::factory()->create(['email' => 'tester@example.com']);
    $this->actingAs($user);

    $service = app(RestoreService::class);
    $run = $service->execute(
        tenant: $tenant,
        backupSet: $backupSet,
        selectedItemIds: [$backupItem->id],
        dryRun: false,
        actorEmail: $user->email,
        actorName: $user->name,
    );

    expect($run->status)->toBe('completed');
    $result = $run->results['items'][$backupItem->id] ?? null;
    expect($result)->not->toBeNull();
    expect($result['status'] ?? null)->toBe('applied');

    $this->assertDatabaseHas('audit_logs', [
        'action' => 'restore.executed',
        'resource_id' => (string) $run->id,
    ]);

    expect(PolicyVersion::where('policy_id', $policy->id)->count())->toBe(1);

    expect($client->requests)->toHaveCount(1);
    expect($client->requests[0]['method'])->toBe('PATCH');
    expect($client->requests[0]['path'])->toBe('deviceManagement/deviceConfigurations/policy-wuring/microsoft.graph.windowsUpdateForBusinessConfiguration');

    expect($client->requests[0]['payload']['automaticUpdateMode'])->toBe('autoInstallAtMaintenanceTime');
    expect($client->requests[0]['payload']['featureUpdatesDeferralPeriodInDays'])->toBe(14);
    expect($client->requests[0]['payload']['roleScopeTagIds'])->toBe(['0']);

    expect($client->requests[0]['payload'])->not->toHaveKey('id');
    expect($client->requests[0]['payload'])->not->toHaveKey('@odata.type');
    expect($client->requests[0]['payload'])->not->toHaveKey('version');
    expect($client->requests[0]['payload'])->not->toHaveKey('qualityUpdatesPauseStartDate');
    expect($client->requests[0]['payload'])->not->toHaveKey('featureUpdatesPauseStartDate');
    expect($client->requests[0]['payload'])->not->toHaveKey('qualityUpdatesWillBeRolledBack');
    expect($client->requests[0]['payload'])->not->toHaveKey('featureUpdatesWillBeRolledBack');
});
