<?php

declare(strict_types=1);

use App\Models\PlatformUser;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Models\Workspace;
use App\Models\WorkspaceMembership;
use App\Models\WorkspaceSubscription;
use App\Services\Entitlements\WorkspaceCommercialLifecycleResolver;
use App\Services\Entitlements\WorkspaceEntitlementResolver;
use App\Services\Settings\SettingsWriter;
use App\Support\Auth\PlatformCapabilities;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

/**
 * @return array{0: Workspace, 1: User}
 */
function commercialLifecycleWorkspaceManager(): array
{
    $workspace = Workspace::factory()->create();
    $user = User::factory()->create();

    WorkspaceMembership::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'user_id' => (int) $user->getKey(),
        'role' => 'manager',
    ]);

    return [$workspace, $user];
}

function commercialLifecyclePlatformOperator(): PlatformUser
{
    return PlatformUser::factory()->create([
        'capabilities' => [
            PlatformCapabilities::ACCESS_SYSTEM_PANEL,
            PlatformCapabilities::DIRECTORY_VIEW,
            PlatformCapabilities::COMMERCIAL_LIFECYCLE_MANAGE,
        ],
        'is_active' => true,
    ]);
}

function setCommercialLifecycleState(Workspace $workspace, string $state, string $reason = 'Unit test commercial state change'): void
{
    app(SettingsWriter::class)->updateWorkspaceCommercialLifecycle(
        actor: commercialLifecyclePlatformOperator(),
        workspace: $workspace,
        state: $state,
        reason: $reason,
    );
}

it('falls back to active paid when no explicit commercial lifecycle setting exists', function (): void {
    [$workspace] = commercialLifecycleWorkspaceManager();

    $summary = app(WorkspaceCommercialLifecycleResolver::class)->summary($workspace);

    expect($summary)
        ->toMatchArray([
            'state' => WorkspaceCommercialLifecycleResolver::STATE_ACTIVE_PAID,
            'state_label' => 'Active paid',
            'source' => WorkspaceCommercialLifecycleResolver::SOURCE_DEFAULT_ACTIVE_PAID,
            'source_label' => 'default active paid',
            'rationale' => null,
        ])
        ->and($summary['last_changed_at'])->toBeNull()
        ->and($summary['last_changed_by'])->toBeNull()
        ->and($summary['action_decisions'][WorkspaceCommercialLifecycleResolver::ACTION_MANAGED_TENANT_ACTIVATION]['outcome'])
        ->toBe(WorkspaceCommercialLifecycleResolver::OUTCOME_ALLOW)
        ->and($summary['action_decisions'][WorkspaceCommercialLifecycleResolver::ACTION_REVIEW_PACK_START]['outcome'])
        ->toBe(WorkspaceCommercialLifecycleResolver::OUTCOME_ALLOW);
});

it('resolves explicit stored commercial lifecycle states with source rationale and platform attribution', function (string $state, string $expectedLabel): void {
    [$workspace] = commercialLifecycleWorkspaceManager();
    $operator = commercialLifecyclePlatformOperator();

    app(SettingsWriter::class)->updateWorkspaceCommercialLifecycle(
        actor: $operator,
        workspace: $workspace,
        state: $state,
        reason: 'Support approved commercial lifecycle transition',
    );

    $summary = app(WorkspaceCommercialLifecycleResolver::class)->summary($workspace);

    expect($summary)
        ->toMatchArray([
            'state' => $state,
            'state_label' => $expectedLabel,
            'source' => WorkspaceCommercialLifecycleResolver::SOURCE_WORKSPACE_SETTING,
            'source_label' => 'workspace setting',
            'rationale' => 'Support approved commercial lifecycle transition',
            'last_changed_by' => $operator->name,
        ])
        ->and($summary['last_changed_at'])->not->toBeNull();
})->with([
    'trial' => [WorkspaceCommercialLifecycleResolver::STATE_TRIAL, 'Trial'],
    'grace' => [WorkspaceCommercialLifecycleResolver::STATE_GRACE, 'Grace'],
    'active paid' => [WorkspaceCommercialLifecycleResolver::STATE_ACTIVE_PAID, 'Active paid'],
    'suspended read-only' => [WorkspaceCommercialLifecycleResolver::STATE_SUSPENDED_READ_ONLY, 'Suspended / read-only'],
]);

it('blocks activation but warns review pack starts during grace', function (): void {
    [$workspace] = commercialLifecycleWorkspaceManager();
    setCommercialLifecycleState($workspace, WorkspaceCommercialLifecycleResolver::STATE_GRACE, 'Payment collection pending');

    $resolver = app(WorkspaceCommercialLifecycleResolver::class);
    $activation = $resolver->actionDecision($workspace, WorkspaceCommercialLifecycleResolver::ACTION_MANAGED_TENANT_ACTIVATION);
    $reviewPackStart = $resolver->actionDecision($workspace, WorkspaceCommercialLifecycleResolver::ACTION_REVIEW_PACK_START);

    expect($activation)
        ->toMatchArray([
            'outcome' => WorkspaceCommercialLifecycleResolver::OUTCOME_BLOCK,
            'is_blocked' => true,
            'reason_family' => WorkspaceCommercialLifecycleResolver::REASON_FAMILY_COMMERCIAL_LIFECYCLE,
            'state' => WorkspaceCommercialLifecycleResolver::STATE_GRACE,
        ])
        ->and($activation['block_reason'])->toContain('grace')
        ->and($reviewPackStart)
        ->toMatchArray([
            'outcome' => WorkspaceCommercialLifecycleResolver::OUTCOME_WARN,
            'is_blocked' => false,
            'is_warning' => true,
            'reason_family' => WorkspaceCommercialLifecycleResolver::REASON_FAMILY_COMMERCIAL_LIFECYCLE,
            'state' => WorkspaceCommercialLifecycleResolver::STATE_GRACE,
        ])
        ->and($reviewPackStart['warning_reason'])->toContain('grace');
});

