<?php

declare(strict_types=1);

use App\Models\SupportRequest;
use App\Models\ManagedEnvironment;
use App\Support\SupportRequests\ExternalSupportDeskHandoffService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Validation\ValidationException;

uses(RefreshDatabase::class);

function configureSpec256SupportDesk(array $overrides = []): void
{
    config([
        'support_desk.target' => array_merge([
            'enabled' => true,
            'name' => 'Spec 256 Desk',
            'create_url' => 'https://desk.example.test/api/tickets',
            'api_token' => null,
            'ticket_url_template' => 'https://desk.example.test/tickets/{reference}',
            'timeout_seconds' => 5,
        ], $overrides),
    ]);
}

function spec256SupportRequest(array $attributes = []): SupportRequest
{
    $tenant = ManagedEnvironment::factory()->create();

    return SupportRequest::factory()->create(array_merge([
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'summary' => 'Need external support desk handoff.',
        'severity' => SupportRequest::SEVERITY_HIGH,
    ], $attributes));
}

it('creates an external ticket through the configured target and normalizes the returned reference', function (): void {
    configureSpec256SupportDesk([
        'api_token' => 'secret-token',
    ]);

    $supportRequest = spec256SupportRequest();

    Http::fake([
        'desk.example.test/*' => Http::response([
            'ticket_reference' => 'PSA-12345',
            'ticket_url' => 'https://desk.example.test/tickets/PSA-12345',
        ], 201),
    ]);

    $result = app(ExternalSupportDeskHandoffService::class)->createTicket($supportRequest);

    expect($result['successful'])->toBeTrue()
        ->and($result['external_ticket_reference'])->toBe('PSA-12345')
        ->and($result['external_ticket_url'])->toBe('https://desk.example.test/tickets/PSA-12345')
        ->and($result['failure_summary'])->toBeNull();

    Http::assertSent(fn (Request $request): bool => $request->url() === 'https://desk.example.test/api/tickets'
        && data_get($request->data(), 'support_request.internal_reference') === $supportRequest->internal_reference
        && data_get($request->data(), 'support_request.summary') === 'Need external support desk handoff.');
});

it('enforces the five second timeout budget and normalizes connection failures', function (): void {
    configureSpec256SupportDesk([
        'timeout_seconds' => 30,
    ]);

    $supportRequest = spec256SupportRequest();

    Http::fake([
        'desk.example.test/*' => Http::failedConnection(),
    ]);

    $service = app(ExternalSupportDeskHandoffService::class);
    $result = $service->createTicket($supportRequest);

    expect($service->timeoutSeconds())->toBe(5)
        ->and($result['successful'])->toBeFalse()
        ->and($result['external_ticket_reference'])->toBeNull()
        ->and($result['failure_summary'])->toContain('configured timeout');
});

it('falls back to unavailable when the single configured target is disabled', function (): void {
    configureSpec256SupportDesk([
        'enabled' => false,
    ]);

    Http::fake();

    $service = app(ExternalSupportDeskHandoffService::class);
    $result = $service->createTicket(spec256SupportRequest());

    expect($service->targetIsConfigured())->toBeFalse()
        ->and($result['successful'])->toBeFalse()
        ->and($result['failure_summary'])->toBe('External support desk target is not configured.');

    Http::assertNothingSent();
});

it('normalizes linked tickets without issuing an outbound create call', function (): void {
    configureSpec256SupportDesk();
    Http::fake();

    $result = app(ExternalSupportDeskHandoffService::class)->normalizeLinkedTicket('  PSA-900  ', null);

    expect($result['external_ticket_reference'])->toBe('PSA-900')
        ->and($result['external_ticket_url'])->toBe('https://desk.example.test/tickets/PSA-900');

    Http::assertNothingSent();
});

it('rejects invalid linked ticket input before storing external truth', function (): void {
    configureSpec256SupportDesk();

    expect(fn (): array => app(ExternalSupportDeskHandoffService::class)->normalizeLinkedTicket('not a ticket', 'javascript:alert(1)'))
        ->toThrow(ValidationException::class);
});
