<?php

declare(strict_types=1);

namespace App\Services\Worlds;

use App\Enums\WorldRewardType;
use App\Http\Resources\ArtworkListResource;
use App\Models\Artwork;
use App\Models\User;
use App\Models\World;
use App\Models\WorldSubmission;
use App\Services\Maturity\ArtworkMaturityService;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;

final class WorldSubmissionService
{
    private array $canonicalRecurringEligibilityIds = [];

    public function __construct(
        private readonly ArtworkMaturityService $maturity,
        private readonly WorldRewardService $rewards,
        private readonly WorldAnalyticsService $analytics,
    ) {
    }

    public function eligibleWorldOptions(?User $viewer = null): array
    {
        return $this->eligibleWorlds()
            ->map(fn (World $world): array => $this->mapCreatorWorldOption($world, null, true))
            ->all();
    }

    public function artworkSubmissionOptions(Artwork $artwork, User $viewer): array
    {
        $artwork->loadMissing(['worldSubmissions.world', 'worldSubmissions.reviewer']);

        $existing = $artwork->worldSubmissions
            ->filter(fn (WorldSubmission $submission): bool => $submission->world !== null)
            ->keyBy(fn (WorldSubmission $submission): int => (int) $submission->world_id);

        $eligibleWorlds = $this->eligibleWorlds()->keyBy(fn (World $world): int => (int) $world->id);
        $worlds = $eligibleWorlds;

        $missingWorldIds = $existing->keys()
            ->map(fn ($id): int => (int) $id)
            ->reject(fn (int $id): bool => $eligibleWorlds->has($id))
            ->values();

        if ($missingWorldIds->isNotEmpty()) {
            World::query()
                ->whereIn('id', $missingWorldIds->all())
                ->get()
                ->each(fn (World $world) => $worlds->put((int) $world->id, $world));
        }

        return $worlds
            ->sortBy([
                fn (World $world): int => $existing->has((int) $world->id) ? 0 : 1,
                fn (World $world): int => $world->isActiveCampaign() ? 0 : ($world->isUpcomingCampaign() ? 1 : 2),
                fn (World $world): int => -1 * (int) ($world->campaign_priority ?? 0),
                fn (World $world): int => $world->effectivePromotionStartsAt()?->getTimestamp() ?? PHP_INT_MAX,
                fn (World $world): string => Str::lower((string) $world->title),
            ])
            ->values()
            ->map(function (World $world) use ($existing): array {
                $submission = $existing->get((int) $world->id);

                return $this->mapCreatorWorldOption($world, $submission, $this->isEligibleWorld($world));
            })
            ->all();
    }

