<?php

declare(strict_types=1);

namespace App\Services;

use App\Models\Artwork;
use App\Models\Collection;
use App\Models\Group;
use App\Models\User;
use App\Services\Maturity\ArtworkMaturityService;
use App\Services\ThumbnailPresenter;
use Illuminate\Http\UploadedFile;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class GroupService
{
    public function __construct(
        private readonly GroupMembershipService $memberships,
        private readonly GroupCardService $cards,
        private readonly CollectionService $collections,
        private readonly GroupMediaService $media,
        private readonly GroupJoinRequestService $joinRequests,
        private readonly GroupRecruitmentService $recruitment,
        private readonly GroupPostService $posts,
        private readonly GroupProjectService $projects,
        private readonly GroupReleaseService $releases,
        private readonly GroupChallengeService $challenges,
        private readonly GroupEventService $events,
        private readonly GroupAssetService $assets,
        private readonly GroupActivityService $activity,
        private readonly GroupHistoryService $history,
        private readonly GroupReputationService $reputation,
        private readonly ArtworkMaturityService $maturity,
    ) {
    }

    public function makeUniqueSlug(string $source, ?int $ignoreGroupId = null): string
    {
        $base = Str::slug(Str::limit($source, 90, ''));
        $base = $base !== '' ? $base : 'group';
        $slug = $base;
        $suffix = 2;

        while (Group::query()
            ->where('slug', $slug)
            ->when($ignoreGroupId !== null, fn ($query) => $query->where('id', '!=', $ignoreGroupId))
            ->exists()) {
            $slug = Str::limit($base, 84, '') . '-' . $suffix;
            $suffix++;
        }

        return $slug;
    }

    public function createGroup(User $owner, array $attributes): Group
    {
        $storedAvatarPath = null;
        $storedBannerPath = null;

        try {
            return DB::transaction(function () use ($owner, $attributes, &$storedAvatarPath, &$storedBannerPath): Group {
                $group = new Group();
                $group->owner()->associate($owner);
                $group->featured_artwork_id = null;
                $group->is_verified = false;
                $group->founded_at = $attributes['founded_at'] ?? null;
                $group->name = (string) $attributes['name'];
                $group->slug = $this->makeUniqueSlug((string) ($attributes['slug'] ?? $attributes['name']));
                $group->headline = $attributes['headline'] ?? null;
                $group->bio = $attributes['bio'] ?? null;
                $group->type = $attributes['type'] ?? null;
                $group->visibility = (string) ($attributes['visibility'] ?? Group::VISIBILITY_PUBLIC);
                $group->status = Group::LIFECYCLE_ACTIVE;
                $group->membership_policy = (string) ($attributes['membership_policy'] ?? Group::MEMBERSHIP_INVITE_ONLY);
                $group->website_url = $attributes['website_url'] ?? null;
                $group->links_json = $this->normalizeLinks($attributes['links_json'] ?? []);
                $group->avatar_path = $this->normalizeMediaPath($attributes['avatar_path'] ?? null);
                $group->banner_path = $this->normalizeMediaPath($attributes['banner_path'] ?? null);
                $group->artworks_count = 0;
                $group->collections_count = 0;
                $group->followers_count = 0;
                $group->last_activity_at = now();
                $group->save();

                if (($attributes['avatar_file'] ?? null) instanceof UploadedFile) {
                    $storedAvatarPath = $this->media->storeUploadedImage($group, $attributes['avatar_file'], 'avatar');
                    $group->avatar_path = $storedAvatarPath;
                }

                if (($attributes['banner_file'] ?? null) instanceof UploadedFile) {
                    $storedBannerPath = $this->media->storeUploadedImage($group, $attributes['banner_file'], 'banner');
                    $group->banner_path = $storedBannerPath;
                }

                if ($storedAvatarPath !== null || $storedBannerPath !== null) {
                    $group->save();
                }

                $this->memberships->ensureOwnerMembership($group);

                return $group->fresh(['owner.profile']);
            });
        } catch (\Throwable $exception) {
            $this->media->deleteIfManaged($storedAvatarPath);
            $this->media->deleteIfManaged($storedBannerPath);

            throw $exception;
        }
    }

    public function updateGroup(Group $group, array $attributes, User $actor): Group
    {
        $storedAvatarPath = null;
        $storedBannerPath = null;
        $obsoleteAvatarPath = null;
        $obsoleteBannerPath = null;

        try {
            $updatedGroup = DB::transaction(function () use ($group, $attributes, $actor, &$storedAvatarPath, &$storedBannerPath, &$obsoleteAvatarPath, &$obsoleteBannerPath): Group {
                $originalAvatarPath = $group->avatar_path;
                $originalBannerPath = $group->banner_path;

                $group->fill([
                    'name' => (string) ($attributes['name'] ?? $group->name),
                    'slug' => $this->makeUniqueSlug((string) ($attributes['slug'] ?? $attributes['name'] ?? $group->slug), (int) $group->id),
                    'headline' => $attributes['headline'] ?? null,
                    'bio' => $attributes['bio'] ?? null,
                    'type' => $attributes['type'] ?? $group->type,
                    'visibility' => (string) ($attributes['visibility'] ?? $group->visibility),
                    'membership_policy' => (string) ($attributes['membership_policy'] ?? $group->membership_policy ?? Group::MEMBERSHIP_INVITE_ONLY),
                    'founded_at' => $attributes['founded_at'] ?? $group->founded_at,
                    'website_url' => $attributes['website_url'] ?? null,
                    'links_json' => $this->normalizeLinks($attributes['links_json'] ?? $group->links_json ?? []),
                    'avatar_path' => array_key_exists('avatar_path', $attributes) ? $this->normalizeMediaPath($attributes['avatar_path']) : $group->avatar_path,
                    'banner_path' => array_key_exists('banner_path', $attributes) ? $this->normalizeMediaPath($attributes['banner_path']) : $group->banner_path,
                    'featured_artwork_id' => $this->normalizeFeaturedArtworkId($group, $attributes['featured_artwork_id'] ?? $group->featured_artwork_id),
                    'last_activity_at' => now(),
                ]);
                $group->save();

                if (($attributes['avatar_file'] ?? null) instanceof UploadedFile) {
                    $storedAvatarPath = $this->media->storeUploadedImage($group, $attributes['avatar_file'], 'avatar');
                    $group->avatar_path = $storedAvatarPath;
                }

                if (($attributes['banner_file'] ?? null) instanceof UploadedFile) {
                    $storedBannerPath = $this->media->storeUploadedImage($group, $attributes['banner_file'], 'banner');
                    $group->banner_path = $storedBannerPath;
                }

                if ($storedAvatarPath !== null || $storedBannerPath !== null) {
                    $group->save();
                }

                $this->memberships->ensureOwnerMembership($group);

                $obsoleteAvatarPath = $originalAvatarPath !== $group->avatar_path ? $originalAvatarPath : null;
                $obsoleteBannerPath = $originalBannerPath !== $group->banner_path ? $originalBannerPath : null;

                return $group->fresh(['owner.profile']);
            });
        } catch (\Throwable $exception) {
            $this->media->deleteIfManaged($storedAvatarPath);
            $this->media->deleteIfManaged($storedBannerPath);

            throw $exception;
        }

        $this->media->deleteIfManaged($obsoleteAvatarPath);
        $this->media->deleteIfManaged($obsoleteBannerPath);

        return $updatedGroup;
    }

    public function syncArtworkCount(Group $group): void
    {
        $group->forceFill([
            'artworks_count' => Artwork::query()
                ->where('group_id', $group->id)
                ->whereNull('deleted_at')
                ->count(),
            'last_activity_at' => now(),
        ])->save();
    }

    public function syncCollectionCount(Group $group): void
    {
        $group->forceFill([
            'collections_count' => Collection::query()
                ->where('group_id', $group->id)
                ->whereNull('deleted_at')
                ->count(),
            'last_activity_at' => now(),
        ])->save();
    }

    public function studioOptionsForUser(User $user): array
    {
        $groups = Group::query()
            ->with(['owner.profile', 'members'])
            ->where('status', '!=', Group::LIFECYCLE_SUSPENDED)
            ->where(function ($query) use ($user): void {
                $query->where('owner_user_id', $user->id)
                    ->orWhereHas('members', function ($memberQuery) use ($user): void {
                        $memberQuery->where('user_id', $user->id)
                            ->where('status', Group::STATUS_ACTIVE);
                    });
            })
            ->orderBy('name')
            ->get();

        return $groups->map(function (Group $group) use ($user): array {
            $canPublishArtworks = $group->canPublishArtworks($user);
            $canSubmitArtworkForReview = $group->canSubmitArtworkForReview($user);
            $canManageReleases = $group->canManageReleases($user);
            $canViewReputation = $group->canViewReputationDashboard($user);

            return [
                'id' => (int) $group->id,
                'name' => (string) $group->name,
                'slug' => (string) $group->slug,
                'role' => $group->activeRoleFor($user),
                'role_label' => Group::displayRole($group->activeRoleFor($user)),
                'status' => (string) ($group->status ?? Group::LIFECYCLE_ACTIVE),
                'avatar_url' => $group->avatarUrl(),
                'artworks_count' => (int) $group->artworks_count,
                'collections_count' => (int) $group->collections_count,
                'followers_count' => (int) $group->followers_count,
                'permissions' => [
                    'can_publish_artworks' => $canPublishArtworks,
                    'can_submit_artwork_for_review' => $canSubmitArtworkForReview,
                ],
                'public_url' => $group->publicUrl(),
                'studio_url' => route('studio.groups.show', ['group' => $group]),
                'studio_artworks_url' => route('studio.groups.artworks', ['group' => $group]),
                'studio_collections_url' => route('studio.groups.collections', ['group' => $group]),
                'studio_members_url' => route('studio.groups.members', ['group' => $group]),
                'studio_invitations_url' => route('studio.groups.invitations', ['group' => $group]),
                'studio_join_requests_url' => route('studio.groups.join-requests', ['group' => $group]),
                'studio_review_url' => route('studio.groups.review', ['group' => $group]),
                'studio_recruitment_url' => route('studio.groups.recruitment', ['group' => $group]),
                'studio_posts_url' => route('studio.groups.posts.index', ['group' => $group]),
                'studio_settings_url' => route('studio.groups.settings', ['group' => $group]),
                'studio_projects_url' => route('studio.groups.projects.index', ['group' => $group]),
                'studio_releases_url' => $canManageReleases ? route('studio.groups.releases.index', ['group' => $group]) : null,
                'studio_challenges_url' => route('studio.groups.challenges.index', ['group' => $group]),
                'studio_events_url' => route('studio.groups.events.index', ['group' => $group]),
                'studio_assets_url' => route('studio.groups.assets.index', ['group' => $group]),
                'studio_reputation_url' => $canViewReputation ? route('studio.groups.reputation', ['group' => $group]) : null,
                'studio_activity_url' => route('studio.groups.activity', ['group' => $group]),
                'upload_url' => ($canPublishArtworks || $canSubmitArtworkForReview) ? route('upload', ['group' => $group->slug]) : null,
                'collection_create_url' => route('settings.collections.create', ['group' => $group->slug]),
            ];
        })->values()->all();
    }

    public function mapGroupCard(Group $group, ?User $viewer = null): array
    {
        return $this->cards->mapGroupCard($group, $viewer);
    }

    public function mapGroupDetail(Group $group, ?User $viewer = null): array
    {
        $recruitment = $this->recruitment->payloadForGroup($group);

        return array_merge($this->mapGroupCard($group, $viewer), [
            'website_url' => $group->website_url,
            'bio' => $group->bio,
            'links' => $this->normalizeLinks($group->links_json ?? []),
            'avatar_path' => $group->avatar_path,
            'banner_path' => $group->banner_path,
            'featured_artwork_id' => $group->featured_artwork_id ? (int) $group->featured_artwork_id : null,
            'founded_at' => $group->founded_at?->toISOString(),
            'last_activity_at' => $group->last_activity_at?->toISOString(),
            'created_at' => $group->created_at?->toISOString(),
            'current_join_request' => $this->joinRequests->currentRequestFor($group, $viewer),
            'recruitment' => $recruitment,
            'pinned_post' => $this->posts->pinnedPost($group),
            'featured_release' => $this->releases->featuredRelease($group, $viewer),
            'featured_project' => $this->projects->featuredProject($group, $viewer),
            'active_challenge' => $this->challenges->activeChallenge($group, $viewer),
            'upcoming_event' => $this->events->upcomingEvent($group, $viewer),
            'badge_showcase' => $this->reputation->groupBadges($group, 8),
            'top_contributors' => $this->reputation->topContributors($group, 6),
            'trust_signals' => $this->reputation->trustSignals($group),
        ]);
    }

    public function recentPostCards(Group $group, int $limit = 3): array
    {
        return $this->posts->recentPosts($group, $limit);
    }

    public function recentProjectCards(Group $group, ?User $viewer = null, int $limit = 3): array
    {
        return $this->projects->publicListing($group, $viewer, $limit);
    }

    public function recentReleaseCards(Group $group, ?User $viewer = null, int $limit = 3): array
    {
        return $this->releases->publicListing($group, $viewer, $limit);
    }

    public function recentChallengeCards(Group $group, ?User $viewer = null, int $limit = 3): array
    {
        return $this->challenges->publicListing($group, $viewer, $limit);
    }

    public function recentEventCards(Group $group, ?User $viewer = null, int $limit = 3): array
    {
        return $this->events->publicListing($group, $viewer, $limit);
    }

    public function publicProjectListing(Group $group, ?User $viewer = null, int $limit = 12): array
    {
        return $this->projects->publicListing($group, $viewer, $limit);
    }

    public function publicReleaseListing(Group $group, ?User $viewer = null, int $limit = 12): array
    {
        return $this->releases->publicListing($group, $viewer, $limit);
    }

    public function publicChallengeListing(Group $group, ?User $viewer = null, int $limit = 12): array
    {
        return $this->challenges->publicListing($group, $viewer, $limit);
    }

    public function publicEventListing(Group $group, ?User $viewer = null, int $limit = 12): array
    {
        return $this->events->publicListing($group, $viewer, $limit);
    }

    public function publicAssetListing(Group $group, int $limit = 12): array
    {
        return $this->assets->publicListing($group, $limit);
    }

    public function publicActivityFeed(Group $group, int $limit = 8): array
    {
        return $this->activity->publicFeed($group, $limit);
    }

    public function studioActivityFeed(Group $group, User $viewer, int $limit = 20): array
    {
        return $this->activity->studioFeed($group, $viewer, $limit);
    }

    public function publicPostListing(Group $group, int $limit = 12): array
    {
        return $this->posts->publicPosts($group, $limit);
    }

    public function recruitmentPayload(Group $group): ?array
    {
        return $this->recruitment->payloadForGroup($group);
    }

    public function recentHistory(Group $group, int $limit = 8): array
    {
        return $this->history->recentFor($group, $limit);
    }

    public function featuredArtworkCards(Group $group, int $limit = 4): array
    {
        $query = Artwork::query()
            ->with(['user.profile', 'group', 'primaryAuthor.profile'])
            ->where('group_id', $group->id)
            ->whereNull('deleted_at')
            ->where('is_public', true)
            ->where('is_approved', true)
            ->whereNotNull('published_at');

        $this->maturity->applyViewerFilter($query, request()->user());

        if ((int) ($group->featured_artwork_id ?? 0) > 0) {
            $featuredArtwork = (clone $query)
                ->where('id', (int) $group->featured_artwork_id)
                ->first();

            $remaining = (clone $query)
                ->where('id', '!=', (int) $group->featured_artwork_id)
                ->latest('published_at')
                ->limit(max($limit - ($featuredArtwork ? 1 : 0), 0))
                ->get();

            return collect([$featuredArtwork])
                ->filter()
                ->concat($remaining)
                ->map(fn (Artwork $artwork): array => $this->mapPublicArtworkCard($artwork))
                ->values()
                ->all();
        }

        return (clone $query)
            ->latest('published_at')
            ->limit($limit)
            ->get()
            ->map(fn (Artwork $artwork): array => $this->mapPublicArtworkCard($artwork))
            ->values()
            ->all();
    }

    public function featuredCollectionCards(Group $group, ?User $viewer = null, int $limit = 3): array
    {
        $collections = Collection::query()
            ->with(['user.profile', 'group', 'coverArtwork'])
            ->where('group_id', $group->id)
            ->whereNull('deleted_at')
            ->where('is_featured', true)
            ->latest('featured_at')
            ->latest('updated_at')
            ->limit($limit)
            ->get()
            ->filter(fn (Collection $collection): bool => $collection->isPubliclyAccessible())
            ->values();

        return $this->collections->mapCollectionCardPayloads($collections, false, $viewer);
    }

    public function mapLeadershipPreview(array $members, array $owner, int $limit = 4): array
    {
        $leadership = collect($members)
            ->filter(fn (array $member): bool => in_array((string) ($member['role'] ?? ''), [Group::ROLE_OWNER, Group::ROLE_ADMIN], true))
            ->map(function (array $member): array {
                return [
                    'id' => (int) ($member['user']['id'] ?? 0),
                    'name' => $member['user']['name'] ?? null,
                    'username' => $member['user']['username'] ?? null,
                    'avatar_url' => $member['user']['avatar_url'] ?? null,
                    'profile_url' => $member['user']['profile_url'] ?? null,
                    'role' => (string) ($member['role'] ?? ''),
                    'role_label' => $member['role_label'] ?? Group::displayRole((string) ($member['role'] ?? '')),
                ];
            })
            ->unique('id')
            ->values();

        if ($leadership->isEmpty() && ! empty($owner['id'])) {
            $leadership = collect([array_merge($owner, [
                'role' => Group::ROLE_OWNER,
                'role_label' => Group::displayRole(Group::ROLE_OWNER),
            ])]);
        }

        return $leadership->take($limit)->all();
    }

    public function archiveGroup(Group $group, User $actor): Group
    {
        if (! $group->canArchive($actor) && ! $actor->isAdmin()) {
            abort(403);
        }

        $group->forceFill([
            'status' => Group::LIFECYCLE_ARCHIVED,
            'last_activity_at' => now(),
        ])->save();

        return $group->fresh(['owner.profile', 'members']);
    }

    public function publicArtworkCards(Group $group, int $limit = 18): array
    {
        $query = Artwork::query()
            ->with(['user.profile', 'group', 'primaryAuthor.profile'])
            ->where('group_id', $group->id)
            ->whereNull('deleted_at')
            ->where('is_public', true)
            ->where('is_approved', true)
            ->whereNotNull('published_at')
            ->latest('published_at');

        $this->maturity->applyViewerFilter($query, request()->user());

        return $query
            ->limit($limit)
            ->get()
            ->map(fn (Artwork $artwork): array => $this->mapPublicArtworkCard($artwork))
            ->values()
            ->all();
    }

    public function publicCollectionCards(Group $group, ?User $viewer = null, int $limit = 12): array
    {
        $collections = Collection::query()
            ->with(['user.profile', 'group', 'coverArtwork'])
            ->where('group_id', $group->id)
            ->whereNull('deleted_at')
            ->latest('updated_at')
            ->limit($limit)
            ->get()
            ->filter(fn (Collection $collection): bool => $collection->isPubliclyAccessible())
            ->values();

        return $this->collections->mapCollectionCardPayloads($collections, false, $viewer);
    }

    private function mapPublicArtworkCard(Artwork $artwork): array
    {
        return $this->maturity->decoratePayload([
            'id' => (int) $artwork->id,
            'title' => (string) $artwork->title,
            'url' => route('art.show', ['id' => $artwork->id, 'slug' => Str::slug((string) ($artwork->slug ?: $artwork->title)) ?: $artwork->id]),
            'thumb' => ThumbnailPresenter::present($artwork, 'md')['url'] ?? $artwork->thumbUrl('md'),
            'thumb_srcset' => ThumbnailPresenter::srcsetForArtwork($artwork),
            'author' => $artwork->primaryAuthor?->name ?: $artwork->primaryAuthor?->username ?: $artwork->user?->name ?: $artwork->user?->username ?: 'Artist',
            'published_at' => $artwork->published_at?->toISOString(),
        ], $artwork, request()->user());
    }

    public function studioDashboardSummary(Group $group): array
    {
        $artworkQuery = Artwork::query()
            ->where('group_id', $group->id)
            ->whereNull('deleted_at');

        $collectionQuery = Collection::query()
            ->where('group_id', $group->id)
            ->whereNull('deleted_at');

        return [
            'draft_artworks_count' => (clone $artworkQuery)->where('artwork_status', 'draft')->count(),
            'scheduled_artworks_count' => (clone $artworkQuery)->where('artwork_status', 'scheduled')->count(),
            'published_artworks_count' => (clone $artworkQuery)->where('artwork_status', 'published')->count(),
            'pending_reviews_count' => (clone $artworkQuery)->where('group_review_status', 'submitted')->count(),
            'draft_collections_count' => (clone $collectionQuery)
                ->where(function ($builder): void {
                    $builder->where('lifecycle_state', Collection::LIFECYCLE_DRAFT)
                        ->orWhere('workflow_state', Collection::WORKFLOW_DRAFT)
                        ->orWhere('workflow_state', Collection::WORKFLOW_IN_REVIEW);
                })
                ->count(),
            'active_members_count' => (int) $group->members()->where('status', Group::STATUS_ACTIVE)->count(),
            'pending_invites_count' => $this->memberships->pendingInviteCount($group),
            'pending_join_requests_count' => $this->joinRequests->pendingCount($group),
            'published_posts_count' => (int) $group->posts()->where('status', \App\Models\GroupPost::STATUS_PUBLISHED)->count(),
            'is_recruiting' => (bool) ($this->recruitment->payloadForGroup($group)['is_recruiting'] ?? false),
            'projects_count' => (int) $group->projects()->count(),
            'releases_count' => (int) $group->releases()->count(),
            'published_releases_count' => (int) $group->releases()->where('status', \App\Models\GroupRelease::STATUS_RELEASED)->count(),
            'active_challenges_count' => (int) $group->challenges()->whereIn('status', ['published', 'active'])->count(),
            'events_count' => (int) $group->events()->count(),
            'assets_count' => (int) $group->assets()->count(),
            'activity_count' => (int) $group->activityItems()->count(),
            'group_badges_count' => (int) $group->badges()->count(),
            'member_badges_count' => (int) $group->memberBadges()->count(),
            'trust_score' => (float) ($group->discoveryMetric?->trust_score ?? 0),
        ];
    }

    public function studioArtworkPreviewItems(Group $group, string $bucket = 'all', int $limit = 6): array
    {
        $query = Artwork::query()
            ->with(['user.profile', 'primaryAuthor.profile', 'stats'])
            ->where('group_id', $group->id)
            ->whereNull('deleted_at');

        if ($bucket === 'drafts') {
            $query->where('artwork_status', 'draft');
        } elseif ($bucket === 'scheduled') {
            $query->where('artwork_status', 'scheduled');
        } elseif ($bucket === 'published') {
            $query->where('artwork_status', 'published');
        }

        return $query->latest('updated_at')
            ->limit($limit)
            ->get()
            ->map(fn (Artwork $artwork): array => $this->mapStudioArtworkItem($artwork))
            ->values()
            ->all();
    }

    public function studioFeaturedArtworkOptions(Group $group, int $limit = 24): array
    {
        return Artwork::query()
            ->with(['user.profile', 'primaryAuthor.profile'])
            ->where('group_id', $group->id)
            ->whereNull('deleted_at')
            ->where('artwork_status', 'published')
            ->where('is_public', true)
            ->where('is_approved', true)
            ->whereNotNull('published_at')
            ->latest('published_at')
            ->limit($limit)
            ->get()
            ->map(fn (Artwork $artwork): array => [
                'id' => (int) $artwork->id,
                'title' => (string) $artwork->title,
                'author' => $artwork->primaryAuthor?->name ?: $artwork->primaryAuthor?->username ?: $artwork->user?->name ?: $artwork->user?->username,
                'thumb' => ThumbnailPresenter::present($artwork, 'md')['url'] ?? $artwork->thumbUrl('md'),
                'published_at' => $artwork->published_at?->toISOString(),
            ])
            ->values()
            ->all();
    }

    public function studioCollectionPreviewItems(Group $group, int $limit = 6): array
    {
        return Collection::query()
            ->with(['user.profile', 'group', 'coverArtwork'])
            ->where('group_id', $group->id)
            ->whereNull('deleted_at')
            ->latest('updated_at')
            ->limit($limit)
            ->get()
            ->map(fn (Collection $collection): array => $this->mapStudioCollectionItem($collection))
            ->values()
            ->all();
    }

    public function studioArtworkListing(Group $group, array $filters = []): array
    {
        $bucket = (string) ($filters['bucket'] ?? 'all');
        $search = trim((string) ($filters['q'] ?? ''));
        $page = max(1, (int) ($filters['page'] ?? 1));
        $perPage = min(max((int) ($filters['per_page'] ?? 24), 12), 48);

        $query = Artwork::query()
            ->with(['user.profile', 'primaryAuthor.profile'])
            ->where('group_id', $group->id)
            ->whereNull('deleted_at');

        if ($bucket === 'drafts') {
            $query->where('artwork_status', 'draft');
        } elseif ($bucket === 'scheduled') {
            $query->where('artwork_status', 'scheduled');
        } elseif ($bucket === 'published') {
            $query->where('artwork_status', 'published');
        }

        if ($search !== '') {
            $query->where(function ($builder) use ($search): void {
                $builder->where('title', 'like', '%' . $search . '%')
                    ->orWhere('description', 'like', '%' . $search . '%');
            });
        }

        $paginator = $query->latest('updated_at')->paginate($perPage, ['*'], 'page', $page);

        return $this->mapStudioListing(
            $paginator,
            fn (Artwork $artwork): array => $this->mapStudioArtworkItem($artwork),
            [
                ['value' => 'all', 'label' => 'All'],
                ['value' => 'published', 'label' => 'Published'],
                ['value' => 'drafts', 'label' => 'Drafts'],
                ['value' => 'scheduled', 'label' => 'Scheduled'],
            ],
            $bucket,
            $search
        );
    }

    public function studioCollectionListing(Group $group, array $filters = []): array
    {
        $bucket = (string) ($filters['bucket'] ?? 'all');
        $search = trim((string) ($filters['q'] ?? ''));
        $page = max(1, (int) ($filters['page'] ?? 1));
        $perPage = min(max((int) ($filters['per_page'] ?? 24), 12), 48);

        $query = Collection::query()
            ->with(['user.profile', 'group', 'coverArtwork'])
            ->where('group_id', $group->id)
            ->whereNull('deleted_at');

        if ($bucket === 'drafts') {
            $query->where(function ($builder): void {
                $builder->where('lifecycle_state', Collection::LIFECYCLE_DRAFT)
                    ->orWhere('workflow_state', Collection::WORKFLOW_DRAFT)
                    ->orWhere('workflow_state', Collection::WORKFLOW_IN_REVIEW);
            });
        } elseif ($bucket === 'scheduled') {
            $query->where('lifecycle_state', Collection::LIFECYCLE_SCHEDULED);
        } elseif ($bucket === 'published') {
            $query->whereIn('lifecycle_state', [Collection::LIFECYCLE_PUBLISHED, Collection::LIFECYCLE_FEATURED, Collection::LIFECYCLE_SCHEDULED]);
        }

        if ($search !== '') {
            $query->where(function ($builder) use ($search): void {
                $builder->where('title', 'like', '%' . $search . '%')
                    ->orWhere('description', 'like', '%' . $search . '%');
            });
        }

        $paginator = $query->latest('updated_at')->paginate($perPage, ['*'], 'page', $page);

        return $this->mapStudioListing(
            $paginator,
            fn (Collection $collection): array => $this->mapStudioCollectionItem($collection),
            [
                ['value' => 'all', 'label' => 'All'],
                ['value' => 'published', 'label' => 'Published'],
                ['value' => 'drafts', 'label' => 'Drafts'],
                ['value' => 'scheduled', 'label' => 'Scheduled'],
            ],
            $bucket,
            $search
        );
    }

    private function mapStudioListing(LengthAwarePaginator $paginator, callable $mapper, array $bucketOptions, string $bucket, string $search): array
    {
        return [
            'items' => collect($paginator->items())->map($mapper)->values()->all(),
            'meta' => [
                'current_page' => $paginator->currentPage(),
                'last_page' => $paginator->lastPage(),
                'per_page' => $paginator->perPage(),
                'total' => $paginator->total(),
            ],
            'filters' => [
                'bucket' => $bucket,
                'q' => $search,
                'sort' => 'updated_desc',
            ],
            'module_options' => [],
            'bucket_options' => $bucketOptions,
            'sort_options' => [
                ['value' => 'updated_desc', 'label' => 'Recently updated'],
            ],
            'advanced_filters' => [],
            'default_view' => 'grid',
        ];
    }

    private function mapStudioArtworkItem(Artwork $artwork): array
    {
        $status = (string) ($artwork->artwork_status ?: ($artwork->published_at ? 'published' : 'draft'));

        return [
            'id' => 'artworks:' . (int) $artwork->id,
            'numeric_id' => (int) $artwork->id,
            'module' => 'artworks',
            'module_label' => 'Artworks',
            'module_icon' => 'fa-solid fa-images',
            'title' => (string) $artwork->title,
            'subtitle' => $artwork->primaryAuthor?->name ?: $artwork->primaryAuthor?->username ?: $artwork->user?->name ?: $artwork->user?->username,
            'description' => $artwork->description,
            'status' => $status,
            'visibility' => (string) ($artwork->visibility ?: Artwork::VISIBILITY_PRIVATE),
            'image_url' => ThumbnailPresenter::present($artwork, 'md')['url'] ?? $artwork->thumbUrl('md'),
            'preview_url' => route('art.show', ['id' => $artwork->id, 'slug' => Str::slug((string) ($artwork->slug ?: $artwork->title)) ?: $artwork->id]),
            'view_url' => route('art.show', ['id' => $artwork->id, 'slug' => Str::slug((string) ($artwork->slug ?: $artwork->title)) ?: $artwork->id]),
            'edit_url' => route('studio.artworks.edit', ['id' => $artwork->id]),
            'manage_url' => route('studio.artworks.edit', ['id' => $artwork->id]),
            'analytics_url' => route('studio.artworks.analytics', ['id' => $artwork->id]),
            'created_at' => $artwork->created_at?->toISOString(),
            'updated_at' => $artwork->updated_at?->toISOString(),
            'published_at' => $artwork->published_at?->toISOString(),
            'metrics' => [
                'views' => (int) ($artwork->stats?->views ?? 0),
                'appreciation' => (int) ($artwork->stats?->favorites ?? 0),
                'comments' => (int) $artwork->comments()->count(),
            ],
            'actions' => [],
        ];
    }

    private function mapStudioCollectionItem(Collection $collection): array
    {
        $mapped = $this->collections->mapCollectionCardPayloads([$collection->loadMissing(['user.profile', 'group', 'coverArtwork'])], true)[0];
        $status = $mapped['lifecycle_state'] === Collection::LIFECYCLE_FEATURED ? 'published' : ($mapped['lifecycle_state'] ?? 'draft');

        return [
            'id' => 'collections:' . (int) $collection->id,
            'numeric_id' => (int) $collection->id,
            'module' => 'collections',
            'module_label' => 'Collections',
            'module_icon' => 'fa-solid fa-layer-group',
            'title' => (string) $mapped['title'],
            'subtitle' => $mapped['subtitle'] ?: ucfirst((string) ($mapped['type'] ?? 'collection')),
            'description' => $mapped['summary'] ?: $mapped['description'],
            'status' => $status,
            'visibility' => (string) $mapped['visibility'],
            'image_url' => $mapped['cover_image'],
            'preview_url' => $mapped['url'],
            'view_url' => $mapped['url'],
            'edit_url' => $mapped['edit_url'] ?: $mapped['manage_url'],
            'manage_url' => $mapped['manage_url'],
            'analytics_url' => route('settings.collections.analytics', ['collection' => $collection->id]),
            'created_at' => ($mapped['published_at'] ?? null) ?: ($mapped['updated_at'] ?? null),
            'updated_at' => $mapped['updated_at'] ?? null,
            'published_at' => $mapped['published_at'] ?? null,
            'metrics' => [
                'views' => (int) ($mapped['views_count'] ?? 0),
                'appreciation' => (int) (($mapped['likes_count'] ?? 0) + ($mapped['followers_count'] ?? 0)),
                'comments' => (int) ($mapped['comments_count'] ?? 0),
            ],
            'actions' => [],
        ];
    }

    private function normalizeLinks(mixed $links): array
    {
        $items = is_array($links) ? $links : [];

        return collect($items)
            ->filter(fn ($item): bool => is_array($item))
            ->map(function (array $item): array {
                return [
                    'label' => trim((string) ($item['label'] ?? '')),
                    'url' => trim((string) ($item['url'] ?? '')),
                ];
            })
            ->filter(fn (array $item): bool => $item['label'] !== '' && $item['url'] !== '')
            ->values()
            ->all();
    }

    private function normalizeMediaPath(mixed $path): ?string
    {
        $trimmed = trim((string) $path);

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

    private function normalizeFeaturedArtworkId(Group $group, mixed $featuredArtworkId): ?int
    {
        $id = (int) $featuredArtworkId;

        if ($id <= 0) {
            return null;
        }

        $exists = Artwork::query()
            ->where('id', $id)
            ->where('group_id', $group->id)
            ->whereNull('deleted_at')
            ->where('artwork_status', 'published')
            ->where('is_public', true)
            ->where('is_approved', true)
            ->whereNotNull('published_at')
            ->exists();

        return $exists ? $id : null;
    }
}