<?php

declare(strict_types=1);

namespace App\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Laravel\Scout\Searchable;

class Group extends Model
{
    use HasFactory;
    use SoftDeletes;
    use Searchable;

    public const VISIBILITY_PUBLIC = 'public';
    public const VISIBILITY_PRIVATE = 'private';
    public const VISIBILITY_UNLISTED = 'unlisted';

    public const LIFECYCLE_ACTIVE = 'active';
    public const LIFECYCLE_ARCHIVED = 'archived';
    public const LIFECYCLE_SUSPENDED = 'suspended';

    public const MEMBERSHIP_INVITE_ONLY = 'invite_only';
    public const MEMBERSHIP_REQUEST_TO_JOIN = 'request_to_join';
    public const MEMBERSHIP_OPEN = 'open';

    public const ROLE_OWNER = 'owner';
    public const ROLE_ADMIN = 'admin';
    public const ROLE_EDITOR = 'editor';
    public const ROLE_MEMBER = 'member';
    public const ROLE_CONTRIBUTOR = 'contributor';

    public const PERMISSION_REVIEW_JOIN_REQUESTS = 'review_join_requests';
    public const PERMISSION_REVIEW_SUBMISSIONS = 'review_submissions';
    public const PERMISSION_MANAGE_RECRUITMENT = 'manage_recruitment';
    public const PERMISSION_MANAGE_POSTS = 'manage_posts';
    public const PERMISSION_PUBLISH_POSTS = 'publish_posts';
    public const PERMISSION_PIN_POSTS = 'pin_posts';
    public const PERMISSION_MANAGE_MEMBER_PERMISSIONS = 'manage_member_permissions';
    public const PERMISSION_MANAGE_EVENTS = 'manage_events';
    public const PERMISSION_MANAGE_CHALLENGES = 'manage_challenges';
    public const PERMISSION_MANAGE_PROJECTS = 'manage_projects';
    public const PERMISSION_MANAGE_RELEASES = 'manage_releases';
    public const PERMISSION_PUBLISH_RELEASES = 'publish_releases';
    public const PERMISSION_MANAGE_MILESTONES = 'manage_milestones';
    public const PERMISSION_VIEW_REPUTATION_DASHBOARD = 'view_reputation_dashboard';
    public const PERMISSION_MANAGE_BADGES = 'manage_badges';
    public const PERMISSION_VIEW_INTERNAL_TRUST_METRICS = 'view_internal_trust_metrics';
    public const PERMISSION_FEATURE_RELEASES = 'feature_releases';
    public const PERMISSION_ASSIGN_RELEASE_LEAD = 'assign_release_lead';
    public const PERMISSION_MANAGE_ASSETS = 'manage_assets';
    public const PERMISSION_FEATURE_CHALLENGE_ENTRIES = 'feature_challenge_entries';
    public const PERMISSION_PUBLISH_EVENT_UPDATES = 'publish_event_updates';
    public const PERMISSION_ATTACH_ASSETS_TO_PROJECTS = 'attach_assets_to_projects';
    public const PERMISSION_VIEW_INTERNAL_ASSETS = 'view_internal_assets';
    public const PERMISSION_MANAGE_ACTIVITY_PINS = 'manage_activity_pins';

    public const STATUS_PENDING = 'pending';
    public const STATUS_ACTIVE = 'active';
    public const STATUS_REVOKED = 'revoked';

    protected $fillable = [
        'owner_user_id',
        'featured_artwork_id',
        'is_verified',
        'founded_at',
        'name',
        'slug',
        'headline',
        'bio',
        'type',
        'visibility',
        'status',
        'membership_policy',
        'website_url',
        'links_json',
        'avatar_path',
        'banner_path',
        'artworks_count',
        'collections_count',
        'followers_count',
        'last_activity_at',
    ];

    protected $casts = [
        'links_json' => 'array',
        'is_verified' => 'boolean',
        'artworks_count' => 'integer',
        'collections_count' => 'integer',
        'followers_count' => 'integer',
        'founded_at' => 'datetime',
        'last_activity_at' => 'datetime',
    ];

    public function owner(): BelongsTo
    {
        return $this->belongsTo(User::class, 'owner_user_id');
    }