    public function syncForArtwork(Artwork $artwork, User $actor, array $entries): void
    {
        $artwork->loadMissing('worldSubmissions');

        $normalizedEntries = collect($entries)
            ->map(function (array $entry): ?array {
                $worldId = (int) ($entry['world_id'] ?? 0);
                if ($worldId < 1) {
                    return null;
                }

                return [
                    'world_id' => $worldId,
                    'note' => Str::limit(trim((string) ($entry['note'] ?? '')), 1000, ''),
                    'source_surface' => trim((string) ($entry['source_surface'] ?? '')),
                ];
            })
            ->filter()
            ->unique('world_id')
            ->values();

        $existing = $artwork->worldSubmissions->keyBy(fn (WorldSubmission $submission): int => (int) $submission->world_id);
        $selectedWorldIds = $normalizedEntries->pluck('world_id')->map(fn ($id): int => (int) $id)->all();
        $allWorldIds = array_values(array_unique(array_merge($selectedWorldIds, $existing->keys()->map(fn ($id): int => (int) $id)->all())));

        $worlds = World::query()
            ->whereIn('id', $allWorldIds)
            ->get()
            ->keyBy(fn (World $world): int => (int) $world->id);

        $errors = [];

        foreach ($normalizedEntries as $index => $entry) {
            $world = $worlds->get((int) $entry['world_id']);
            $submission = $existing->get((int) $entry['world_id']);

            if (! $world) {
                $errors["world_submissions.{$index}.world_id"] = 'Selected world no longer exists.';
                continue;
            }

            if (! $this->isEligibleWorld($world)) {
                $errors["world_submissions.{$index}.world_id"] = 'That world is not currently accepting community submissions.';
                continue;
            }

            if ($submission && $submission->isBlockingResubmission()) {
                $errors["world_submissions.{$index}.world_id"] = 'This artwork is blocked from that world until a moderator clears the block.';
                continue;
            }

            if ($submission && (string) $submission->status === WorldSubmission::STATUS_REMOVED && ! (bool) $world->allow_readd_after_removal) {
                $errors["world_submissions.{$index}.world_id"] = 'That world does not allow re-adding removed artworks right now.';
            }
        }

        if ($errors !== []) {
            throw ValidationException::withMessages($errors);
        }

        DB::transaction(function () use ($normalizedEntries, $artwork, $actor, $existing, $worlds, $selectedWorldIds): void {
            foreach ($normalizedEntries as $entry) {
                $worldId = (int) $entry['world_id'];
                $submission = $existing->get($worldId);
                $world = $worlds->get($worldId);

                $note = ($world?->submission_note_enabled ?? true) ? ($entry['note'] !== '' ? $entry['note'] : null) : null;
                $startingStatus = $world?->submissionStartsAsLive()
                    ? WorldSubmission::STATUS_LIVE
                    : WorldSubmission::STATUS_PENDING;
                $reviewedAt = $startingStatus === WorldSubmission::STATUS_LIVE ? now() : null;

                if ($submission && $submission->isBlockingResubmission()) {
                    continue;
                }

                if ($submission) {
                    $wasRemoved = (string) $submission->status === WorldSubmission::STATUS_REMOVED;
                    $payload = [
                        'mode_snapshot' => $world?->participation_mode,
                        'note' => $note,
                    ];

                    if ((string) $submission->status === WorldSubmission::STATUS_REMOVED) {
                        $payload = array_merge($payload, [
                            'status' => $startingStatus,
                            'is_featured' => false,
                            'reviewer_note' => null,
                            'moderation_reason' => null,
                            'reviewed_by_user_id' => null,
                            'reviewed_at' => $reviewedAt,
                            'removed_at' => null,
                            'blocked_at' => null,
                            'featured_at' => null,
                        ]);
                    }

                    $submission->forceFill($payload)->save();

                    if ($wasRemoved) {
                        $eventType = self::lifecycleEventForStatus($startingStatus);

                        if ($eventType !== null) {
                            $this->analytics->recordSubmissionLifecycle(
                                $submission,
                                $eventType,
                                $actor,
                                $entry['source_surface'] !== '' ? $entry['source_surface'] : null,
                                ['reactivated' => true]
                            );
                        }
                    }

                    $this->rewards->syncAutomaticRewardsForSubmission($submission);

                    continue;
                }

                $submission = WorldSubmission::query()->create([
                    'world_id' => $worldId,
                    'artwork_id' => (int) $artwork->id,
                    'submitted_by_user_id' => (int) $actor->id,
                    'status' => $startingStatus,
                    'is_featured' => false,
                    'mode_snapshot' => $world?->participation_mode,
                    'note' => $note,
                    'reviewed_at' => $reviewedAt,
                ]);

                $this->analytics->recordSubmissionLifecycle(
                    $submission,
                    WorldAnalyticsService::EVENT_SUBMISSION_CREATED,
                    $actor,
                    $entry['source_surface'] !== '' ? $entry['source_surface'] : null
                );

                if ($startingStatus === WorldSubmission::STATUS_LIVE) {
                    $this->analytics->recordSubmissionLifecycle(
                        $submission,
                        WorldAnalyticsService::EVENT_SUBMISSION_APPROVED,
                        $actor,
                        $entry['source_surface'] !== '' ? $entry['source_surface'] : null,
                        ['auto_approved' => true]
                    );
                }

                $this->rewards->syncAutomaticRewardsForSubmission($submission);
            }

            $existing->each(function (WorldSubmission $submission, int $worldId) use ($selectedWorldIds): void {
                if (in_array((string) $submission->status, [WorldSubmission::STATUS_LIVE, WorldSubmission::STATUS_REMOVED, WorldSubmission::STATUS_BLOCKED], true)) {
                    return;
                }

                if (! in_array($worldId, $selectedWorldIds, true)) {
                    $submission->delete();
                }
            });
        });
    }

