<?php

declare(strict_types=1);

use App\Exceptions\Entitlements\WorkspaceEntitlementBlockedException;
use App\Filament\Resources\ReviewPackResource;
use App\Filament\Widgets\ManagedEnvironment\ManagedEnvironmentReviewPackCard;
use App\Models\EvidenceSnapshot;
use App\Models\Finding;
use App\Models\OperationRun;
use App\Models\PlatformUser;
use App\Models\ReviewPack;
use App\Models\StoredReport;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Services\Entitlements\WorkspaceCommercialLifecycleResolver;
use App\Services\Evidence\EvidenceSnapshotService;
use App\Services\ReviewPackService;
use App\Services\Settings\SettingsWriter;
use App\Support\Auth\PlatformCapabilities;
use App\Support\Evidence\EvidenceSnapshotStatus;
use App\Support\OperationRunType;
use App\Support\Workspaces\WorkspaceContext;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Storage;
use Livewire\Livewire;

beforeEach(function (): void {
    Storage::fake('exports');
});

function seedEntitlementReviewPackSnapshot(ManagedEnvironment $tenant): EvidenceSnapshot
{
    StoredReport::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'report_type' => StoredReport::REPORT_TYPE_PERMISSION_POSTURE,
        'payload' => ['required_count' => 1, 'granted_count' => 1],
    ]);

    StoredReport::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'report_type' => StoredReport::REPORT_TYPE_ENTRA_ADMIN_ROLES,
        'payload' => ['roles' => [['displayName' => 'Global Administrator']]],
    ]);

    Finding::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
    ]);

    Finding::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'finding_type' => Finding::FINDING_TYPE_DRIFT,
    ]);

    OperationRun::factory()->forTenant($tenant)->create([
        'type' => OperationRunType::PolicySync->value,
    ]);

    /** @var EvidenceSnapshotService $service */
    $service = app(EvidenceSnapshotService::class);
    $payload = $service->buildSnapshotPayload($tenant);

    $snapshot = EvidenceSnapshot::query()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'status' => EvidenceSnapshotStatus::Active->value,
        'fingerprint' => $payload['fingerprint'],
        'completeness_state' => $payload['completeness'],
        'summary' => $payload['summary'],
        'generated_at' => now(),
    ]);

    foreach ($payload['items'] as $item) {
        $snapshot->items()->create([
            'managed_environment_id' => (int) $tenant->getKey(),
            'workspace_id' => (int) $tenant->workspace_id,
            'dimension_key' => $item['dimension_key'],
            'state' => $item['state'],
            'required' => $item['required'],
            'source_kind' => $item['source_kind'],
            'source_record_type' => $item['source_record_type'],
            'source_record_id' => $item['source_record_id'],
            'source_fingerprint' => $item['source_fingerprint'],
            'measured_at' => $item['measured_at'],
            'freshness_at' => $item['freshness_at'],
            'summary_payload' => $item['summary_payload'],
            'sort_order' => $item['sort_order'],
        ]);
    }

    return $snapshot;
}

function disableReviewPackGenerationForWorkspace(ManagedEnvironment $tenant, User $user, string $reason): void
{
    $writer = app(SettingsWriter::class);

    $writer->updateWorkspaceSetting(
        actor: $user,
        workspace: $tenant->workspace,
        domain: 'entitlements',
        key: 'review_pack_generation_override_value',
        value: false,
    );

    $writer->updateWorkspaceSetting(
        actor: $user,
        workspace: $tenant->workspace,
        domain: 'entitlements',
        key: 'review_pack_generation_override_reason',
        value: $reason,
    );
}

function setReviewPackCommercialLifecycleState(ManagedEnvironment $tenant, string $state, string $reason = 'Review pack commercial lifecycle test'): void
{
    app(SettingsWriter::class)->updateWorkspaceCommercialLifecycle(
        actor: PlatformUser::factory()->create([
            'capabilities' => [
                PlatformCapabilities::ACCESS_SYSTEM_PANEL,
                PlatformCapabilities::DIRECTORY_VIEW,
                PlatformCapabilities::COMMERCIAL_LIFECYCLE_MANAGE,
            ],
            'is_active' => true,
        ]),
        workspace: $tenant->workspace,
        state: $state,
        reason: $reason,
    );
}