    public function getRouteKeyName(): string
    {
        return 'slug';
    }

    public function members(): HasMany
    {
        return $this->hasMany(GroupMember::class);
    }

    public function invitations(): HasMany
    {
        return $this->hasMany(GroupInvitation::class);
    }

    public function follows(): HasMany
    {
        return $this->hasMany(GroupFollow::class);
    }

    public function artworks(): HasMany
    {
        return $this->hasMany(Artwork::class);
    }

    public function collections(): HasMany
    {
        return $this->hasMany(Collection::class);
    }

    public function joinRequests(): HasMany
    {
        return $this->hasMany(GroupJoinRequest::class);
    }

    public function posts(): HasMany
    {
        return $this->hasMany(GroupPost::class);
    }

    public function recruitmentProfile(): HasOne
    {
        return $this->hasOne(GroupRecruitmentProfile::class);
    }

    public function projects(): HasMany
    {
        return $this->hasMany(GroupProject::class);
    }

    public function releases(): HasMany
    {
        return $this->hasMany(GroupRelease::class);
    }

    public function challenges(): HasMany
    {
        return $this->hasMany(GroupChallenge::class);
    }

    public function events(): HasMany
    {
        return $this->hasMany(GroupEvent::class);
    }

    public function assets(): HasMany
    {
        return $this->hasMany(GroupAsset::class);
    }

    public function activityItems(): HasMany
    {
        return $this->hasMany(GroupActivityItem::class);
    }

    public function contributorStats(): HasMany
    {
        return $this->hasMany(GroupContributorStat::class);
    }

    public function badges(): HasMany
    {
        return $this->hasMany(GroupBadge::class);
    }

    public function memberBadges(): HasMany
    {
        return $this->hasMany(GroupMemberBadge::class);
    }

    public function discoveryMetric(): HasOne
    {
        return $this->hasOne(GroupDiscoveryMetric::class);
    }

    public function historyEntries(): HasMany
    {
        return $this->hasMany(GroupHistory::class);
    }

    public function scopePublic(Builder $query): Builder
    {
        return $query
            ->where('visibility', self::VISIBILITY_PUBLIC)
            ->where('status', self::LIFECYCLE_ACTIVE);
    }

    public static function acceptedVisibilityValues(): array
    {
        return [self::VISIBILITY_PUBLIC, self::VISIBILITY_PRIVATE, self::VISIBILITY_UNLISTED];
    }

    public static function acceptedMembershipPolicies(): array
    {
        return [self::MEMBERSHIP_INVITE_ONLY, self::MEMBERSHIP_REQUEST_TO_JOIN, self::MEMBERSHIP_OPEN];
    }

    public static function normalizeMemberRole(string $role): string
    {
        $normalized = strtolower(trim($role));

        return $normalized === self::ROLE_CONTRIBUTOR ? self::ROLE_MEMBER : $normalized;
    }

    public static function displayRole(?string $role): ?string
    {
        if ($role === null) {
            return null;
        }

        return self::normalizeMemberRole($role) === self::ROLE_MEMBER ? self::ROLE_CONTRIBUTOR : self::normalizeMemberRole($role);
    }

    public function isOwnedBy(User|int|null $user): bool
    {
        $userId = $user instanceof User ? $user->id : $user;

        return $userId !== null && (int) $userId === (int) $this->owner_user_id;
    }

    public function isPubliclyVisible(): bool
    {
        return in_array($this->visibility, [self::VISIBILITY_PUBLIC, self::VISIBILITY_UNLISTED], true)
            && $this->status !== self::LIFECYCLE_SUSPENDED;
    }

    public function isOperational(): bool
    {
        return $this->status === self::LIFECYCLE_ACTIVE;
    }

    public function canArchive(User $user): bool
    {
        return $this->isOwnedBy($user);
    }

    public function canViewStudio(User $user): bool
    {
        if ($this->status === self::LIFECYCLE_SUSPENDED) {
            return false;
        }

        return $this->hasActiveMember($user);
    }