    public function transition(WorldSubmission $submission, User $reviewer, string $status, ?string $reviewerNote = null): WorldSubmission
    {
        $payload = [
            'status' => $status,
            'reviewer_note' => $this->nullableText($reviewerNote),
            'moderation_reason' => $this->nullableText($reviewerNote),
        ];

        if ($status === WorldSubmission::STATUS_PENDING) {
            $payload['reviewer_note'] = null;
            $payload['moderation_reason'] = null;
            $payload['reviewed_by_user_id'] = null;
            $payload['reviewed_at'] = null;
            $payload['removed_at'] = null;
            $payload['blocked_at'] = null;
        } else {
            $payload['reviewed_by_user_id'] = (int) $reviewer->id;
            $payload['reviewed_at'] = now();
            $payload['removed_at'] = $status === WorldSubmission::STATUS_REMOVED ? now() : null;
            $payload['blocked_at'] = $status === WorldSubmission::STATUS_BLOCKED ? now() : null;
        }

        if ($status !== WorldSubmission::STATUS_LIVE) {
            $payload['is_featured'] = false;
            $payload['featured_at'] = null;
        }

        $submission->forceFill($payload)->save();

        $eventType = self::lifecycleEventForStatus($status);
        if ($eventType !== null) {
            $this->analytics->recordSubmissionLifecycle($submission, $eventType, $reviewer);
        }

        $this->rewards->syncAutomaticRewardsForSubmission($submission);

        return $submission->fresh(['artwork.user.profile', 'artwork.stats', 'artwork.categories', 'submittedBy.profile', 'reviewer.profile']);
    }

    public function setFeatured(WorldSubmission $submission, User $reviewer, bool $featured, ?string $reviewerNote = null): WorldSubmission
    {
        $payload = [
            'is_featured' => $featured,
            'featured_at' => $featured ? now() : null,
            'reviewed_by_user_id' => (int) $reviewer->id,
            'reviewed_at' => now(),
        ];

        if ($reviewerNote !== null) {
            $payload['reviewer_note'] = $this->nullableText($reviewerNote);
            $payload['moderation_reason'] = $this->nullableText($reviewerNote);
        }

        if ((string) $submission->status !== WorldSubmission::STATUS_LIVE) {
            $payload['status'] = WorldSubmission::STATUS_LIVE;
            $payload['removed_at'] = null;
            $payload['blocked_at'] = null;
        }

        $submission->forceFill($payload)->save();

        if ($featured) {
            $this->analytics->recordSubmissionLifecycle($submission, WorldAnalyticsService::EVENT_SUBMISSION_FEATURED, $reviewer);
        }

        $this->rewards->syncAutomaticRewardsForSubmission($submission);

        return $submission->fresh(['artwork.user.profile', 'artwork.stats', 'artwork.categories', 'submittedBy.profile', 'reviewer.profile']);
    }

