<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use App\Models\Group;
use App\Models\GroupFollow;
use App\Models\GroupInvitation;
use App\Models\GroupMember;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\HasMany;
use App\Models\SocialAccount;
use App\Models\Conversation;
use App\Models\ConversationParticipant;
use App\Models\AcademyBadge;
use App\Models\AcademyCourseEnrollment;
use App\Models\AcademyChallengeSubmission;
use App\Models\AcademyEvent;
use App\Models\AcademyLike;
use App\Models\AcademyLessonProgress;
use App\Models\AcademySave;
use App\Models\AcademySavedPrompt;
use App\Models\AcademySearchLog;
use App\Models\AcademyUserProgress;
use App\Models\Message;
use App\Models\Notification;
use App\Models\Achievement;
use App\Models\UserAchievement;
use App\Models\UserActivity;
use App\Models\UserXpLog;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\DB;
use Laravel\Cashier\Billable;
use Laravel\Scout\Searchable;

class User extends Authenticatable
{
    private const EMAIL_LOGIN_UPGRADE_PLACEHOLDER_DOMAINS = [
        'users.skinbase.org',
        'legacy.skinbase.org',
    ];

    /** @use HasFactory<\Database\Factories\UserFactory> */
    use Billable, HasFactory, Notifiable, SoftDeletes;
    use Searchable {
        Searchable::bootSearchable as private bootScoutSearchable;
    }