    public function activeRoleFor(User|int|null $user): ?string
    {
        $userId = $user instanceof User ? $user->id : $user;

        if ($userId === null) {
            return null;
        }

        if ($this->isOwnedBy($userId)) {
            return self::ROLE_OWNER;
        }

        $members = $this->relationLoaded('members')
            ? $this->members
            : $this->members()->where('status', self::STATUS_ACTIVE)->get();

        return $members
            ->first(fn (GroupMember $member): bool => (int) $member->user_id === (int) $userId && $member->status === self::STATUS_ACTIVE)
            ?->role;
    }

    public function hasActiveMember(User|int|null $user): bool
    {
        return $this->activeRoleFor($user) !== null;
    }

    public function activeMembershipFor(User|int|null $user): ?GroupMember
    {
        $userId = $user instanceof User ? $user->id : $user;

        if ($userId === null || $this->isOwnedBy($userId)) {
            return null;
        }

        $members = $this->relationLoaded('members')
            ? $this->members
            : $this->members()->where('status', self::STATUS_ACTIVE)->get();

        return $members->first(
            fn (GroupMember $member): bool => (int) $member->user_id === (int) $userId && $member->status === self::STATUS_ACTIVE
        );
    }

    public function permissionOverridesFor(User|int|null $user): array
    {
        if ($this->isOwnedBy($user)) {
            return collect(self::allowedPermissionOverrides())
                ->mapWithKeys(fn (string $permission): array => [$permission => true])
                ->all();
        }

        $member = $this->activeMembershipFor($user);
        if (! $member) {
            return [];
        }

        return collect($member->permission_overrides_json ?? [])
            ->mapWithKeys(function ($override): array {
                if (is_array($override)) {
                    $key = trim((string) ($override['key'] ?? ''));
                    if ($key === '' || ! in_array($key, self::allowedPermissionOverrides(), true)) {
                        return [];
                    }

                    return [$key => (bool) ($override['is_allowed'] ?? false)];
                }

                $key = trim((string) $override);
                if ($key === '' || ! in_array($key, self::allowedPermissionOverrides(), true)) {
                    return [];
                }

                return [$key => true];
            })
            ->all();
    }

    public function hasPermission(User|int|null $user, string $permission): bool
    {
        return $this->permissionOverridesFor($user)[$permission] ?? false;
    }

    public function hasDeniedPermission(User|int|null $user, string $permission): bool
    {
        $overrides = $this->permissionOverridesFor($user);

        return array_key_exists($permission, $overrides) && $overrides[$permission] === false;
    }

    public static function permissionKeys(): array
    {
        return self::allowedPermissionOverrides();
    }

    public function canBeViewedBy(?User $user): bool
    {
        if ($this->isPubliclyVisible()) {
            return true;
        }

        return $user !== null && $this->hasActiveMember($user);
    }

    public function canManage(User $user): bool
    {
        return in_array($this->activeRoleFor($user), [self::ROLE_OWNER, self::ROLE_ADMIN], true);
    }

    public function canManageMembers(User $user): bool
    {
        return in_array($this->activeRoleFor($user), [self::ROLE_OWNER, self::ROLE_ADMIN], true);
    }

    public function canPublishArtworks(User $user): bool
    {
        return $this->isOperational()
            && in_array($this->activeRoleFor($user), [self::ROLE_OWNER, self::ROLE_ADMIN, self::ROLE_EDITOR], true);
    }

    public function canCreateArtworkDrafts(User $user): bool
    {
        return $this->isOperational() && $this->hasActiveMember($user);
    }

    public function canSubmitArtworkForReview(User $user): bool
    {
        return $this->isOperational() && $this->hasActiveMember($user);
    }

