<?php

use App\Filament\Resources\BackupSetResource;
use App\Models\Policy;
use App\Models\PolicyVersion;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Services\Graph\GraphClientInterface;
use App\Services\Graph\GraphResponse;
use App\Services\Graph\ScopeTagResolver;
use App\Services\Intune\BackupService;
use App\Services\Intune\PolicySnapshotService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Mockery\MockInterface;

uses(RefreshDatabase::class);

test('backup creation captures snapshots and audit log', function () {
    // Mock PolicySnapshotService
    $this->mock(PolicySnapshotService::class, function (MockInterface $mock) {
        $mock->shouldReceive('fetch')
            ->once() // Called once for the active policy
            ->andReturnUsing(function ($tenant, $policy) {
                return [
                    'payload' => [
                        'id' => $policy->external_id,
                        'name' => $policy->display_name,
                        'roleScopeTagIds' => ['0'],
                    ],
                    'metadata' => [],
                    'warnings' => [],
                ];
            });
    });

    // Mock ScopeTagResolver
    $this->mock(ScopeTagResolver::class, function (MockInterface $mock) {
        $mock->shouldReceive('resolve')
            ->andReturn([['id' => '0', 'displayName' => 'Default']]);
    });

    app()->bind(GraphClientInterface::class, fn () => new class implements GraphClientInterface
    {
        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' => ['policyId' => $policyId, 'type' => $policyType]]);
        }

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

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

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

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

    $tenant = ManagedEnvironment::create([
        'managed_environment_id' => 'local-tenant',
        'name' => 'ManagedEnvironment One',
        'metadata' => [],
    ]);

    ensureDefaultProviderConnection($tenant);

    $tenant->makeCurrent();

    $policyA = Policy::create([
        'managed_environment_id' => $tenant->id,
        'external_id' => 'policy-1',
        'policy_type' => 'deviceConfiguration',
        'display_name' => 'Policy A',
        'platform' => 'windows',
        'last_synced_at' => now(),
    ]);

    $policyB = Policy::create([
        'managed_environment_id' => $tenant->id,
        'external_id' => 'policy-2',
        'policy_type' => 'deviceCompliancePolicy',
        'display_name' => 'Policy B',
        'platform' => 'windows',
        'last_synced_at' => now(),
        'ignored_at' => now(),
    ]);

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

    $backupSet = BackupSetResource::createBackupSet([
        'name' => 'Test backup',
    ]);

    app(BackupService::class)->addPoliciesToSet(
        tenant: $tenant,
        backupSet: $backupSet,
        policyIds: [$policyA->id],
        actorEmail: $user->email,
        actorName: $user->name,
        includeAssignments: false,
        includeScopeTags: true,
        includeFoundations: true,
    );

    $backupSet->refresh();

    expect($backupSet->item_count)->toBe(1);
    expect($backupSet->items)->toHaveCount(1);
    expect($backupSet->items->first()->payload['id'])->toBe('policy-1');

    $firstVersion = PolicyVersion::find($backupSet->items->first()->policy_version_id);
    expect($firstVersion)->not->toBeNull();
    expect($firstVersion->scope_tags)->toBe([
        'ids' => ['0'],
        'names' => ['Default'],
    ]);
    expect($firstVersion->assignments)->toBeNull();

    $this->assertDatabaseHas('audit_logs', [
        'action' => 'backup.created',
        'resource_type' => 'backup_set',
        'resource_id' => (string) $backupSet->id,
    ]);

    $this->assertDatabaseHas('audit_logs', [
        'action' => 'backup.items_added',
        'resource_type' => 'backup_set',
        'resource_id' => (string) $backupSet->id,
    ]);
});

test('backup service skips ignored and provider-missing policies', function () {
    $this->mock(PolicySnapshotService::class, function (MockInterface $mock) {
        $mock->shouldReceive('fetch')
            ->once()
            ->andReturnUsing(function ($tenant, $policy) {
                return [
                    'payload' => [
                        'id' => $policy->external_id,
                        'name' => $policy->display_name,
                        'roleScopeTagIds' => ['0'],
                    ],
                    'metadata' => [],
                    'warnings' => [],
                ];
            });
    });

    $tenant = ManagedEnvironment::create([
        'name' => 'Test tenant',
        'external_id' => 'tenant-1',
        'managed_environment_id' => 'tenant-1',
        'status' => 'active',
        'metadata' => [],
    ]);

    ensureDefaultProviderConnection($tenant);

    $tenant->makeCurrent();

    $policyA = Policy::create([
        'managed_environment_id' => $tenant->id,
        'external_id' => 'policy-1',
        'policy_type' => 'deviceConfiguration',
        'display_name' => 'Policy A',
        'platform' => 'windows',
        'last_synced_at' => now(),
    ]);

    $policyB = Policy::create([
        'managed_environment_id' => $tenant->id,
        'external_id' => 'policy-2',
        'policy_type' => 'deviceCompliancePolicy',
        'display_name' => 'Policy B',
        'platform' => 'windows',
        'last_synced_at' => now(),
        'ignored_at' => now(),
    ]);

    $policyC = Policy::create([
        'managed_environment_id' => $tenant->id,
        'external_id' => 'policy-3',
        'policy_type' => 'deviceCompliancePolicy',
        'display_name' => 'Policy C',
        'platform' => 'windows',
        'last_synced_at' => now(),
        'missing_from_provider_at' => now(),
    ]);

    $policyD = Policy::create([
        'managed_environment_id' => $tenant->id,
        'external_id' => 'policy-4',
        'policy_type' => 'deviceCompliancePolicy',
        'display_name' => 'Policy D',
        'platform' => 'windows',
        'last_synced_at' => now(),
        'ignored_at' => now(),
        'missing_from_provider_at' => now(),
    ]);

    $service = app(\App\Services\Intune\BackupService::class);
    $backupSet = $service->createBackupSet(
        tenant: $tenant,
        policyIds: [$policyA->id, $policyB->id, $policyC->id, $policyD->id],
        actorEmail: 'tester@example.com',
        actorName: 'Tester',
    );

    expect($backupSet->item_count)->toBe(1);
    expect($backupSet->items->pluck('policy_id')->all())->toBe([$policyA->id]);
    expect($policyD->currentBackupBlockedReason())->toBe(Policy::VISIBILITY_PROVIDER_MISSING);
});