function setReviewPackSubscriptionState(ManagedEnvironment $tenant, array $attributes): void
{
    app(SettingsWriter::class)->updateWorkspaceSubscription(
        actor: PlatformUser::factory()->create([
            'capabilities' => [
                PlatformCapabilities::ACCESS_SYSTEM_PANEL,
                PlatformCapabilities::DIRECTORY_VIEW,
                PlatformCapabilities::COMMERCIAL_LIFECYCLE_MANAGE,
            ],
            'is_active' => true,
        ]),
        workspace: $tenant->workspace,
        attributes: $attributes,
    );
}

it('blocks new review pack generation before creating a review pack or operation run when the workspace is not entitled', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');
    seedEntitlementReviewPackSnapshot($tenant);
    disableReviewPackGenerationForWorkspace($tenant, $user, 'Workspace is temporarily limited to manual reporting only');
    $initialRunCount = OperationRun::query()
        ->where('managed_environment_id', (int) $tenant->getKey())
        ->where('type', OperationRunType::ReviewPackGenerate->value)
        ->count();

    expect(fn (): ReviewPack => app(ReviewPackService::class)->generate($tenant, $user))
        ->toThrow(WorkspaceEntitlementBlockedException::class, 'Workspace is temporarily limited to manual reporting only');

    expect(ReviewPack::query()->count())->toBe(0)
        ->and(OperationRun::query()
            ->where('managed_environment_id', (int) $tenant->getKey())
            ->where('type', OperationRunType::ReviewPackGenerate->value)
            ->count())->toBe($initialRunCount);
});

it('blocks executive pack export before creating a review pack or operation run when the workspace is not entitled', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');
    $snapshot = seedEntitlementReviewPackSnapshot($tenant);
    $review = composeEnvironmentReviewForTest($tenant, $user, $snapshot);
    disableReviewPackGenerationForWorkspace($tenant, $user, 'Workspace is temporarily limited to manual reporting only');
    $initialRunCount = OperationRun::query()
        ->where('managed_environment_id', (int) $tenant->getKey())
        ->where('type', OperationRunType::ReviewPackGenerate->value)
        ->count();

    expect(fn (): ReviewPack => app(ReviewPackService::class)->generateFromReview($review, $user))
        ->toThrow(WorkspaceEntitlementBlockedException::class, 'Workspace is temporarily limited to manual reporting only');

    expect(ReviewPack::query()->count())->toBe(0)
        ->and(OperationRun::query()
            ->where('managed_environment_id', (int) $tenant->getKey())
            ->where('type', OperationRunType::ReviewPackGenerate->value)
            ->count())->toBe($initialRunCount);
});

it('shows the blocked reason on the review pack card and keeps existing pack downloads accessible', function (): void {
    $tenant = ManagedEnvironment::factory()->create();
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
    disableReviewPackGenerationForWorkspace($tenant, $user, 'Workspace is temporarily limited to manual reporting only');
    $initialRunCount = OperationRun::query()
        ->where('managed_environment_id', (int) $tenant->getKey())
        ->where('type', OperationRunType::ReviewPackGenerate->value)
        ->count();

    session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
    setAdminEnvironmentContext($tenant);

    Livewire::actingAs($user)
        ->test(ManagedEnvironmentReviewPackCard::class, ['record' => $tenant])
        ->assertSee('Workspace is temporarily limited to manual reporting only')
        ->assertSee('Generate pack')
        ->call('generatePack', true, true)
        ->assertHasNoErrors();

    expect(ReviewPack::query()->count())->toBe(0)
        ->and(OperationRun::query()
            ->where('managed_environment_id', (int) $tenant->getKey())
            ->where('type', OperationRunType::ReviewPackGenerate->value)
            ->count())->toBe($initialRunCount);

    $filePath = 'review-packs/entitlement-download-test.zip';
    Storage::disk('exports')->put($filePath, 'PK-test');

    $pack = ReviewPack::factory()->ready()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'initiated_by_user_id' => (int) $user->getKey(),
        'file_path' => $filePath,
        'file_disk' => 'exports',
    ]);

    $this->actingAs($user)
        ->get(ReviewPackResource::getUrl('view', ['record' => $pack], tenant: $tenant, panel: 'admin'))
        ->assertOk()
        ->assertSee('Download');
});

it('allows review pack generation in trial and active paid states', function (string $state): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');
    seedEntitlementReviewPackSnapshot($tenant);
    setReviewPackCommercialLifecycleState($tenant, $state);

    $pack = app(ReviewPackService::class)->generate($tenant, $user);

    expect($pack)->toBeInstanceOf(ReviewPack::class)
        ->and($pack->operation_run_id)->not->toBeNull()
        ->and($pack->status)->toBe(\App\Support\ReviewPackStatus::Queued->value);
})->with([
    'trial' => [WorkspaceCommercialLifecycleResolver::STATE_TRIAL],
    'active paid' => [WorkspaceCommercialLifecycleResolver::STATE_ACTIVE_PAID],
]);