it('blocks new starts but allows read-only history during suspended read-only', function (): void {
    [$workspace] = commercialLifecycleWorkspaceManager();
    setCommercialLifecycleState($workspace, WorkspaceCommercialLifecycleResolver::STATE_SUSPENDED_READ_ONLY, 'Commercial suspension');

    $resolver = app(WorkspaceCommercialLifecycleResolver::class);

    expect($resolver->actionDecision($workspace, WorkspaceCommercialLifecycleResolver::ACTION_MANAGED_TENANT_ACTIVATION))
        ->toMatchArray([
            'outcome' => WorkspaceCommercialLifecycleResolver::OUTCOME_BLOCK,
            'is_blocked' => true,
            'reason_family' => WorkspaceCommercialLifecycleResolver::REASON_FAMILY_COMMERCIAL_LIFECYCLE,
        ])
        ->and($resolver->actionDecision($workspace, WorkspaceCommercialLifecycleResolver::ACTION_REVIEW_PACK_START))
        ->toMatchArray([
            'outcome' => WorkspaceCommercialLifecycleResolver::OUTCOME_BLOCK,
            'is_blocked' => true,
            'reason_family' => WorkspaceCommercialLifecycleResolver::REASON_FAMILY_COMMERCIAL_LIFECYCLE,
        ])
        ->and($resolver->actionDecision($workspace, WorkspaceCommercialLifecycleResolver::ACTION_EVIDENCE_READ))
        ->toMatchArray([
            'outcome' => WorkspaceCommercialLifecycleResolver::OUTCOME_ALLOW_READ_ONLY,
            'is_blocked' => false,
            'reason_family' => WorkspaceCommercialLifecycleResolver::REASON_FAMILY_COMMERCIAL_LIFECYCLE,
        ]);
});

it('preserves entitlement substrate blocks ahead of lifecycle outcomes', function (): void {
    [$workspace, $manager] = commercialLifecycleWorkspaceManager();
    setCommercialLifecycleState($workspace, WorkspaceCommercialLifecycleResolver::STATE_GRACE, 'Grace should not bypass substrate');

    ManagedEnvironment::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'status' => ManagedEnvironment::STATUS_ACTIVE,
    ]);

    app(SettingsWriter::class)->updateWorkspaceSetting(
        actor: $manager,
        workspace: $workspace,
        domain: WorkspaceEntitlementResolver::SETTING_DOMAIN,
        key: WorkspaceEntitlementResolver::SETTING_MANAGED_TENANT_LIMIT_OVERRIDE_VALUE,
        value: 1,
    );

    app(SettingsWriter::class)->updateWorkspaceSetting(
        actor: $manager,
        workspace: $workspace,
        domain: WorkspaceEntitlementResolver::SETTING_DOMAIN,
        key: WorkspaceEntitlementResolver::SETTING_REVIEW_PACK_GENERATION_OVERRIDE_VALUE,
        value: false,
    );

    $resolver = app(WorkspaceCommercialLifecycleResolver::class);

    expect($resolver->actionDecision($workspace, WorkspaceCommercialLifecycleResolver::ACTION_MANAGED_TENANT_ACTIVATION))
        ->toMatchArray([
            'outcome' => WorkspaceCommercialLifecycleResolver::OUTCOME_BLOCK,
            'reason_family' => WorkspaceCommercialLifecycleResolver::REASON_FAMILY_ENTITLEMENT_SUBSTRATE,
        ])
        ->and($resolver->actionDecision($workspace, WorkspaceCommercialLifecycleResolver::ACTION_REVIEW_PACK_START))
        ->toMatchArray([
            'outcome' => WorkspaceCommercialLifecycleResolver::OUTCOME_BLOCK,
            'reason_family' => WorkspaceCommercialLifecycleResolver::REASON_FAMILY_ENTITLEMENT_SUBSTRATE,
            'is_warning' => false,
        ]);
});

it('prefers subscription truth over fallback lifecycle settings when a current subscription exists', function (): void {
    [$workspace] = commercialLifecycleWorkspaceManager();

    setCommercialLifecycleState($workspace, WorkspaceCommercialLifecycleResolver::STATE_SUSPENDED_READ_ONLY, 'Legacy fallback state');

    WorkspaceSubscription::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'state' => 'active',
        'current_period_starts_at' => now()->subDay(),
        'current_period_ends_at' => now()->addDays(29),
        'status_reason' => 'Annual plan is current.',
    ]);

    $summary = app(WorkspaceCommercialLifecycleResolver::class)->summary($workspace);

    expect($summary)
        ->toMatchArray([
            'state' => WorkspaceCommercialLifecycleResolver::STATE_ACTIVE_PAID,
            'source' => 'workspace_subscription',
            'source_label' => 'workspace subscription',
        ])
        ->and($summary['action_decisions'][WorkspaceCommercialLifecycleResolver::ACTION_MANAGED_TENANT_ACTIVATION]['outcome'])
        ->toBe(WorkspaceCommercialLifecycleResolver::OUTCOME_ALLOW)
        ->and($summary['action_decisions'][WorkspaceCommercialLifecycleResolver::ACTION_REVIEW_PACK_START]['outcome'])
        ->toBe(WorkspaceCommercialLifecycleResolver::OUTCOME_ALLOW);
});
