<?php

declare(strict_types=1);

use App\Models\AuditLog;
use App\Models\PlatformUser;
use App\Models\SupportAccessGrant;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Models\Workspace;
use App\Models\WorkspaceMembership;
use App\Services\Auth\BreakGlassSession;
use App\Services\Auth\SupportAccessGrantManager;
use App\Services\Auth\SupportAccessGrantResolver;
use App\Support\Audit\AuditActionId;
use App\Support\Auth\PlatformCapabilities;
use App\Support\Auth\WorkspaceRole;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Validation\ValidationException;

uses(RefreshDatabase::class);

beforeEach(function (): void {
    ManagedEnvironment::factory()->create([
        'managed_environment_id' => null,
        'external_id' => 'platform',
        'name' => 'Platform',
    ]);

    config()->set('tenantpilot.break_glass.enabled', true);
    config()->set('tenantpilot.break_glass.ttl_minutes', 15);
    config()->set('tenantpilot.support_access.max_ttl_minutes', 120);
});

function spec276_resolver_platform_user(): PlatformUser
{
    return PlatformUser::factory()->create([
        'capabilities' => [
            PlatformCapabilities::ACCESS_SYSTEM_PANEL,
            PlatformCapabilities::DIRECTORY_VIEW,
            PlatformCapabilities::SUPPORT_ACCESS_MANAGE,
            PlatformCapabilities::USE_BREAK_GLASS,
        ],
    ]);
}

it('auto activates audit-view support access and dedupes overlapping requests from the same platform actor', function (): void {
    $workspace = Workspace::factory()->create();
    $platformUser = spec276_resolver_platform_user();

    $grant = app(SupportAccessGrantManager::class)->request(
        workspace: $workspace,
        actor: $platformUser,
        scope: SupportAccessGrant::SCOPE_AUDIT_VIEW,
        reason: 'Review support case audit history',
        ttlMinutes: 45,
    );

    $duplicate = app(SupportAccessGrantManager::class)->request(
        workspace: $workspace,
        actor: $platformUser,
        scope: SupportAccessGrant::SCOPE_AUDIT_VIEW,
        reason: 'Review support case audit history again',
        ttlMinutes: 60,
    );

    expect($grant->status)->toBe(SupportAccessGrant::STATUS_ACTIVE)
        ->and($grant->approval_mode)->toBe(SupportAccessGrant::APPROVAL_MODE_AUTO)
        ->and($grant->starts_at)->not->toBeNull()
        ->and($grant->expires_at)->not->toBeNull()
        ->and($duplicate->getKey())->toBe($grant->getKey())
        ->and(SupportAccessGrant::query()->count())->toBe(1);

    expect(AuditLog::query()
        ->where('workspace_id', (int) $workspace->getKey())
        ->whereIn('action', [
            AuditActionId::SupportAccessRequested->value,
            AuditActionId::SupportAccessActivated->value,
        ])
        ->count())->toBe(2);
});

it('keeps workspace-recovery support access pending until a workspace owner approves it', function (): void {
    $workspace = Workspace::factory()->create();
    $platformUser = spec276_resolver_platform_user();
    $owner = User::factory()->create();

    WorkspaceMembership::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'user_id' => (int) $owner->getKey(),
        'role' => WorkspaceRole::Owner->value,
    ]);

    $grant = app(SupportAccessGrantManager::class)->request(
        workspace: $workspace,
        actor: $platformUser,
        scope: SupportAccessGrant::SCOPE_WORKSPACE_RECOVERY,
        reason: 'Recover workspace ownership safely',
        ttlMinutes: 30,
    );

    expect($grant->status)->toBe(SupportAccessGrant::STATUS_REQUESTED)
        ->and($grant->approval_mode)->toBe(SupportAccessGrant::APPROVAL_MODE_OWNER_REQUIRED)
        ->and($grant->starts_at)->toBeNull()
        ->and($grant->expires_at)->toBeNull();

    $approved = app(SupportAccessGrantManager::class)->approve($grant, $owner);

    expect($approved->status)->toBe(SupportAccessGrant::STATUS_ACTIVE)
        ->and($approved->approved_by_user_id)->toBe((int) $owner->getKey())
        ->and($approved->expires_at)->not->toBeNull()
        ->and(app(SupportAccessGrantResolver::class)->activeRecoveryGrantFor($workspace)?->getKey())->toBe($grant->getKey());
});

it('expires stale active grants before returning active support access', function (): void {
    $workspace = Workspace::factory()->create();
    $grant = SupportAccessGrant::factory()->expired()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'scope' => SupportAccessGrant::SCOPE_WORKSPACE_RECOVERY,
    ]);

    expect(app(SupportAccessGrantResolver::class)->activeRecoveryGrantFor($workspace))->toBeNull()
        ->and($grant->fresh()->status)->toBe(SupportAccessGrant::STATUS_EXPIRED);

    expect(AuditLog::query()
        ->where('workspace_id', (int) $workspace->getKey())
        ->where('action', AuditActionId::SupportAccessExpired->value)
        ->exists())->toBeTrue();
});

it('requires break-glass and a waiver reason for ownerless workspace recovery', function (): void {
    $workspace = Workspace::factory()->create();
    $platformUser = spec276_resolver_platform_user();

    expect(fn () => app(SupportAccessGrantManager::class)->request(
        workspace: $workspace,
        actor: $platformUser,
        scope: SupportAccessGrant::SCOPE_WORKSPACE_RECOVERY,
        reason: 'Recover ownerless workspace',
        ttlMinutes: 30,
        waiverReason: 'No owners exist',
    ))->toThrow(ValidationException::class);

    $this->actingAs($platformUser, 'platform');
    app(BreakGlassSession::class)->start($platformUser, 'Recover ownerless workspace');

    $grant = app(SupportAccessGrantManager::class)->request(
        workspace: $workspace,
        actor: $platformUser,
        scope: SupportAccessGrant::SCOPE_WORKSPACE_RECOVERY,
        reason: 'Recover ownerless workspace',
        ttlMinutes: 30,
        waiverReason: 'Validated no workspace owners remain',
    );

    expect($grant->status)->toBe(SupportAccessGrant::STATUS_ACTIVE)
        ->and($grant->approval_mode)->toBe(SupportAccessGrant::APPROVAL_MODE_OWNERLESS_WAIVER)
        ->and($grant->waiver_reason)->toBe('Validated no workspace owners remain');

    expect(AuditLog::query()
        ->where('workspace_id', (int) $workspace->getKey())
        ->where('action', AuditActionId::SupportAccessOwnerlessWaiverUsed->value)
        ->whereJsonContains('metadata->waiver_reason', 'Validated no workspace owners remain')
        ->exists())->toBeTrue();
});
