<?php

use App\Models\SettingsCatalogDefinition;
use App\Services\Graph\GraphClientInterface;
use App\Services\Graph\GraphContractRegistry;
use App\Services\Graph\GraphResponse;
use App\Services\Intune\SettingsCatalogDefinitionResolver;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;

uses(RefreshDatabase::class);

beforeEach(function () {
    // Clear cache before each test
    Cache::flush();
});

it('uses cached definitions from database on second call', function () {
    // Arrange
    $definitionId = 'device_vendor_msft_policy_config_defender_allowbehaviormonitoring';

    // Pre-populate cache
    SettingsCatalogDefinition::create([
        'definition_id' => $definitionId,
        'display_name' => 'Allow Behavior Monitoring',
        'description' => 'Enable behavior monitoring',
        'help_text' => 'This setting controls...',
        'raw' => ['id' => $definitionId],
    ]);

    $mockClient = Mockery::mock(GraphClientInterface::class);
    // Should NOT call Graph API
    $mockClient->shouldNotReceive('request');

    $resolver = new SettingsCatalogDefinitionResolver($mockClient, new GraphContractRegistry);

    // Act
    $result = $resolver->resolve([$definitionId]);

    // Assert
    expect($result)->toHaveCount(1);
    expect($result[$definitionId])->toMatchArray([
        'displayName' => 'Allow Behavior Monitoring',
        'description' => 'Enable behavior monitoring',
    ]);
});

it('returns fallback for missing definitions with prettified ID', function () {
    // Arrange
    $definitionId = 'device_vendor_msft_policy_config_unknown_setting';

    $mockClient = Mockery::mock(GraphClientInterface::class);
    $mockResponse = Mockery::mock(GraphResponse::class);
    $mockResponse->shouldReceive('successful')->andReturn(false);

    $mockClient->shouldReceive('request')
        ->once()
        ->andReturn($mockResponse);

    $resolver = new SettingsCatalogDefinitionResolver($mockClient, new GraphContractRegistry);

    // Act
    $result = $resolver->resolve([$definitionId]);

    // Assert
    expect($result)->toHaveCount(1);
    expect($result[$definitionId])->toMatchArray([
        'displayName' => 'Device Vendor Msft Policy Config Unknown Setting',
        'description' => null,
        'isFallback' => true,
    ]);
});

it('resolveOne method returns single definition from cache', function () {
    // Arrange
    $definitionId = 'device_vendor_msft_policy_config_connectivity_disallownetworkconnectivityactivetest';

    SettingsCatalogDefinition::create([
        'definition_id' => $definitionId,
        'display_name' => 'Disallow Network Connectivity Active Test',
        'description' => 'Disable NCSI probes',
        'raw' => ['id' => $definitionId],
    ]);

    $mockClient = Mockery::mock(GraphClientInterface::class);
    $mockClient->shouldNotReceive('request');

    $resolver = new SettingsCatalogDefinitionResolver($mockClient, new GraphContractRegistry);

    // Act
    $result = $resolver->resolveOne($definitionId);

    // Assert
    expect($result)->toMatchArray([
        'displayName' => 'Disallow Network Connectivity Active Test',
        'description' => 'Disable NCSI probes',
    ]);
});

it('handles batch of definitions with mixed cached and uncached', function () {
    // Arrange
    $cachedId = 'device_vendor_msft_policy_config_cached_setting';
    $uncachedId = 'device_vendor_msft_policy_config_uncached_setting';

    // Pre-cache one definition
    SettingsCatalogDefinition::create([
        'definition_id' => $cachedId,
        'display_name' => 'Cached Setting',
        'description' => 'This was cached',
        'raw' => ['id' => $cachedId],
    ]);

    $mockClient = Mockery::mock(GraphClientInterface::class);
    $mockResponse = Mockery::mock(GraphResponse::class);
    $mockResponse->shouldReceive('successful')->andReturn(false);

    $mockClient->shouldReceive('request')
        ->once()
        ->with('GET', "/deviceManagement/configurationSettings/{$uncachedId}")
        ->andReturn($mockResponse);

    $resolver = new SettingsCatalogDefinitionResolver($mockClient, new GraphContractRegistry);

    // Act
    $result = $resolver->resolve([$cachedId, $uncachedId]);

    // Assert
    expect($result)->toHaveCount(2);
    expect($result[$cachedId]['displayName'])->toBe('Cached Setting');
    expect($result[$uncachedId]['displayName'])->toBe('Device Vendor Msft Policy Config Uncached Setting'); // Fallback
    expect($result[$uncachedId]['isFallback'])->toBeTrue();
});

it('warmCache method pre-populates cache without throwing', function () {
    // Arrange
    $definitionId = 'device_vendor_msft_policy_config_firewall_enablefirewall';

    SettingsCatalogDefinition::create([
        'definition_id' => $definitionId,
        'display_name' => 'Enable Firewall',
        'description' => 'Turn Windows Firewall on or off',
        'raw' => ['id' => $definitionId],
    ]);

    $mockClient = Mockery::mock(GraphClientInterface::class);
    $mockClient->shouldNotReceive('request');

    $resolver = new SettingsCatalogDefinitionResolver($mockClient, new GraphContractRegistry);

    // Act & Assert (should not throw)
    expect(fn () => $resolver->warmCache([$definitionId]))->not->toThrow(Exception::class);

    // Cache should be populated
    $cached = SettingsCatalogDefinition::where('definition_id', $definitionId)->first();
    expect($cached)->not->toBeNull();
    expect($cached->display_name)->toBe('Enable Firewall');
});

it('warmCache handles errors gracefully without throwing', function () {
    // Arrange
    $definitionIds = ['device_vendor_msft_policy_config_test'];

    $mockClient = Mockery::mock(GraphClientInterface::class);
    $mockClient->shouldReceive('request')
        ->once()
        ->andThrow(new Exception('Graph API error'));

    $resolver = new SettingsCatalogDefinitionResolver($mockClient, new GraphContractRegistry);

    // Act & Assert (should not throw)
    expect(fn () => $resolver->warmCache($definitionIds))->not->toThrow(Exception::class);

    // Cache should remain empty
    $cached = SettingsCatalogDefinition::where('definition_id', $definitionIds[0])->first();
    expect($cached)->toBeNull();
});