    public function studioReviewQueue(World $world): array
    {
        $world->loadMissing([
            'worldSubmissions.artwork.user.profile',
            'worldSubmissions.artwork.stats',
            'worldSubmissions.artwork.categories',
            'worldSubmissions.submittedBy.profile',
            'worldSubmissions.reviewer.profile',
        ]);

        $rewardMap = $this->rewards->creatorRewardMapForWorld($world);

        $items = $world->worldSubmissions
            ->sortBy([
                fn (WorldSubmission $submission): int => match ((string) $submission->status) {
                    WorldSubmission::STATUS_PENDING => 0,
                    WorldSubmission::STATUS_LIVE => $submission->is_featured ? 1 : 2,
                    WorldSubmission::STATUS_REMOVED => 3,
                    WorldSubmission::STATUS_BLOCKED => 4,
                    default => 4,
                },
                fn (WorldSubmission $submission): int => -1 * ($submission->reviewed_at?->getTimestamp() ?? $submission->created_at?->getTimestamp() ?? 0),
            ])
            ->values();

        return [
            'counts' => [
                'pending' => $items->where('status', WorldSubmission::STATUS_PENDING)->count(),
                'live' => $items->where('status', WorldSubmission::STATUS_LIVE)->count(),
                'removed' => $items->where('status', WorldSubmission::STATUS_REMOVED)->count(),
                'blocked' => $items->where('status', WorldSubmission::STATUS_BLOCKED)->count(),
                'featured' => $items->where('is_featured', true)->count(),
            ],
            'items' => $items->map(fn (WorldSubmission $submission): array => $this->mapStudioSubmission($submission, $rewardMap))->all(),
        ];
    }

    public function publicSectionPayload(World $world, ?User $viewer = null): ?array
    {
        if (! $world->community_section_enabled) {
            return null;
        }

        $query = Artwork::query()
            ->select('artworks.*', 'world_submissions.status as world_submission_status', 'world_submissions.is_featured as world_submission_is_featured', 'world_submissions.note as world_submission_note', 'world_submissions.reviewed_at as world_submission_reviewed_at')
            ->join('world_submissions', function ($join) use ($world): void {
                $join->on('world_submissions.artwork_id', '=', 'artworks.id')
                    ->where('world_submissions.world_id', '=', $world->id)
                    ->where('world_submissions.status', '=', WorldSubmission::STATUS_LIVE);
            })
            ->with(['user.profile', 'categories.contentType', 'stats'])
            ->catalogVisible();

        $this->maturity->applyViewerFilter($query, $viewer);

        $items = $query
            ->orderByRaw('CASE WHEN world_submissions.is_featured = 1 THEN 0 ELSE 1 END')
            ->orderByDesc('world_submissions.reviewed_at')
            ->limit(24)
            ->get()
            ->map(fn (Artwork $artwork): array => $this->mapPublicSubmissionArtwork($artwork))
            ->all();

        if ($items === []) {
            return null;
        }

        return [
            'title' => 'Community submissions',
            'description' => 'Artworks submitted by creators and selected for this world outside the editorial curated-relation system.',
            'items' => $items,
        ];
    }

    private function eligibleWorldsQuery(): Builder
    {
        return World::query()
            ->published()
            ->where('accepts_submissions', true)
            ->whereIn('participation_mode', [World::PARTICIPATION_MODE_MANUAL_APPROVAL, World::PARTICIPATION_MODE_AUTO_ADD])
            ->where(function (Builder $builder): void {
                $builder->whereNull('submission_starts_at')
                    ->orWhere('submission_starts_at', '<=', now());
            })
            ->where(function (Builder $builder): void {
                $builder->whereNull('submission_ends_at')
                    ->orWhere('submission_ends_at', '>=', now());
            })
            ->orderByDesc('is_active_campaign')
            ->orderByDesc('is_homepage_featured')
            ->orderByRaw('COALESCE(campaign_priority, 0) DESC')
            ->orderByRaw('COALESCE(promotion_ends_at, submission_ends_at, ends_at) ASC')
            ->orderByRaw('COALESCE(promotion_starts_at, starts_at, submission_starts_at) ASC')
            ->orderBy('title');
    }

