<?php

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

uses(RefreshDatabase::class);

class FakeGraphClientForSync implements GraphClientInterface
{
    /**
     * @param  array<string, GraphResponse>  $responses
     */
    public function __construct(private array $responses = []) {}

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

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

    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, []);
    }
}

test('sync preserves local ignore when policies still exist in Intune', function () {
    $tenant = ManagedEnvironment::create([
        'managed_environment_id' => 'test-tenant',
        'name' => 'Test ManagedEnvironment',
        'metadata' => [],
        'is_current' => true,
    ]);

    ensureDefaultProviderConnection($tenant);

    // Create an ignored policy
    $policy = Policy::create([
        'managed_environment_id' => $tenant->id,
        'external_id' => 'policy-123',
        'policy_type' => 'deviceConfiguration',
        'display_name' => 'Test Policy',
        'platform' => 'windows',
        'ignored_at' => now(),
    ]);

    expect($policy->ignored_at)->not->toBeNull();

    // Mock Graph response with the same policy
    $responses = [
        'deviceConfiguration' => new GraphResponse(true, [
            [
                'id' => 'policy-123',
                'displayName' => 'Test Policy (Updated)',
                'platform' => 'windows',
            ],
        ]),
    ];

    app()->instance(GraphClientInterface::class, new FakeGraphClientForSync($responses));

    // Sync policies
    app(PolicySyncService::class)->syncPolicies($tenant);

    // Refresh the policy
    $policy->refresh();

    // Provider reappearance updates local metadata, but only a user action clears local ignore.
    expect($policy->ignored_at)->not->toBeNull();
    expect($policy->missing_from_provider_at)->toBeNull();
    expect($policy->display_name)->toBe('Test Policy (Updated)');
    expect($policy->last_synced_at)->not->toBeNull();
});

test('sync updates ignored policies without reviving them', function () {
    $tenant = ManagedEnvironment::create([
        'managed_environment_id' => 'test-tenant-2',
        'name' => 'Test ManagedEnvironment 2',
        'metadata' => [],
        'is_current' => true,
    ]);

    ensureDefaultProviderConnection($tenant);

    // Create multiple ignored policies
    Policy::create([
        'managed_environment_id' => $tenant->id,
        'external_id' => 'policy-abc',
        'policy_type' => 'deviceConfiguration',
        'display_name' => 'Old Policy ABC',
        'platform' => 'windows',
        'ignored_at' => now()->subDay(),
    ]);

    Policy::create([
        'managed_environment_id' => $tenant->id,
        'external_id' => 'policy-def',
        'policy_type' => 'deviceCompliancePolicy',
        'display_name' => 'Old Policy DEF',
        'platform' => 'android',
        'ignored_at' => now()->subDay(),
    ]);

    expect(Policy::active()->count())->toBe(0);
    expect(Policy::ignored()->count())->toBe(2);

    // Mock Graph response with same policy IDs but potentially different data
    $responses = [
        'deviceConfiguration' => new GraphResponse(true, [
            [
                'id' => 'policy-abc',
                'displayName' => 'Restored Policy ABC',
                'platform' => 'windows',
            ],
        ]),
        'deviceCompliancePolicy' => new GraphResponse(true, [
            [
                'id' => 'policy-def',
                'displayName' => 'Restored Policy DEF',
                'platform' => 'android',
            ],
        ]),
    ];

    app()->instance(GraphClientInterface::class, new FakeGraphClientForSync($responses));

    // Sync policies
    app(PolicySyncService::class)->syncPolicies($tenant);

    // Both provider-visible policies remain locally ignored until explicitly restored.
    expect(Policy::active()->count())->toBe(0);
    expect(Policy::ignored()->count())->toBe(2);

    $policyAbc = Policy::where('external_id', 'policy-abc')->first();
    expect($policyAbc->display_name)->toBe('Restored Policy ABC');
    expect($policyAbc->ignored_at)->not->toBeNull();
    expect($policyAbc->missing_from_provider_at)->toBeNull();

    $policyDef = Policy::where('external_id', 'policy-def')->first();
    expect($policyDef->display_name)->toBe('Restored Policy DEF');
    expect($policyDef->ignored_at)->not->toBeNull();
    expect($policyDef->missing_from_provider_at)->toBeNull();
});
