<?php

use App\Models\BackupSet;
use App\Models\Policy;
use App\Models\PolicyVersion;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Services\Intune\BackupService;
use App\Services\Intune\FoundationSnapshotService;
use App\Services\Intune\PolicyCaptureOrchestrator;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Mockery\MockInterface;

uses(RefreshDatabase::class);

it('reuses latest policy version for backup when it is up-to-date and satisfies capture options', function () {
    $tenant = ManagedEnvironment::factory()->create();
    $tenant->makeCurrent();

    $user = User::factory()->create();
    $this->actingAs($user);

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

    $policy = Policy::factory()->create([
        'managed_environment_id' => $tenant->id,
        'last_synced_at' => now(),
        'ignored_at' => null,
    ]);

    $existingVersion = PolicyVersion::factory()->create([
        'managed_environment_id' => $tenant->id,
        'policy_id' => $policy->id,
        'captured_at' => now(),
        'snapshot' => ['id' => $policy->external_id, 'name' => $policy->display_name],
        'assignments' => null,
        'scope_tags' => null,
    ]);

    $this->mock(PolicyCaptureOrchestrator::class, function (MockInterface $mock) use ($existingVersion) {
        $mock->shouldReceive('capture')
            ->once()
            ->andReturn([
                'version' => $existingVersion,
                'captured' => [
                    'payload' => $existingVersion->snapshot,
                    'assignments' => $existingVersion->assignments,
                    'scope_tags' => $existingVersion->scope_tags,
                    'metadata' => [],
                ],
            ]);
    });

    $service = app(BackupService::class);

    $service->addPoliciesToSet(
        tenant: $tenant,
        backupSet: $backupSet,
        policyIds: [$policy->id],
        actorEmail: $user->email,
        actorName: $user->name,
        includeAssignments: false,
        includeScopeTags: false,
        includeFoundations: false,
    );

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

    $item = $backupSet->items()->first();
    expect($item)->not->toBeNull();
    expect($item->policy_version_id)->toBe($existingVersion->id);
});

it('captures a new policy version for backup when no suitable existing version is available', function () {
    $tenant = ManagedEnvironment::factory()->create();
    $tenant->makeCurrent();

    $user = User::factory()->create();
    $this->actingAs($user);

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

    $policy = Policy::factory()->create([
        'managed_environment_id' => $tenant->id,
        'last_synced_at' => now(),
        'ignored_at' => null,
    ]);

    $staleVersion = PolicyVersion::factory()->create([
        'managed_environment_id' => $tenant->id,
        'policy_id' => $policy->id,
        'version_number' => 1,
        'captured_at' => now()->subDays(2),
        'snapshot' => ['id' => $policy->external_id, 'name' => $policy->display_name],
    ]);

    $policy->update(['last_synced_at' => now()]);

    $this->mock(PolicyCaptureOrchestrator::class, function (MockInterface $mock) use ($policy, $tenant) {
        $mock->shouldReceive('capture')
            ->once()
            ->andReturnUsing(function () use ($policy, $tenant) {
                $newVersion = PolicyVersion::factory()->create([
                    'managed_environment_id' => $tenant->id,
                    'policy_id' => $policy->id,
                    'version_number' => 2,
                    'captured_at' => now(),
                    'snapshot' => ['id' => $policy->external_id, 'name' => $policy->display_name, 'changed' => true],
                ]);

                return [
                    'version' => $newVersion,
                    'captured' => [
                        'payload' => $newVersion->snapshot,
                        'assignments' => null,
                        'scope_tags' => null,
                        'metadata' => [],
                    ],
                ];
            });
    });

    $service = app(BackupService::class);

    $service->addPoliciesToSet(
        tenant: $tenant,
        backupSet: $backupSet,
        policyIds: [$policy->id],
        actorEmail: $user->email,
        actorName: $user->name,
        includeAssignments: false,
        includeScopeTags: false,
        includeFoundations: false,
    );

    $item = $backupSet->items()->first();
    expect($item)->not->toBeNull();
    expect($item->policy_version_id)->not->toBe($staleVersion->id);
});

it('reuses an existing RBAC foundation version across backup sets when the snapshot is unchanged', function () {
    $tenant = ManagedEnvironment::factory()->create(['status' => 'active']);
    ensureDefaultProviderConnection($tenant);

    config()->set('tenantpilot.foundation_types', [
        [
            'type' => 'intuneRoleDefinition',
            'label' => 'Intune Role Definition',
            'category' => 'RBAC',
            'platform' => 'all',
            'endpoint' => 'deviceManagement/roleDefinitions',
            'backup' => 'full',
            'restore' => 'preview-only',
            'risk' => 'high',
        ],
    ]);

    $payload = [
        'id' => 'role-def-1',
        'displayName' => 'Policy and Profile Manager',
        'description' => 'Built-in RBAC role',
        'isBuiltIn' => true,
        'rolePermissions' => [
            [
                'resourceActions' => [
                    [
                        'allowedResourceActions' => [
                            'Microsoft.Intune/deviceConfigurations/read',
                        ],
                    ],
                ],
            ],
        ],
    ];

    $this->mock(FoundationSnapshotService::class, function (MockInterface $mock) use ($payload) {
        $mock->shouldReceive('fetchAll')
            ->twice()
            ->withArgs(fn (ManagedEnvironment $tenant, string $foundationType): bool => $foundationType === 'intuneRoleDefinition')
            ->andReturn([
                'items' => [[
                    'source_id' => 'role-def-1',
                    'display_name' => 'Policy and Profile Manager',
                    'payload' => $payload,
                    'metadata' => [
                        'displayName' => 'Policy and Profile Manager',
                        'kind' => 'intuneRoleDefinition',
                        'graph' => [
                            'resource' => 'deviceManagement/roleDefinitions',
                            'apiVersion' => 'beta',
                        ],
                    ],
                ]],
                'failures' => [],
            ]);
    });

    $service = app(BackupService::class);

    $firstBackupSet = $service->createBackupSet(
        tenant: $tenant,
        policyIds: [],
        name: 'RBAC Backup 1',
        includeFoundations: true,
    );

    $secondBackupSet = $service->createBackupSet(
        tenant: $tenant,
        policyIds: [],
        name: 'RBAC Backup 2',
        includeFoundations: true,
    );

    $firstItem = $firstBackupSet->items()->first();
    $secondItem = $secondBackupSet->items()->first();

    expect($firstItem)->not->toBeNull();
    expect($secondItem)->not->toBeNull();
    expect($firstItem->policy_id)->toBe($secondItem->policy_id);
    expect($firstItem->policy_version_id)->toBe($secondItem->policy_version_id);
    expect(PolicyVersion::query()->where('managed_environment_id', $tenant->id)->where('policy_type', 'intuneRoleDefinition')->count())->toBe(1);
});
