<?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;
    }
}

class WindowsUpdateProfilesRestoreGraphClient implements GraphClientInterface
{
    /**
     * @var array<int, array{policyType:string,policyId:string,payload:array,options:array}>
     */
    public array $applyPolicyCalls = [];

    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->applyPolicyCalls[] = [
            'policyType' => $policyType,
            'policyId' => $policyId,
            'payload' => $payload,
            'options' => $options,
        ];

        return new GraphResponse(true, []);
    }

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

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

test('restore execution applies windows feature update profile with sanitized payload', function () {
    $client = new WindowsUpdateProfilesRestoreGraphClient;
    app()->instance(GraphClientInterface::class, $client);

    $tenant = makeTenantWithDefaultProviderConnection();

    $policy = Policy::create([
        'managed_environment_id' => $tenant->id,
        'external_id' => 'policy-feature',
        'policy_type' => 'windowsFeatureUpdateProfile',
        'display_name' => 'Feature Updates A',
        'platform' => 'windows',
    ]);

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

    $backupPayload = [
        'id' => 'policy-feature',
        '@odata.type' => '#microsoft.graph.windowsFeatureUpdateProfile',
        'displayName' => 'Feature Updates A',
        'featureUpdateVersion' => 'Windows 11, version 23H2',
        'deployableContentDisplayName' => 'Some Content',
        'endOfSupportDate' => '2026-01-01T00:00:00Z',
        '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');

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

    expect($client->applyPolicyCalls)->toHaveCount(1);
    expect($client->applyPolicyCalls[0]['policyType'])->toBe('windowsFeatureUpdateProfile');
    expect($client->applyPolicyCalls[0]['policyId'])->toBe('policy-feature');
    expect($client->applyPolicyCalls[0]['options']['method'] ?? null)->toBe('PATCH');

    expect($client->applyPolicyCalls[0]['payload']['featureUpdateVersion'])->toBe('Windows 11, version 23H2');
    expect($client->applyPolicyCalls[0]['payload']['roleScopeTagIds'])->toBe(['0']);

    expect($client->applyPolicyCalls[0]['payload'])->not->toHaveKey('id');
    expect($client->applyPolicyCalls[0]['payload'])->not->toHaveKey('@odata.type');
    expect($client->applyPolicyCalls[0]['payload'])->not->toHaveKey('deployableContentDisplayName');
    expect($client->applyPolicyCalls[0]['payload'])->not->toHaveKey('endOfSupportDate');
});

test('restore execution applies windows quality update profile with sanitized payload', function () {
    $client = new WindowsUpdateProfilesRestoreGraphClient;
    app()->instance(GraphClientInterface::class, $client);

    $tenant = makeTenantWithDefaultProviderConnection();

    $policy = Policy::create([
        'managed_environment_id' => $tenant->id,
        'external_id' => 'policy-quality',
        'policy_type' => 'windowsQualityUpdateProfile',
        'display_name' => 'Quality Updates A',
        'platform' => 'windows',
    ]);

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

    $backupPayload = [
        'id' => 'policy-quality',
        '@odata.type' => '#microsoft.graph.windowsQualityUpdateProfile',
        'displayName' => 'Quality Updates A',
        'qualityUpdateCveIds' => ['CVE-2025-0001'],
        'deployableContentDisplayName' => 'Some Content',
        'releaseDateDisplayName' => 'January 2026',
        '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');

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

    expect($client->applyPolicyCalls)->toHaveCount(1);
    expect($client->applyPolicyCalls[0]['policyType'])->toBe('windowsQualityUpdateProfile');
    expect($client->applyPolicyCalls[0]['policyId'])->toBe('policy-quality');
    expect($client->applyPolicyCalls[0]['options']['method'] ?? null)->toBe('PATCH');

    expect($client->applyPolicyCalls[0]['payload']['qualityUpdateCveIds'])->toBe(['CVE-2025-0001']);
    expect($client->applyPolicyCalls[0]['payload']['roleScopeTagIds'])->toBe(['0']);

    expect($client->applyPolicyCalls[0]['payload'])->not->toHaveKey('id');
    expect($client->applyPolicyCalls[0]['payload'])->not->toHaveKey('@odata.type');
    expect($client->applyPolicyCalls[0]['payload'])->not->toHaveKey('deployableContentDisplayName');
    expect($client->applyPolicyCalls[0]['payload'])->not->toHaveKey('releaseDateDisplayName');
});

test('restore execution applies windows driver update profile with sanitized payload', function () {
    $client = new WindowsUpdateProfilesRestoreGraphClient;
    app()->instance(GraphClientInterface::class, $client);

    $tenant = makeTenantWithDefaultProviderConnection();

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

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

    $backupPayload = [
        'id' => 'policy-driver',
        '@odata.type' => '#microsoft.graph.windowsDriverUpdateProfile',
        'displayName' => 'Driver Updates A',
        'description' => 'Drivers rollout policy',
        'approvalType' => 'automatic',
        'deploymentDeferralInDays' => 7,
        'deviceReporting' => 12,
        'newUpdates' => 3,
        'inventorySyncStatus' => [
            'driverInventorySyncState' => 'success',
            'lastSuccessfulSyncDateTime' => '2026-01-01T00:00:00Z',
        ],
        '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');

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

    expect($client->applyPolicyCalls)->toHaveCount(1);
    expect($client->applyPolicyCalls[0]['policyType'])->toBe('windowsDriverUpdateProfile');
    expect($client->applyPolicyCalls[0]['policyId'])->toBe('policy-driver');
    expect($client->applyPolicyCalls[0]['options']['method'] ?? null)->toBe('PATCH');

    expect($client->applyPolicyCalls[0]['payload']['approvalType'])->toBe('automatic');
    expect($client->applyPolicyCalls[0]['payload']['deploymentDeferralInDays'])->toBe(7);
    expect($client->applyPolicyCalls[0]['payload']['roleScopeTagIds'])->toBe(['0']);

    expect($client->applyPolicyCalls[0]['payload'])->not->toHaveKey('id');
    expect($client->applyPolicyCalls[0]['payload'])->not->toHaveKey('@odata.type');
    expect($client->applyPolicyCalls[0]['payload'])->not->toHaveKey('deviceReporting');
    expect($client->applyPolicyCalls[0]['payload'])->not->toHaveKey('newUpdates');
    expect($client->applyPolicyCalls[0]['payload'])->not->toHaveKey('inventorySyncStatus');
});