    private static function lifecycleEventForStatus(string $status): ?string
    {
        return match ($status) {
            WorldSubmission::STATUS_LIVE => WorldAnalyticsService::EVENT_SUBMISSION_APPROVED,
            WorldSubmission::STATUS_REMOVED => WorldAnalyticsService::EVENT_SUBMISSION_REMOVED,
            WorldSubmission::STATUS_BLOCKED => WorldAnalyticsService::EVENT_SUBMISSION_BLOCKED,
            default => null,
        };
    }

    private function eligibleWorlds()
    {
        return $this->eligibleWorldsQuery()
            ->get()
            ->filter(fn (World $world): bool => $this->isCanonicalRecurringEdition($world))
            ->values();
    }

    private function isEligibleWorld(World $world): bool
    {
        return $world->isAcceptingSubmissions() && $this->isCanonicalRecurringEdition($world);
    }

    private function isCanonicalRecurringEdition(World $world): bool
    {
        $recurrenceKey = trim((string) ($world->recurrence_key ?? ''));

        if (! $world->is_recurring || $recurrenceKey === '') {
            return true;
        }

        if (! array_key_exists($recurrenceKey, $this->canonicalRecurringEligibilityIds)) {
            $canonical = $this->eligibleWorldsQuery()
                ->where('recurrence_key', $recurrenceKey)
                ->get()
                ->sortBy([
                    fn (World $edition): int => $edition->isActiveCampaign() ? 0 : ($edition->isUpcomingCampaign() ? 1 : 2),
                    fn (World $edition): int => -1 * (int) ($edition->campaign_priority ?? 0),
                    fn (World $edition): int => -1 * (int) ($edition->edition_year ?? 0),
                    fn (World $edition): int => -1 * ($edition->starts_at?->getTimestamp() ?? $edition->published_at?->getTimestamp() ?? 0),
                    fn (World $edition): int => -1 * (int) $edition->id,
                ])
                ->first();

            $this->canonicalRecurringEligibilityIds[$recurrenceKey] = $canonical?->id;
        }

        return (int) ($this->canonicalRecurringEligibilityIds[$recurrenceKey] ?? 0) === (int) $world->id;
    }

    private function mapCreatorWorldOption(World $world, ?WorldSubmission $submission, bool $eligible): array
    {
        $status = $submission ? (string) $submission->status : null;
        $selected = match ($status) {
            WorldSubmission::STATUS_PENDING,
            WorldSubmission::STATUS_LIVE => true,
            default => false,
        };

        $locked = match ($status) {
            WorldSubmission::STATUS_BLOCKED => true,
            WorldSubmission::STATUS_PENDING => ! $eligible,
            WorldSubmission::STATUS_REMOVED => ! $eligible || ! (bool) $world->allow_readd_after_removal,
            default => false,
        };

        $lockedReason = $locked
            ? match ($status) {
                WorldSubmission::STATUS_BLOCKED => 'This artwork is blocked from this world until a moderator clears the block.',
                WorldSubmission::STATUS_PENDING => 'This world is no longer accepting submission changes right now.',
                WorldSubmission::STATUS_REMOVED => (bool) $world->allow_readd_after_removal
                    ? 'This world is not currently open for re-adding removed artworks.'
                    : 'Removed artworks cannot be re-added to this world right now.',
                default => 'This world is locked.',
            }
            : null;

        return [
            'id' => (int) $world->id,
            'title' => (string) $world->title,
            'teaser_title' => $world->teaserTitle(),
            'slug' => (string) $world->slug,
            'tagline' => (string) ($world->tagline ?? ''),
            'summary' => (string) ($world->summary ?? ''),
            'teaser_summary' => (string) ($world->teaserSummary() ?? ''),
            'cover_url' => $world->coverUrl(),
            'teaser_image_url' => $world->teaserImageUrl(),
            'campaign_label' => (string) ($world->campaign_label ?? ''),
            'timeframe_label' => $this->timeframeLabel($world),
            'promotion_window_label' => $this->promotionWindowLabel($world),
            'submission_window_label' => $this->submissionWindowLabel($world),
            'submission_guidelines' => (string) ($world->submission_guidelines ?? ''),
            'participation_mode' => (string) ($world->participation_mode ?: World::PARTICIPATION_MODE_CLOSED),
            'participation_mode_label' => $this->participationModeLabel((string) ($world->participation_mode ?: World::PARTICIPATION_MODE_CLOSED)),
            'submission_note_enabled' => (bool) $world->submission_note_enabled,
            'is_accepting_submissions' => $eligible,
            'is_active_campaign' => (bool) $world->is_active_campaign,
            'is_homepage_featured' => (bool) $world->is_homepage_featured,
            'campaign_priority' => $world->campaign_priority,
            'campaign_state_label' => $this->campaignStateLabel($world),
            'status_badges' => $this->campaignBadges($world),
            'selected' => $selected,
            'selection_locked' => $locked,
            'selection_locked_reason' => $lockedReason,
            'note' => (string) ($submission?->note ?? ''),
            'status' => $status,
            'status_label' => $status ? $this->statusLabel($status, (bool) ($submission?->is_featured ?? false)) : null,
            'reviewer_note' => (string) ($submission?->moderation_reason ?: $submission?->reviewer_note ?? ''),
            'is_featured' => (bool) ($submission?->is_featured ?? false),
            'submitted_at' => $submission?->created_at?->toIso8601String(),
            'reviewed_at' => $submission?->reviewed_at?->toIso8601String(),
            'can_resubmit' => $eligible && (bool) $world->allow_readd_after_removal && $status === WorldSubmission::STATUS_REMOVED,
        ];
    }

