<?php

namespace App\Models;

use App\Enums\UserStatusEnum;
use App\Observers\UserObserver;
use App\Traits\VerifyEmailAndPhone;
use Cviebrock\EloquentSluggable\Sluggable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

#[ObservedBy([UserObserver::class])]
class User extends Authenticatable implements MustVerifyEmail
{
    /** @use HasFactory<\Database\Factories\UserFactory> */
    use HasApiTokens, HasFactory, Notifiable, Sluggable, VerifyEmailAndPhone;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'provider_id',
        'payment_user_id',
        'first_name',
        'last_name',
        'username',
        'email',
        'phone',
        'address',
        'avatar',
        'status',
        'city',
        'state',
        'country',
        'zip_code',
        'is_2fa_enabled',
        'is_kyc_verified',
        'email_verified_at',
        'phone_verified_at',
        'fcm_token',
        'password',
        'score',
        'coins',
    ];

    protected $appends = ['full_name', 'level', 'level_progress'];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, 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',
            'phone_verified_at' => 'datetime',
            'password' => 'hashed',
            'is_2fa_enabled' => 'boolean',
            'is_kyc_verified' => 'boolean',
            'status' => UserStatusEnum::class,
        ];
    }

    public function withdrawals()
    {
        return $this->hasMany(Withdraw::class, 'user_id');
    }

    public function payments()
    {
        return $this->hasMany(Payment::class);
    }

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

    public function fullName(): Attribute
    {
        return Attribute::make(
            get: fn () => $this->first_name.' '.($this->last_name ?? ''),
        );
    }

    public function getSearchAttribute()
    {
        return [
            'first_name',
            'last_name',
            'email',
            'phone',
            'username',
            'created_at',

        ];
    }

    public function sluggable(): array
    {
        return [
            'username' => [
                'source' => ['first_name', 'last_name'],
                'separator' => '-',
            ],
        ];
    }

    public function scopeActive($query)
    {
        return $query->where('status', UserStatusEnum::ACTIVE);
    }

    public function scopeInactive($query)
    {
        return $query->where('status', UserStatusEnum::INACTIVE);
    }

    public function scopeBanned($query)
    {
        return $query->where('status', UserStatusEnum::BANNED);
    }

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

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

    public function transactions(): MorphMany
    {
        return $this->morphMany(Transaction::class, 'holder');
    }

    public function notifications(): MorphMany
    {
        return $this->morphMany(Notification::class, 'notifiable');
    }

    public function supportTickets()
    {
        return $this->hasMany(SupportTicket::class, 'user_id');
    }

    public function kyc()
    {
        return $this->hasOne(KycSubmission::class);
    }

    public function favorites()
    {
        return $this->hasMany(Favorite::class, 'user_id');
    }

    public function quizzes()
    {
        return $this->hasMany(UserQuiz::class, 'user_id');
    }

    public function deposits()
    {
        return $this->hasMany(Deposit::class, 'user_id');
    }

    public function loginLogs()
    {
        return $this->hasMany(LoginLog::class, 'user_id');
    }

    public function badges(): BelongsToMany
    {
        return $this->belongsToMany(Badge::class, 'user_badges', 'user_id', 'badge_id')->withPivot('earned_at');
    }

    public function referrals()
    {
        return $this->hasMany(User::class, 'referer_id');
    }

    public function level(): Attribute
    {
        return Attribute::make(
            get: function () {
                $totalScore = $this->score;

                $levels = config('application_info.quiz_ranking_levels');

                foreach ($levels as $key => $levelData) {
                    if ($levelData['min_score'] <= $totalScore && $levelData['max_score'] >= $totalScore) {
                        return [
                            'position' => $key + 1,
                            'title' => $levelData['title'],
                        ];
                    }
                }

                // If no match found, return last level
                return $levels[count($levels) - 1]['title'] ?? '';
            }
        );
    }

    public function levelProgress(): Attribute
    {
        return Attribute::make(
            get: function () {
                $totalScore = $this->score;
                $levels = config('application_info.quiz_ranking_levels');
                $progress = 0;
                foreach ($levels as $levelData) {
                    if ($levelData['min_score'] <= $totalScore && $levelData['max_score'] >= $totalScore) {
                        $progress = ($totalScore - $levelData['min_score']) / ($levelData['max_score'] - $levelData['min_score']) * 100;
                        break;
                    }
                }

                return (int) round($progress);
            }
        );
    }
}