    public function canReviewSubmissions(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_REVIEW_SUBMISSIONS)) {
            return false;
        }

        $role = $this->activeRoleFor($user);

        return in_array($role, [self::ROLE_OWNER, self::ROLE_ADMIN, self::ROLE_EDITOR], true)
            || $this->hasPermission($user, self::PERMISSION_REVIEW_SUBMISSIONS);
    }

    public function canRequestJoin(?User $user): bool
    {
        if (! $this->isOperational() || $user === null || $this->hasActiveMember($user)) {
            return false;
        }

        return in_array($this->membership_policy, [self::MEMBERSHIP_REQUEST_TO_JOIN, self::MEMBERSHIP_OPEN], true);
    }

    public function canReviewJoinRequests(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_REVIEW_JOIN_REQUESTS)) {
            return false;
        }

        $role = $this->activeRoleFor($user);

        return in_array($role, [self::ROLE_OWNER, self::ROLE_ADMIN], true)
            || $this->hasPermission($user, self::PERMISSION_REVIEW_JOIN_REQUESTS);
    }

    public function canManageRecruitment(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_MANAGE_RECRUITMENT)) {
            return false;
        }

        $role = $this->activeRoleFor($user);

        return in_array($role, [self::ROLE_OWNER, self::ROLE_ADMIN, self::ROLE_EDITOR], true)
            || $this->hasPermission($user, self::PERMISSION_MANAGE_RECRUITMENT);
    }

    public function canManagePosts(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_MANAGE_POSTS)) {
            return false;
        }

        $role = $this->activeRoleFor($user);

        return in_array($role, [self::ROLE_OWNER, self::ROLE_ADMIN, self::ROLE_EDITOR], true)
            || $this->hasPermission($user, self::PERMISSION_MANAGE_POSTS);
    }

    public function canPublishPosts(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_PUBLISH_POSTS)) {
            return false;
        }

        $role = $this->activeRoleFor($user);

        return in_array($role, [self::ROLE_OWNER, self::ROLE_ADMIN, self::ROLE_EDITOR], true)
            || $this->hasPermission($user, self::PERMISSION_PUBLISH_POSTS)
            || $this->canManagePosts($user);
    }

    public function canPinPosts(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_PIN_POSTS)) {
            return false;
        }

        return in_array($this->activeRoleFor($user), [self::ROLE_OWNER, self::ROLE_ADMIN], true)
            || $this->hasPermission($user, self::PERMISSION_PIN_POSTS);
    }

    public function canManageMemberPermissions(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_MANAGE_MEMBER_PERMISSIONS)) {
            return false;
        }

        return in_array($this->activeRoleFor($user), [self::ROLE_OWNER, self::ROLE_ADMIN], true)
            || $this->hasPermission($user, self::PERMISSION_MANAGE_MEMBER_PERMISSIONS);
    }

    public function canManageEvents(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_MANAGE_EVENTS)) {
            return false;
        }

        return in_array($this->activeRoleFor($user), [self::ROLE_OWNER, self::ROLE_ADMIN, self::ROLE_EDITOR], true)
            || $this->hasPermission($user, self::PERMISSION_MANAGE_EVENTS);
    }

    public function canManageChallenges(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_MANAGE_CHALLENGES)) {
            return false;
        }

        return in_array($this->activeRoleFor($user), [self::ROLE_OWNER, self::ROLE_ADMIN, self::ROLE_EDITOR], true)
            || $this->hasPermission($user, self::PERMISSION_MANAGE_CHALLENGES);
    }

    public function canManageProjects(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_MANAGE_PROJECTS)) {
            return false;
        }

        return in_array($this->activeRoleFor($user), [self::ROLE_OWNER, self::ROLE_ADMIN, self::ROLE_EDITOR], true)
            || $this->hasPermission($user, self::PERMISSION_MANAGE_PROJECTS);
    }

    public function canManageReleases(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_MANAGE_RELEASES)) {
            return false;
        }

        return $this->canManageProjects($user)
            || $this->hasPermission($user, self::PERMISSION_MANAGE_RELEASES);
    }

    public function canPublishReleases(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_PUBLISH_RELEASES)) {
            return false;
        }

        return in_array($this->activeRoleFor($user), [self::ROLE_OWNER, self::ROLE_ADMIN], true)
            || $this->hasPermission($user, self::PERMISSION_PUBLISH_RELEASES)
            || $this->canManageReleases($user);
    }

    public function canManageMilestones(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_MANAGE_MILESTONES)) {
            return false;
        }

        return $this->canManageProjects($user)
            || $this->canManageReleases($user)
            || $this->hasPermission($user, self::PERMISSION_MANAGE_MILESTONES);
    }

    public function canViewReputationDashboard(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_VIEW_REPUTATION_DASHBOARD)) {
            return false;
        }

        return in_array($this->activeRoleFor($user), [self::ROLE_OWNER, self::ROLE_ADMIN], true)
            || $this->hasPermission($user, self::PERMISSION_VIEW_REPUTATION_DASHBOARD);
    }

    public function canManageBadges(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_MANAGE_BADGES)) {
            return false;
        }

        return in_array($this->activeRoleFor($user), [self::ROLE_OWNER, self::ROLE_ADMIN], true)
            || $this->hasPermission($user, self::PERMISSION_MANAGE_BADGES);
    }

    public function canViewInternalTrustMetrics(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_VIEW_INTERNAL_TRUST_METRICS)) {
            return false;
        }

        return in_array($this->activeRoleFor($user), [self::ROLE_OWNER, self::ROLE_ADMIN], true)
            || $this->hasPermission($user, self::PERMISSION_VIEW_INTERNAL_TRUST_METRICS);
    }

    public function canFeatureReleases(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_FEATURE_RELEASES)) {
            return false;
        }

        return in_array($this->activeRoleFor($user), [self::ROLE_OWNER, self::ROLE_ADMIN], true)
            || $this->hasPermission($user, self::PERMISSION_FEATURE_RELEASES);
    }

    public function canAssignReleaseLead(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_ASSIGN_RELEASE_LEAD)) {
            return false;
        }

        return $this->canManageReleases($user)
            || $this->hasPermission($user, self::PERMISSION_ASSIGN_RELEASE_LEAD);
    }

    public function canManageAssets(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_MANAGE_ASSETS)) {
            return false;
        }

        return in_array($this->activeRoleFor($user), [self::ROLE_OWNER, self::ROLE_ADMIN, self::ROLE_EDITOR], true)
            || $this->hasPermission($user, self::PERMISSION_MANAGE_ASSETS);
    }

    public function canFeatureChallengeEntries(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_FEATURE_CHALLENGE_ENTRIES)) {
            return false;
        }

        return in_array($this->activeRoleFor($user), [self::ROLE_OWNER, self::ROLE_ADMIN, self::ROLE_EDITOR], true)
            || $this->hasPermission($user, self::PERMISSION_FEATURE_CHALLENGE_ENTRIES);
    }

    public function canPublishEventUpdates(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_PUBLISH_EVENT_UPDATES)) {
            return false;
        }

        return $this->canManageEvents($user)
            || $this->hasPermission($user, self::PERMISSION_PUBLISH_EVENT_UPDATES);
    }

    public function canAttachAssetsToProjects(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_ATTACH_ASSETS_TO_PROJECTS)) {
            return false;
        }

        return $this->canManageProjects($user)
            || $this->hasPermission($user, self::PERMISSION_ATTACH_ASSETS_TO_PROJECTS);
    }

    public function canViewInternalAssets(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_VIEW_INTERNAL_ASSETS)) {
            return false;
        }

        return in_array($this->activeRoleFor($user), [self::ROLE_OWNER, self::ROLE_ADMIN, self::ROLE_EDITOR], true)
            || $this->hasPermission($user, self::PERMISSION_VIEW_INTERNAL_ASSETS);
    }

    public function canPinActivity(User $user): bool
    {
        if (! $this->isOperational()) {
            return false;
        }

        if ($this->hasDeniedPermission($user, self::PERMISSION_MANAGE_ACTIVITY_PINS)) {
            return false;
        }

        return in_array($this->activeRoleFor($user), [self::ROLE_OWNER, self::ROLE_ADMIN], true)
            || $this->hasPermission($user, self::PERMISSION_MANAGE_ACTIVITY_PINS);
    }

    public static function allowedPermissionOverrides(): array
    {
        return [
            self::PERMISSION_REVIEW_JOIN_REQUESTS,
            self::PERMISSION_REVIEW_SUBMISSIONS,
            self::PERMISSION_MANAGE_RECRUITMENT,
            self::PERMISSION_MANAGE_POSTS,
            self::PERMISSION_PUBLISH_POSTS,
            self::PERMISSION_PIN_POSTS,
            self::PERMISSION_MANAGE_MEMBER_PERMISSIONS,
            self::PERMISSION_MANAGE_EVENTS,
            self::PERMISSION_MANAGE_CHALLENGES,
            self::PERMISSION_MANAGE_PROJECTS,
            self::PERMISSION_MANAGE_RELEASES,
            self::PERMISSION_PUBLISH_RELEASES,
            self::PERMISSION_MANAGE_MILESTONES,
            self::PERMISSION_VIEW_REPUTATION_DASHBOARD,
            self::PERMISSION_MANAGE_BADGES,
            self::PERMISSION_VIEW_INTERNAL_TRUST_METRICS,
            self::PERMISSION_FEATURE_RELEASES,
            self::PERMISSION_ASSIGN_RELEASE_LEAD,
            self::PERMISSION_MANAGE_ASSETS,
            self::PERMISSION_FEATURE_CHALLENGE_ENTRIES,
            self::PERMISSION_PUBLISH_EVENT_UPDATES,
            self::PERMISSION_ATTACH_ASSETS_TO_PROJECTS,
            self::PERMISSION_VIEW_INTERNAL_ASSETS,
            self::PERMISSION_MANAGE_ACTIVITY_PINS,
        ];
    }

    public function canManageCollections(User $user): bool
    {
        return $this->isOperational() && $this->canPublishArtworks($user);
    }

    public function avatarUrl(): ?string
    {
        return $this->assetUrl($this->avatar_path);
    }

    public function bannerUrl(): ?string
    {
        return $this->assetUrl($this->banner_path);
    }

    public function publicUrl(): string
    {
        return route('groups.show', ['group' => $this->slug]);
    }

    public function shouldBeSearchable(): bool
    {
        return $this->visibility === self::VISIBILITY_PUBLIC && $this->status === self::LIFECYCLE_ACTIVE;
    }

    public function toSearchableArray(): array
    {
        $recruitment = $this->relationLoaded('recruitmentProfile')
            ? $this->recruitmentProfile
            : $this->recruitmentProfile()->first();

        $memberNames = $this->members()
            ->with('user:id,name,username')
            ->where('status', self::STATUS_ACTIVE)
            ->limit(12)
            ->get()
            ->map(fn (GroupMember $member): string => (string) ($member->user?->name ?: $member->user?->username ?: ''))
            ->filter()
            ->values()
            ->all();

        return [
            'id' => (int) $this->id,
            'name' => (string) $this->name,
            'slug' => (string) $this->slug,
            'headline' => (string) ($this->headline ?? ''),
            'bio' => (string) ($this->bio ?? ''),
            'type' => (string) ($this->type ?? ''),
            'visibility' => (string) $this->visibility,
            'status' => (string) ($this->status ?? self::LIFECYCLE_ACTIVE),
            'artworks_count' => (int) ($this->artworks_count ?? 0),
            'followers_count' => (int) ($this->followers_count ?? 0),
            'is_recruiting' => (bool) ($recruitment?->is_recruiting ?? false),
            'recruitment_headline' => (string) ($recruitment?->headline ?? ''),
            'recruitment_roles' => array_values(array_filter($recruitment?->roles_json ?? [])),
            'recruitment_skills' => array_values(array_filter($recruitment?->skills_json ?? [])),
            'release_titles' => $this->releases()->where('visibility', GroupRelease::VISIBILITY_PUBLIC)->latest('published_at')->limit(6)->pluck('title')->filter()->values()->all(),
            'project_titles' => $this->projects()->where('visibility', GroupProject::VISIBILITY_PUBLIC)->latest('updated_at')->limit(6)->pluck('title')->filter()->values()->all(),
            'challenge_titles' => $this->challenges()->where('visibility', GroupChallenge::VISIBILITY_PUBLIC)->latest('updated_at')->limit(6)->pluck('title')->filter()->values()->all(),
            'event_titles' => $this->events()->where('visibility', GroupEvent::VISIBILITY_PUBLIC)->latest('start_at')->limit(6)->pluck('title')->filter()->values()->all(),
            'badge_keys' => $this->badges()->latest('awarded_at')->limit(6)->pluck('badge_key')->filter()->values()->all(),
            'member_names' => $memberNames,
        ];
    }

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

        if ($trimmed === '') {
            return null;
        }

        if (str_starts_with($trimmed, 'http://') || str_starts_with($trimmed, 'https://')) {
            return $trimmed;
        }

        return rtrim((string) config('cdn.files_url', 'https://files.skinbase.org'), '/') . '/' . ltrim($trimmed, '/');
    }
}