    private function campaignStateLabel(World $world): string
    {
        if ($world->isActiveCampaign()) {
            return 'Live now';
        }

        if ($world->isUpcomingCampaign() || ($world->starts_at && $world->starts_at->isFuture())) {
            return 'Upcoming';
        }

        if ((string) $world->status === World::STATUS_ARCHIVED || ($world->ends_at && $world->ends_at->isPast())) {
            return 'Archived';
        }

        return 'Open';
    }

    private function campaignBadges(World $world): array
    {
        $badges = [];

        if ($world->isActiveCampaign()) {
            $badges[] = ['label' => 'Live now', 'tone' => 'emerald'];
        } elseif ($world->isUpcomingCampaign() || ($world->starts_at && $world->starts_at->isFuture())) {
            $badges[] = ['label' => 'Upcoming', 'tone' => 'sky'];
        }

        if ($world->isEndingSoon()) {
            $badges[] = ['label' => 'Ending soon', 'tone' => 'amber'];
        }

        if ((bool) $world->is_homepage_featured || (bool) $world->is_featured) {
            $badges[] = ['label' => 'Featured', 'tone' => 'rose'];
        }

        return $badges;
    }

    private function promotionWindowLabel(World $world): ?string
    {
        if (! $world->promotion_starts_at && ! $world->promotion_ends_at) {
            return null;
        }

        if ($world->promotion_starts_at && $world->promotion_ends_at) {
            return 'Promotion ' . $world->promotion_starts_at->format('d M Y') . ' - ' . $world->promotion_ends_at->format('d M Y');
        }

        if ($world->promotion_starts_at) {
            return 'Promotion starts ' . $world->promotion_starts_at->format('d M Y');
        }

        return 'Promoted through ' . $world->promotion_ends_at?->format('d M Y');
    }

