<?php

declare(strict_types=1);

use App\Filament\Pages\EnvironmentDashboard;
use App\Models\SupportRequest;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Support\Workspaces\WorkspaceContext;
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Http;
use Livewire\Livewire;

uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);

function spec256ConfigureTenantSupportDesk(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 spec256TenantHandoffComponent(User $user, ManagedEnvironment $tenant): \Livewire\Features\SupportTesting\Testable
{
    test()->actingAs($user);
    session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
    setAdminEnvironmentContext($tenant);

    return Livewire::actingAs($user)->test(EnvironmentDashboard::class);
}

it('creates an external ticket from the tenant dashboard support action', function (): void {
    spec256ConfigureTenantSupportDesk();

    $tenant = ManagedEnvironment::factory()->create(['name' => 'Spec 256 ManagedEnvironment']);
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');

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

    spec256TenantHandoffComponent($user, $tenant)
        ->mountAction('requestSupport')
        ->setActionData([
            'severity' => SupportRequest::SEVERITY_HIGH,
            'summary' => 'ManagedEnvironment create external ticket handoff.',
            'external_handoff_mode' => SupportRequest::EXTERNAL_HANDOFF_MODE_CREATE_EXTERNAL_TICKET,
        ])
        ->callMountedAction()
        ->assertHasNoActionErrors()
        ->assertNotified('Support request submitted');

    $supportRequest = SupportRequest::query()->sole();

    expect($supportRequest->internal_reference)->toMatch('/^SR-[0-9A-HJKMNP-TV-Z]{26}$/')
        ->and($supportRequest->external_handoff_mode)->toBe(SupportRequest::EXTERNAL_HANDOFF_MODE_CREATE_EXTERNAL_TICKET)
        ->and($supportRequest->external_ticket_reference)->toBe('PSA-2561')
        ->and($supportRequest->external_ticket_url)->toBe('https://desk.example.test/tickets/PSA-2561')
        ->and($supportRequest->external_handoff_failure_summary)->toBeNull()
        ->and($supportRequest->externalHandoffOutcome())->toBe(SupportRequest::HANDOFF_OUTCOME_EXTERNAL_TICKET_CREATED);

    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);
});

it('links an existing external ticket from the tenant dashboard without creating a duplicate external ticket', function (): void {
    spec256ConfigureTenantSupportDesk();

    $tenant = ManagedEnvironment::factory()->create();
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'operator');

    Http::fake();

    spec256TenantHandoffComponent($user, $tenant)
        ->mountAction('requestSupport')
        ->setActionData([
            'severity' => SupportRequest::SEVERITY_NORMAL,
            'summary' => 'ManagedEnvironment link existing external ticket.',
            'external_handoff_mode' => SupportRequest::EXTERNAL_HANDOFF_MODE_LINK_EXISTING_TICKET,
            'external_ticket_reference' => 'PSA-256-LINK',
            'external_ticket_url' => 'https://desk.example.test/tickets/PSA-256-LINK',
        ])
        ->callMountedAction()
        ->assertHasNoActionErrors()
        ->assertNotified('Support request submitted');

    $supportRequest = SupportRequest::query()->sole();

    expect($supportRequest->external_handoff_mode)->toBe(SupportRequest::EXTERNAL_HANDOFF_MODE_LINK_EXISTING_TICKET)
        ->and($supportRequest->external_ticket_reference)->toBe('PSA-256-LINK')
        ->and($supportRequest->external_ticket_url)->toBe('https://desk.example.test/tickets/PSA-256-LINK')
        ->and($supportRequest->externalHandoffOutcome())->toBe(SupportRequest::HANDOFF_OUTCOME_EXTERNAL_TICKET_LINKED);

    Http::assertNothingSent();
});

it('rejects invalid linked external ticket input before storing a tenant support request', function (): void {
    spec256ConfigureTenantSupportDesk();

    $tenant = ManagedEnvironment::factory()->create();
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'operator');

    Http::fake();

    spec256TenantHandoffComponent($user, $tenant)
        ->mountAction('requestSupport')
        ->setActionData([
            'severity' => SupportRequest::SEVERITY_NORMAL,
            'summary' => 'ManagedEnvironment invalid link should not create support truth.',
            'external_handoff_mode' => SupportRequest::EXTERNAL_HANDOFF_MODE_LINK_EXISTING_TICKET,
            'external_ticket_reference' => 'not a ticket',
        ])
        ->callMountedAction()
        ->assertHasErrors(['external_ticket_reference']);

    expect(SupportRequest::query()->count())->toBe(0);

    Http::assertNothingSent();
});

it('keeps the internal tenant support request when external create fails', function (): void {
    spec256ConfigureTenantSupportDesk();

    $tenant = ManagedEnvironment::factory()->create();
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'manager');

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

    spec256TenantHandoffComponent($user, $tenant)
        ->mountAction('requestSupport')
        ->setActionData([
            'severity' => SupportRequest::SEVERITY_BLOCKING,
            'summary' => 'ManagedEnvironment external desk timeout should keep internal support request.',
            'external_handoff_mode' => SupportRequest::EXTERNAL_HANDOFF_MODE_CREATE_EXTERNAL_TICKET,
        ])
        ->callMountedAction()
        ->assertHasNoActionErrors()
        ->assertNotified('Support request submitted');

    $supportRequest = SupportRequest::query()->sole();

    expect($supportRequest->internal_reference)->toMatch('/^SR-/')
        ->and($supportRequest->external_handoff_mode)->toBe(SupportRequest::EXTERNAL_HANDOFF_MODE_CREATE_EXTERNAL_TICKET)
        ->and($supportRequest->external_ticket_reference)->toBeNull()
        ->and($supportRequest->external_ticket_url)->toBeNull()
        ->and($supportRequest->external_handoff_failure_summary)->toContain('configured timeout')
        ->and($supportRequest->externalHandoffOutcome())->toBe(SupportRequest::HANDOFF_OUTCOME_EXTERNAL_HANDOFF_FAILED);
});

it('forces tenant support requests to internal only when no external target is configured', function (): void {
    spec256ConfigureTenantSupportDesk([
        'enabled' => false,
    ]);

    $tenant = ManagedEnvironment::factory()->create();
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');

    Http::fake();

    spec256TenantHandoffComponent($user, $tenant)
        ->mountAction('requestSupport')
        ->setActionData([
            'summary' => 'ManagedEnvironment support stays internal when no support desk target exists.',
        ])
        ->callMountedAction()
        ->assertHasNoActionErrors();

    $supportRequest = SupportRequest::query()->sole();

    expect($supportRequest->external_handoff_mode)->toBe(SupportRequest::EXTERNAL_HANDOFF_MODE_INTERNAL_ONLY)
        ->and($supportRequest->external_ticket_reference)->toBeNull()
        ->and($supportRequest->external_handoff_failure_summary)->toBeNull();

    Http::assertNothingSent();
});