    /**
     * The attributes that are mass assignable.
     *
     * @var list<string>
     */
    protected $fillable = [
        'username',
        'username_changed_at',
        'last_username_change_at',
        'onboarding_step',
        'name',
        'email',
        'last_verification_sent_at',
        'verification_send_count_24h',
        'verification_send_window_started_at',
        'is_active',
        'needs_password_reset',
        'cover_hash',
        'cover_ext',
        'cover_position',
        'trust_score',
        'bot_risk_score',
        'bot_flags',
        'last_bot_activity_at',
        'spam_reports',
        'approved_posts',
        'flagged_posts',
        'xp',
        'level',
        'rank',
        'password',
        'role',
        'nova_featured_creator',
        'allow_messages_from',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var list<string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'last_visit_at' => 'datetime',
            'last_verification_sent_at' => 'datetime',
            'verification_send_window_started_at' => 'datetime',
            'verification_send_count_24h' => 'integer',
            'username_changed_at' => 'datetime',
            'last_username_change_at' => 'datetime',
            'deleted_at' => 'datetime',
            'cover_position' => 'integer',
            'trust_score' => 'integer',
            'bot_risk_score' => 'integer',
            'bot_flags' => 'array',
            'last_bot_activity_at' => 'datetime',
            'spam_reports' => 'integer',
            'approved_posts' => 'integer',
            'flagged_posts' => 'integer',
            'xp' => 'integer',
            'level' => 'integer',
            'rank' => 'string',
            'nova_featured_creator' => 'boolean',
            'password' => 'hashed',
            'allow_messages_from' => 'string',
        ];
    }

    public function hasCompletedOnboarding(): bool
    {
        return strtolower(trim((string) ($this->onboarding_step ?? ''))) === 'complete';
    }

    public function requiresEmailLoginUpgrade(): bool
    {
        return ! $this->hasCompletedOnboarding()
            && self::isEmailLoginUpgradePlaceholder($this->email);
    }

    public static function isEmailLoginUpgradePlaceholder(?string $email): bool
    {
        $email = strtolower(trim((string) ($email ?? '')));

        if ($email === '') {
            return true;
        }

        $atPos = strrpos($email, '@');
        if ($atPos === false) {
            return true;
        }

        $domain = substr($email, $atPos + 1);

        return in_array($domain, self::EMAIL_LOGIN_UPGRADE_PLACEHOLDER_DOMAINS, true);
    }

    public function supportsUsernameLogin(): bool
    {
        return ! $this->hasCompletedOnboarding();
    }

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

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

    public function givenArtworkMedals(): HasMany
    {
        return $this->hasMany(ArtworkMedal::class, 'user_id');
    }

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

    public function ownedGroups(): HasMany
    {
        return $this->hasMany(Group::class, 'owner_user_id')->latest('updated_at');
    }

    public function groupMemberships(): HasMany
    {
        return $this->hasMany(GroupMember::class)->latest('updated_at');
    }

    public function groupInvitations(): HasMany
    {
        return $this->hasMany(GroupInvitation::class, 'invited_user_id')->latest('updated_at');
    }

    public function groupFollows(): HasMany
    {
        return $this->hasMany(GroupFollow::class)->latest('updated_at');
    }

    public function savedCollectionLists(): HasMany
    {
        return $this->hasMany(CollectionSavedList::class, 'user_id')->orderBy('title');
    }

    public function academyLessonProgress(): HasMany
    {
        return $this->hasMany(AcademyLessonProgress::class, 'user_id');
    }

    public function academyCourseEnrollments(): HasMany
    {
        return $this->hasMany(AcademyCourseEnrollment::class, 'user_id');
    }

    public function academySavedPrompts(): HasMany
    {
        return $this->hasMany(AcademySavedPrompt::class, 'user_id');
    }

    public function academyEvents(): HasMany
    {
        return $this->hasMany(AcademyEvent::class, 'user_id');
    }

    public function academyLikes(): HasMany
    {
        return $this->hasMany(AcademyLike::class, 'user_id');
    }

    public function academySaves(): HasMany
    {
        return $this->hasMany(AcademySave::class, 'user_id');
    }

    public function academyUserProgress(): HasMany
    {
        return $this->hasMany(AcademyUserProgress::class, 'user_id');
    }

    public function academySearchLogs(): HasMany
    {
        return $this->hasMany(AcademySearchLog::class, 'user_id');
    }

    public function academyChallengeSubmissions(): HasMany
    {
        return $this->hasMany(AcademyChallengeSubmission::class, 'user_id');
    }

    public function academyBadges(): BelongsToMany
    {
        return $this->belongsToMany(AcademyBadge::class, 'academy_user_badges', 'user_id', 'badge_id')
            ->withPivot('awarded_at')
            ->withTimestamps();
    }

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

    public function profile(): HasOne
    {
        return $this->hasOne(UserProfile::class, 'user_id');
    }

    public function dashboardPreference(): HasOne
    {
        return $this->hasOne(DashboardPreference::class, 'user_id');
    }

    public function country(): BelongsTo
    {
        return $this->belongsTo(Country::class);
    }

    public function statistics(): HasOne
    {
        return $this->hasOne(UserStatistic::class, 'user_id');
    }

    /** Users that follow this user */
    public function followers(): BelongsToMany
    {
        return $this->belongsToMany(
            User::class,
            'user_followers',
            'user_id',
            'follower_id'
        )->withPivot('created_at');
    }

    /** Users that this user follows */
    public function following(): BelongsToMany
    {
        return $this->belongsToMany(
            User::class,
            'user_followers',
            'follower_id',
            'user_id'
        )->withPivot('created_at');
    }

    public function profileComments(): HasMany
    {
        return $this->hasMany(ProfileComment::class, 'profile_user_id');
    }

    public function xpLogs(): HasMany
    {
        return $this->hasMany(UserXpLog::class, 'user_id');
    }

    public function userAchievements(): HasMany
    {
        return $this->hasMany(UserAchievement::class, 'user_id');
    }

    public function userActivities(): HasMany
    {
        return $this->hasMany(UserActivity::class, 'user_id');
    }

    public function worldRewardGrants(): HasMany
    {
        return $this->hasMany(WorldRewardGrant::class, 'user_id')->orderByDesc('granted_at')->orderByDesc('id');
    }

    public function achievements(): BelongsToMany
    {
        return $this->belongsToMany(Achievement::class, 'user_achievements', 'user_id', 'achievement_id')
            ->withPivot('unlocked_at');
    }

    // ── Messaging ────────────────────────────────────────────────────────────

    public function conversations(): BelongsToMany
    {
        return $this->belongsToMany(Conversation::class, 'conversation_participants')
            ->withPivot(['role', 'last_read_at', 'is_muted', 'is_archived', 'is_pinned', 'pinned_at', 'joined_at', 'left_at'])
            ->wherePivotNull('left_at')
            ->orderByPivot('joined_at', 'desc');
    }

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

    public function sentMessages(): HasMany
    {
        return $this->hasMany(Message::class, 'sender_id');
    }

    /**
     * Skinbase notifications are keyed by user_id (non-polymorphic table).
     */
    public function notifications(): HasMany
    {
        return $this->hasMany(Notification::class, 'user_id')->latest();
    }

    public function unreadNotifications(): HasMany
    {
        return $this->notifications()->whereNull('read_at');
    }

    /**
     * Check if this user allows receiving messages from the given user.
     */
    public function allowsMessagesFrom(User $sender): bool
    {
        $pref = $this->allow_messages_from ?? 'everyone';

        return match ($pref) {
            'everyone'         => true,
            'followers'        => $this->followers()->where('follower_id', $sender->id)->exists(),
            'mutual_followers' => $this->followers()->where('follower_id', $sender->id)->exists()
                                  && $this->following()->where('user_id', $sender->id)->exists(),
            'nobody'           => false,
            default            => true,
        };
    }

    // ────────────────────────────────────────────────────────────────────────

    /** Artworks this user has added to their favourites. */
    public function favouriteArtworks(): BelongsToMany
    {
        return $this->belongsToMany(Artwork::class, 'artwork_favourites', 'user_id', 'artwork_id')
            ->withPivot('legacy_id')
            ->withTimestamps();
    }

    public function hasRole(string $role): bool
    {
        return strtolower((string) ($this->role ?? '')) === strtolower($role);
    }

    private function hasLegacyPrivilegeFlag(string $attribute): bool
    {
        if (! array_key_exists($attribute, $this->getAttributes())) {
            return false;
        }

        return (bool) $this->getAttribute($attribute);
    }

    // ─── Follow helpers ───────────────────────────────────────────────────────

    /**
     * Whether $viewerId is following this user.
     * Uses a single indexed lookup – safe to call on every profile render.
     */
    public function isFollowedBy(int $viewerId): bool
    {
        if ($viewerId === $this->id) {
            return false;
        }

        return DB::table('user_followers')
            ->where('user_id', $this->id)
            ->where('follower_id', $viewerId)
            ->exists();
    }

    /**
     * Cached follower count from user_statistics.
     * Returns 0 if the statistics row does not exist yet.
     */
    public function getFollowersCountAttribute(): int
    {
        return (int) ($this->statistics?->followers_count ?? 0);
    }

    /**
     * Cached following count from user_statistics.
     */
    public function getFollowingCountAttribute(): int
    {
        return (int) ($this->statistics?->following_count ?? 0);
    }

    public function isAdmin(): bool
    {
        return $this->hasRole('admin') || $this->hasLegacyPrivilegeFlag('isAdmin');
    }

    public function isManager(): bool
    {
        return $this->hasRole('manager');
    }

    public function isEditorial(): bool
    {
        return $this->hasRole('editorial');
    }

    /**
     * Returns true for any role that grants access to the /admin panel
     * (admin, manager, editorial).
     */
    public function hasStaffAccess(): bool
    {
        return $this->isAdmin() || $this->isManager() || $this->isEditorial();
    }

    public function isModerator(): bool
    {
        return $this->hasRole('moderator') || $this->hasLegacyPrivilegeFlag('isModerator');
    }

    public function hasAcademyCreatorAccess(): bool
    {
        return in_array(app(\App\Services\Academy\AcademyAccessService::class)->currentTier($this), ['creator', 'pro', 'admin'], true);
    }

    public function hasAcademyProAccess(): bool
    {
        return in_array(app(\App\Services\Academy\AcademyAccessService::class)->currentTier($this), ['pro', 'admin'], true);
    }

    public function canAccessAcademyContent(object|array $content): bool
    {
        $accessLevel = is_array($content)
            ? (string) ($content['access_level'] ?? 'free')
            : (string) ($content->access_level ?? 'free');

        return app(\App\Services\Academy\AcademyAccessService::class)->canAccessContent($this, $accessLevel);
    }

    public function posts(): HasMany
    {
        return $this->hasMany(Post::class)->orderByDesc('created_at');
    }

    public function stories(): HasMany
    {
        return $this->hasMany(Story::class, 'creator_id')->orderByDesc('published_at');
    }

    // ─── Meilisearch ──────────────────────────────────────────────────────────

    /**
     * User indexing is handled explicitly through IndexUserJob so unrelated
     * model writes do not enqueue Scout sync jobs implicitly.
     */
    protected static function bootSearchable(): void
    {
        // Register the SearchableScope so that the Builder::searchable() macro
        // is available (needed for scout:import and manual ::searchable() calls).
        // We intentionally skip static::observe(new ModelObserver) so that
        // User saves/deletes do not auto-enqueue Scout sync jobs.
        static::addGlobalScope(new \Laravel\Scout\SearchableScope);

        $whenBootedCallback = function () {
            (new static)->registerSearchableMacros();
        };

        if (method_exists(static::class, 'whenBooted')) {
            static::whenBooted($whenBootedCallback);
        } else {
            $whenBootedCallback();
        }
    }

    /**
     * Only index active users (not soft-deleted, is_active = true).
     */
    public function shouldBeSearchable(): bool
    {
        return (bool) $this->is_active && ! $this->trashed();
    }

    /**
     * Data indexed in Meilisearch.
     * Includes all v2 stat counters for top-creator sorting.
     */
    public function toSearchableArray(): array
    {
        $stats = $this->statistics;

        return [
            'id'                           => $this->id,
            'username'                     => strtolower((string) ($this->username ?? '')),
            'name'                         => $this->name,
            // Upload activity
            'uploads_count'                => (int) ($stats?->uploads_count ?? 0),
            // Creator-received metrics
            'downloads_received_count'     => (int) ($stats?->downloads_received_count ?? 0),
            'artwork_views_received_count' => (int) ($stats?->artwork_views_received_count ?? 0),
            'awards_received_count'        => (int) ($stats?->awards_received_count ?? 0),
            'favorites_received_count'     => (int) ($stats?->favorites_received_count ?? 0),
            'comments_received_count'      => (int) ($stats?->comments_received_count ?? 0),
            'reactions_received_count'     => (int) ($stats?->reactions_received_count ?? 0),
            // Social
            'followers_count'              => (int) ($stats?->followers_count ?? 0),
            'following_count'              => (int) ($stats?->following_count ?? 0),
            'created_at'                   => $this->created_at?->toISOString(),
        ];
    }
}