    private function mapStudioSubmission(WorldSubmission $submission, \Illuminate\Support\Collection $rewardMap): array
    {
        $artwork = $submission->artwork;
        $views = (int) ($artwork?->stats?->views ?? 0);
        $creatorId = (int) ($artwork?->user?->id ?? 0);

        return [
            'id' => (int) $submission->id,
            'status' => (string) $submission->status,
            'status_label' => $this->statusLabel((string) $submission->status, (bool) $submission->is_featured),
            'can_grant_manual_rewards' => (string) $submission->status === WorldSubmission::STATUS_LIVE,
            'is_featured' => (bool) $submission->is_featured,
            'note' => (string) ($submission->note ?? ''),
            'reviewer_note' => (string) ($submission->moderation_reason ?: $submission->reviewer_note ?? ''),
            'submitted_at' => $submission->created_at?->toIso8601String(),
            'reviewed_at' => $submission->reviewed_at?->toIso8601String(),
            'removed_at' => $submission->removed_at?->toIso8601String(),
            'blocked_at' => $submission->blocked_at?->toIso8601String(),
            'featured_at' => $submission->featured_at?->toIso8601String(),
            'submitted_by' => $submission->submittedBy ? [
                'id' => (int) $submission->submittedBy->id,
                'name' => (string) ($submission->submittedBy->name ?: $submission->submittedBy->username ?: 'Unknown creator'),
                'username' => (string) ($submission->submittedBy->username ?? ''),
            ] : null,
            'reviewed_by' => $submission->reviewer ? [
                'id' => (int) $submission->reviewer->id,
                'name' => (string) ($submission->reviewer->name ?: $submission->reviewer->username ?: 'Moderator'),
            ] : null,
            'artwork' => $artwork ? [
                'id' => (int) $artwork->id,
                'title' => (string) ($artwork->title ?: 'Untitled artwork'),
                'slug' => (string) ($artwork->slug ?? ''),
                'url' => route('art.show', ['id' => $artwork->id, 'slug' => $artwork->slug ?: Str::slug((string) $artwork->title)]),
                'edit_url' => route('studio.artworks.edit', ['id' => $artwork->id]),
                'thumbnail_url' => $artwork->thumbUrl('md'),
                'creator_name' => (string) ($artwork->user?->name ?: $artwork->user?->username ?: ''),
                'meta' => array_values(array_filter([
                    $artwork->categories->first()?->name,
                    $views > 0 ? number_format($views) . ' views' : null,
                    $artwork->visibility ? Str::headline((string) $artwork->visibility) : null,
                ])),
            ] : null,
            'world_rewards' => $creatorId > 0 ? ($rewardMap->get($creatorId) ?? []) : [],
            'actions' => [
                'approve' => route('studio.worlds.submissions.approve', ['world' => $submission->world_id, 'submission' => $submission->id]),
                'remove' => route('studio.worlds.submissions.remove', ['world' => $submission->world_id, 'submission' => $submission->id]),
                'block' => route('studio.worlds.submissions.block', ['world' => $submission->world_id, 'submission' => $submission->id]),
                'unblock' => route('studio.worlds.submissions.unblock', ['world' => $submission->world_id, 'submission' => $submission->id]),
                'restore' => route('studio.worlds.submissions.restore', ['world' => $submission->world_id, 'submission' => $submission->id]),
                'feature' => route('studio.worlds.submissions.feature', ['world' => $submission->world_id, 'submission' => $submission->id]),
                'unfeature' => route('studio.worlds.submissions.unfeature', ['world' => $submission->world_id, 'submission' => $submission->id]),
                'pending' => route('studio.worlds.submissions.pending', ['world' => $submission->world_id, 'submission' => $submission->id]),
                'grant_rewards' => [
                    WorldRewardType::Winner->value => route('studio.worlds.submissions.rewards.grant', ['world' => $submission->world_id, 'submission' => $submission->id, 'rewardType' => WorldRewardType::Winner->value]),
                    WorldRewardType::Finalist->value => route('studio.worlds.submissions.rewards.grant', ['world' => $submission->world_id, 'submission' => $submission->id, 'rewardType' => WorldRewardType::Finalist->value]),
                    WorldRewardType::Spotlight->value => route('studio.worlds.submissions.rewards.grant', ['world' => $submission->world_id, 'submission' => $submission->id, 'rewardType' => WorldRewardType::Spotlight->value]),
                ],
                'revoke_rewards' => [
                    WorldRewardType::Winner->value => route('studio.worlds.submissions.rewards.revoke', ['world' => $submission->world_id, 'submission' => $submission->id, 'rewardType' => WorldRewardType::Winner->value]),
                    WorldRewardType::Finalist->value => route('studio.worlds.submissions.rewards.revoke', ['world' => $submission->world_id, 'submission' => $submission->id, 'rewardType' => WorldRewardType::Finalist->value]),
                    WorldRewardType::Spotlight->value => route('studio.worlds.submissions.rewards.revoke', ['world' => $submission->world_id, 'submission' => $submission->id, 'rewardType' => WorldRewardType::Spotlight->value]),
                ],
            ],
        ];
    }

