<?php

use App\Models\ProviderConnection;
use App\Models\ProviderCredential;
use App\Models\ManagedEnvironment;
use App\Services\Graph\GraphClientInterface;
use App\Services\Graph\GraphResponse;
use App\Services\Graph\ScopeTagResolver;
use App\Services\Providers\ProviderConnectionResolver;
use App\Services\Providers\ProviderGateway;
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 = ManagedEnvironment::factory()->create();
    $connection = ProviderConnection::factory()->dedicated()->consentGranted()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'provider' => 'microsoft',
        'is_default' => true,
        'consent_status' => 'granted',
    ]);
    ProviderCredential::factory()->create([
        'provider_connection_id' => (int) $connection->getKey(),
    ]);

    $graph = Mockery::mock(GraphClientInterface::class);
    $graph->shouldReceive('request')
        ->with(
            'GET',
            '/deviceManagement/roleScopeTags',
            Mockery::on(function (array $options) use ($connection): bool {
                return ($options['query']['$select'] ?? null) === 'id,displayName'
                    && ($options['tenant'] ?? null) === $connection->entra_tenant_id
                    && is_string($options['client_id'] ?? null)
                    && is_string($options['client_secret'] ?? null);
            }),
        )
        ->once()
        ->andReturn(new GraphResponse(
            success: true,
            data: [
                'value' => [
                    ['id' => '0', 'displayName' => 'Default'],
                    ['id' => '1', 'displayName' => 'Verbund-1'],
                    ['id' => '2', 'displayName' => 'Verbund-2'],
                ],
            ],
        ));

    $gateway = app()->make(ProviderGateway::class, ['graph' => $graph]);

    $resolver = new ScopeTagResolver(app(ProviderConnectionResolver::class), $gateway);
    $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 = ManagedEnvironment::factory()->create();
    $connection = ProviderConnection::factory()->dedicated()->consentGranted()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'provider' => 'microsoft',
        'is_default' => true,
        'consent_status' => 'granted',
    ]);
    ProviderCredential::factory()->create([
        'provider_connection_id' => (int) $connection->getKey(),
    ]);

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

    $gateway = app()->make(ProviderGateway::class, ['graph' => $graph]);
    $resolver = new ScopeTagResolver(app(ProviderConnectionResolver::class), $gateway);

    $result1 = $resolver->resolve(['0'], $tenant);
    $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 = ManagedEnvironment::factory()->create();
    $graph = Mockery::mock(GraphClientInterface::class);

    $gateway = app()->make(ProviderGateway::class, ['graph' => $graph]);

    $resolver = new ScopeTagResolver(app(ProviderConnectionResolver::class), $gateway);
    $result = $resolver->resolve([], $tenant);

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

test('handles 403 forbidden gracefully', function () {
    $tenant = ManagedEnvironment::factory()->create();
    $connection = ProviderConnection::factory()->dedicated()->consentGranted()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'provider' => 'microsoft',
        'is_default' => true,
        'consent_status' => 'granted',
    ]);
    ProviderCredential::factory()->create([
        'provider_connection_id' => (int) $connection->getKey(),
    ]);

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

    $gateway = app()->make(ProviderGateway::class, ['graph' => $graph]);

    $resolver = new ScopeTagResolver(app(ProviderConnectionResolver::class), $gateway);
    $result = $resolver->resolve(['0', '1'], $tenant);

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

test('filters returned scope tags to requested IDs', function () {
    $tenant = ManagedEnvironment::factory()->create();
    $connection = ProviderConnection::factory()->dedicated()->consentGranted()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'provider' => 'microsoft',
        'is_default' => true,
        'consent_status' => 'granted',
    ]);
    ProviderCredential::factory()->create([
        'provider_connection_id' => (int) $connection->getKey(),
    ]);

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

    $gateway = app()->make(ProviderGateway::class, ['graph' => $graph]);

    $resolver = new ScopeTagResolver(app(ProviderConnectionResolver::class), $gateway);
    $result = $resolver->resolve(['0', '2'], $tenant);

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