it('warns but allows review pack generation in grace', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');
    seedEntitlementReviewPackSnapshot($tenant);
    setReviewPackCommercialLifecycleState($tenant, WorkspaceCommercialLifecycleResolver::STATE_GRACE, 'Grace period');

    $decision = app(ReviewPackService::class)->reviewPackGenerationDecisionForTenant($tenant);

    expect($decision)
        ->toMatchArray([
            'is_blocked' => false,
            'is_warning' => true,
            'outcome' => WorkspaceCommercialLifecycleResolver::OUTCOME_WARN,
        ])
        ->and($decision['warning_reason'])->toContain('grace');

    session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
    setAdminEnvironmentContext($tenant);

    Livewire::actingAs($user)
        ->test(ManagedEnvironmentReviewPackCard::class, ['record' => $tenant])
        ->assertSee('Workspace is in grace. Review-pack starts remain available')
        ->assertSee('Commercial source: fallback-backed.');

    $pack = app(ReviewPackService::class)->generate($tenant, $user);

    expect($pack)->toBeInstanceOf(ReviewPack::class)
        ->and(OperationRun::query()
            ->where('managed_environment_id', (int) $tenant->getKey())
            ->where('type', OperationRunType::ReviewPackGenerate->value)
            ->exists())->toBeTrue();
});

it('labels subscription-backed review pack warnings when subscription truth drives grace behavior', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');
    seedEntitlementReviewPackSnapshot($tenant);
    setReviewPackSubscriptionState($tenant, [
        'state' => 'past_due',
        'billing_reference' => 'sub_past_due_001',
        'current_period_starts_at' => now()->subDays(15)->startOfMinute()->toDateTimeString(),
        'current_period_ends_at' => now()->addDays(15)->startOfMinute()->toDateTimeString(),
        'status_reason' => 'Payment collection is pending.',
    ]);

    $decision = app(ReviewPackService::class)->reviewPackGenerationDecisionForTenant($tenant);

    expect($decision)
        ->toMatchArray([
            'is_blocked' => false,
            'is_warning' => true,
            'outcome' => WorkspaceCommercialLifecycleResolver::OUTCOME_WARN,
            'source' => WorkspaceCommercialLifecycleResolver::SOURCE_WORKSPACE_SUBSCRIPTION,
        ])
        ->and($decision['warning_reason'])->toContain('Commercial source: subscription-backed.');

    session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
    setAdminEnvironmentContext($tenant);

    Livewire::actingAs($user)
        ->test(ManagedEnvironmentReviewPackCard::class, ['record' => $tenant])
        ->assertSee('Commercial source: subscription-backed.');
});

it('blocks suspended read-only review pack generation before creating a review pack or operation run and sends no run notifications', function (): void {
    Notification::fake();

    [$user, $tenant] = createUserWithTenant(role: 'owner');
    seedEntitlementReviewPackSnapshot($tenant);
    setReviewPackCommercialLifecycleState($tenant, WorkspaceCommercialLifecycleResolver::STATE_SUSPENDED_READ_ONLY, 'Suspension');
    $initialRunCount = OperationRun::query()
        ->where('managed_environment_id', (int) $tenant->getKey())
        ->where('type', OperationRunType::ReviewPackGenerate->value)
        ->count();

    expect(fn (): ReviewPack => app(ReviewPackService::class)->generate($tenant, $user))
        ->toThrow(WorkspaceEntitlementBlockedException::class, 'suspended / read-only');

    expect(ReviewPack::query()->count())->toBe(0)
        ->and(OperationRun::query()
            ->where('managed_environment_id', (int) $tenant->getKey())
            ->where('type', OperationRunType::ReviewPackGenerate->value)
            ->count())->toBe($initialRunCount);

    Notification::assertNothingSent();
});

it('does not alter already queued review-pack work when a workspace is suspended later', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');
    seedEntitlementReviewPackSnapshot($tenant);

    $pack = app(ReviewPackService::class)->generate($tenant, $user);
    $initialStatus = (string) $pack->fresh()?->status;
    setReviewPackCommercialLifecycleState($tenant, WorkspaceCommercialLifecycleResolver::STATE_SUSPENDED_READ_ONLY, 'Later suspension');

    expect($pack->fresh()?->status)->toBe($initialStatus)
        ->and(OperationRun::query()
            ->whereKey((int) $pack->operation_run_id)
            ->exists())->toBeTrue();
});