    private function mapPublicSubmissionArtwork(Artwork $artwork): array
    {
        $resource = ArtworkListResource::make($artwork)->toArray(request());
        $views = (int) ($artwork->stats?->views ?? 0);
        $status = (string) ($artwork->world_submission_status ?? WorldSubmission::STATUS_LIVE);
        $isFeatured = (bool) ($artwork->world_submission_is_featured ?? false);

        return [
            'id' => (int) $artwork->id,
            'title' => (string) ($resource['title'] ?? $artwork->title ?? 'Untitled artwork'),
            'subtitle' => (string) ($resource['author']['name'] ?? ''),
            'description' => Str::limit(trim(strip_tags((string) ($artwork->description ?? ''))), 120),
            'url' => (string) ($resource['urls']['canonical'] ?? route('art.show', ['id' => $artwork->id, 'slug' => $artwork->slug ?: Str::slug((string) $artwork->title)])),
            'image' => $resource['thumbnail_url'] ?? $artwork->thumbUrl('md'),
            'status' => $status,
            'status_label' => $this->statusLabel($status, $isFeatured),
            'context_label' => $isFeatured ? 'Community featured' : 'Community submission',
            'meta' => array_values(array_filter([
                $resource['category']['name'] ?? null,
                $views > 0 ? number_format($views) . ' views' : null,
            ])),
        ];
    }

    private function statusLabel(string $status, bool $isFeatured = false): string
    {
        if ($status === WorldSubmission::STATUS_LIVE && $isFeatured) {
            return 'Featured';
        }

        return match ($status) {
            WorldSubmission::STATUS_PENDING => 'Pending',
            WorldSubmission::STATUS_LIVE => 'Live',
            WorldSubmission::STATUS_REMOVED => 'Removed',
            WorldSubmission::STATUS_BLOCKED => 'Blocked',
            default => Str::headline($status),
        };
    }

    private function participationModeLabel(string $mode): string
    {
        return match ($mode) {
            World::PARTICIPATION_MODE_MANUAL_APPROVAL => 'Manual approval',
            World::PARTICIPATION_MODE_AUTO_ADD => 'Auto add',
            World::PARTICIPATION_MODE_CLOSED => 'Closed',
            default => Str::headline($mode),
        };
    }

    private function timeframeLabel(World $world): string
    {
        if ($world->starts_at && $world->ends_at) {
            return $world->starts_at->format('M j') . ' - ' . $world->ends_at->format('M j, Y');
        }

        if ($world->starts_at) {
            return 'Starts ' . $world->starts_at->format('M j, Y');
        }

        if ($world->ends_at) {
            return 'Until ' . $world->ends_at->format('M j, Y');
        }

        return 'Open-ended world';
    }

    private function submissionWindowLabel(World $world): string
    {
        $start = $world->submission_starts_at;
        $end = $world->submission_ends_at;

        if ($start && $end) {
            return $start->format('M j') . ' - ' . $end->format('M j, Y');
        }

        if ($start) {
            return 'Opens ' . $start->format('M j, Y');
        }

        if ($end) {
            return 'Open until ' . $end->format('M j, Y');
        }

        return 'Open submissions';
    }

    private function nullableText(?string $value): ?string
    {
        $value = trim((string) $value);

        return $value !== '' ? $value : null;
    }
}