<?php

use App\Models\Tenant;
use App\Services\Graph\GraphLogger;
use App\Services\Graph\GraphResponse;
use App\Services\Graph\MicrosoftGraphClient;
use App\Services\Graph\ScopeTagResolver;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;

uses(RefreshDatabase::class);
beforeEach(function () {
    Cache::flush();
});

test('resolves scope tag IDs to objects with id and displayName', function () {
    $tenant = Tenant::factory()->create();

    $mockGraphClient = Mockery::mock(MicrosoftGraphClient::class);
    $mockGraphClient->shouldReceive('request')
        ->with('GET', '/deviceManagement/roleScopeTags', Mockery::on(function ($options) use ($tenant) {
            return $options['query']['$select'] === 'id,displayName'
                && $options['tenant'] === $tenant->external_id
                && $options['client_id'] === $tenant->app_client_id
                && $options['client_secret'] === $tenant->app_client_secret;
        }))
        ->once()
        ->andReturn(new GraphResponse(
            success: true,
            data: [
                'value' => [
                    ['id' => '0', 'displayName' => 'Default'],
                    ['id' => '1', 'displayName' => 'Verbund-1'],
                    ['id' => '2', 'displayName' => 'Verbund-2'],
                ],
            ]
        ));

    $mockLogger = Mockery::mock(GraphLogger::class);

    $resolver = new ScopeTagResolver($mockGraphClient, $mockLogger);
    $result = $resolver->resolve(['0', '1', '2'], $tenant);

    expect($result)->toBe([
        ['id' => '0', 'displayName' => 'Default'],
        ['id' => '1', 'displayName' => 'Verbund-1'],
        ['id' => '2', 'displayName' => 'Verbund-2'],
    ]);
});

test('caches scope tag objects for 1 hour', function () {
    $tenant = Tenant::factory()->create();

    $mockGraphClient = Mockery::mock(MicrosoftGraphClient::class);
    $mockGraphClient->shouldReceive('request')
        ->once() // Only called once due to caching
        ->andReturn(new GraphResponse(
            success: true,
            data: [
                'value' => [
                    ['id' => '0', 'displayName' => 'Default'],
                ],
            ]
        ));

    $mockLogger = Mockery::mock(GraphLogger::class);

    $resolver = new ScopeTagResolver($mockGraphClient, $mockLogger);

    // First call - fetches from API
    $result1 = $resolver->resolve(['0'], $tenant);

    // Second call - should use cache
    $result2 = $resolver->resolve(['0'], $tenant);

    expect($result1)->toBe([['id' => '0', 'displayName' => 'Default']]);
    expect($result2)->toBe([['id' => '0', 'displayName' => 'Default']]);
});

test('returns empty array for empty input', function () {
    $tenant = Tenant::factory()->create();
    $mockGraphClient = Mockery::mock(MicrosoftGraphClient::class);
    $mockLogger = Mockery::mock(GraphLogger::class);

    $resolver = new ScopeTagResolver($mockGraphClient, $mockLogger);
    $result = $resolver->resolve([], $tenant);

    expect($result)->toBe([]);
});

test('handles 403 forbidden gracefully', function () {
    $tenant = Tenant::factory()->create();

    $mockGraphClient = Mockery::mock(MicrosoftGraphClient::class);
    $mockGraphClient->shouldReceive('request')
        ->once()
        ->andReturn(new GraphResponse(
            success: false,
            status: 403,
            data: []
        ));

    $mockLogger = Mockery::mock(GraphLogger::class);

    $resolver = new ScopeTagResolver($mockGraphClient, $mockLogger);
    $result = $resolver->resolve(['0', '1'], $tenant);

    // Should return empty array when 403
    expect($result)->toBe([]);
});

test('filters returned scope tags to requested IDs', function () {
    $tenant = Tenant::factory()->create();

    $mockGraphClient = Mockery::mock(MicrosoftGraphClient::class);
    $mockGraphClient->shouldReceive('request')
        ->once()
        ->andReturn(new GraphResponse(
            success: true,
            data: [
                'value' => [
                    ['id' => '0', 'displayName' => 'Default'],
                    ['id' => '1', 'displayName' => 'Verbund-1'],
                    ['id' => '2', 'displayName' => 'Verbund-2'],
                ],
            ]
        ));

    $mockLogger = Mockery::mock(GraphLogger::class);

    $resolver = new ScopeTagResolver($mockGraphClient, $mockLogger);
    // Request only IDs 0 and 2
    $result = $resolver->resolve(['0', '2'], $tenant);

    expect($result)->toHaveCount(2);
    // Note: array_filter preserves keys, so result will be [0 => ..., 2 => ...]
    expect($result[0])->toBe(['id' => '0', 'displayName' => 'Default']);
    expect($result[2])->toBe(['id' => '2', 'displayName' => 'Verbund-2']);
});
