<?php

namespace App\Models;

use App\Enum\Status;
use App\Traits\HasStatus;
use App\Models\Buyer\Buyer;
use Laravel\Sanctum\HasApiTokens;
use Spatie\Permission\Traits\HasRoles;
use Illuminate\Support\Facades\Storage;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\Notifications\SendOtpEmailNotification;
use App\Notifications\ResetPasswordNotification;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use App\Notifications\SendEmailInvitationNotification;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    /** @use HasFactory<\Database\Factories\UserFactory> */
    use HasFactory, Notifiable, HasRoles, HasApiTokens, SoftDeletes, HasStatus;
    public const GENDER_MALE = 'male';
    public const GENDER_FEMALE = 'female';

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        // IDs
        'uuid',

        // Account info
        'first_name',
        'last_name',
        'email',
        'phone',
        'address',
        'password',
        'avatar',

        // Personal info
        'gender',

        // System info
        'status',
        'admin_comments',
        'invite',

        // timestamps
        'email_verified_at',
        'updated_at',
    ];

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

    protected $casts = [
        'status' => Status::class,
    ];

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'password' => 'hashed',
            'dob' => 'date',
        ];
    }

    /**
     * The "booting" method of the model.
     *
     * @return void
     */
    protected static function boot(): void
    {
        parent::boot();

        // auto-sets values on creation
        static::creating(function ($query) {
            $query->uuid = !empty($query->uuid) ? $query->uuid : getUuid();
        });
    }

    /**
     * Get the route key for the model for route-model-binding.
     *
     * @return string
     */
    public function getRouteKeyName()
    {
        return 'uuid';
    }

    /**
     * Get the activity logs of the user.
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany<\App\Models\ActivityLog>
     */
    public function model()
    {
        return $this->morphTo();
    }
    public function activityLogs(): HasMany
    {
        return $this->hasMany(ActivityLog::class);
    }

    public function users(): HasMany
    {
        return $this->hasMany(User::class, 'user_id', 'id');
    }

    public function parentUser(): BelongsTo
    {
        return $this->belongsTo(User::class, 'user_id', 'id');
    }

    /**
     * Get the user's system settings.
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function settings(): HasMany
    {
        return $this->hasMany(Setting::class);
    }

    /**
     * Get the user's devices.
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function devices(): HasMany
    {
        return $this->hasMany(UserDevice::class);
    }

    /**
     * Get the user's listings.
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function listings(): HasMany
    {
        return $this->hasMany(Listing::class, 'owner_id');
    }

    /**
     * Determine if the user has the 'super_admin' role.
     *
     * @return bool
     */
    public function isSuperAdmin(): bool
    {
        return $this->hasRole(Role::SUPER_ADMIN);
    }

    /**
     * Determine if the user has the 'admin' role.
     *
     * @return bool
     */
    public function isAdmin(): bool
    {
        return $this->hasRole(Role::ADMIN);
    }

    /**
     * Determine if the user has the 'dealer' role.
     *
     * @return bool
     */
    public function isDealer(): bool
    {
        return $this->hasRole(Role::DEALER);
    }

    /**
     * Determine if the user has the 'dealer' role.
     *
     * @return bool
     */
    public function isBuyer(): bool
    {
        return $this->hasRole(Role::BUYER);
    }



    /**
     * Determine if the user is a user.
     *
     * @return bool
     */
    public function isUser(): bool
    {
        return $this->hasRole(Role::USER);
    }

    /**
     * Scope a query to only include users with the 'super_admin' role.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeHasRoleSuperAdmin(): Builder
    {
        return $this->whereHas('roles', function ($query) {
            $query->where('name', Role::SUPER_ADMIN);
        });
    }

    /**
     * Scope a query to only include users with the 'admin' role.
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeHasRoleAdmin(): Builder
    {
        return $this->whereHas('roles', function ($query) {
            $query->where('name', Role::ADMIN);
        });
    }

    /**
     * Scope a query to only include users with the 'dealer' role.
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeHasRoleDealer(): Builder
    {
        return $this->whereHas('roles', function ($query) {
            $query->where('name', Role::DEALER);
        });
    }

    /**
     * Scope a query to only include users with the 'user' role.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeHasRoleUser(): Builder
    {
        return $this->whereHas('roles', function ($query) {
            $query->where('name', Role::USER);
        });
    }

    /**
     * Scope a query to only include users with an active status.
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeStatusActive(): Builder
    {
        return $this->where('status', Status::ACTIVE);
    }

    /**
     * Scope a query to only include users with a pending status.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeStatusPending(): Builder
    {
        return $this->where('status', Status::PENDING);
    }

    /**
     * Scope a query to only include verified models.
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeStatusVerified(): Builder
    {
        return $this->where('status', Status::VERIFIED);
    }

    /**
     * Scope a query to only include invited models.
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeStatusInvited(): Builder
    {
        return $this->where('status', Status::INVITED);
    }

    /**
     * Check if the user's status is invited.
     *
     * @return bool
     */
    public function isInvited(): bool
    {
        return $this->status === Status::INVITED;
    }

    /**
     * Check if the user's status is active.
     *
     * @return bool
     */

    public function isActive(): bool
    {
        return $this->status === Status::ACTIVE;
    }

    /**
     * Check if the user's status is suspended.
     *
     * @return bool
     */
    public function isSuspended(): bool
    {
        return $this->status === Status::SUSPENDED;
    }

    /**
     * Determine if the user's email is verified.
     *
     * @return bool
     */
    public function isVerified(): bool
    {
        return !empty($this->email_verified_at);
    }

    /**
     * Get the user's full name.
     *
     * @return string
     */
    public function getFullNameAttribute(): string
    {
        return ucwords("{$this->first_name} {$this->last_name}");
    }

    /**
     * Get the URL of the user's avatar thumbnail.
     *
     * @return string|null
     */
    public function getThumbnailAvatarUrlAttribute(): string|null
    {
        return $this->avatar ? Storage::disk('public')->url('thumbnails/' . $this->avatar) : null;
    }

    /**
     * Get the URL of the user's avatar.
     *
     * @return string|null
     */
    public function getAvatarUrlAttribute(): string|null
    {
        return $this->avatar ? Storage::disk('public')->url($this->avatar) : null;
    }

    /**
     * Format the creation date of the user for frontend display.
     *
     * @param bool $isHumanTime Whether to return a human-readable time format.
     * @return string The formatted creation date.
     */
    public function createdAt($isHumanTime = true): string
    {
        return $isHumanTime
            ? humanTime($this->created_at)
            : frontendDateTime($this->created_at);
    }

    /**
     * Format the last update date of the user for frontend display.
     *
     * @param bool $isHumanTime Whether to return a human-readable time format.
     * @return string The formatted last update date.
     */
    public function updatedAt($isHumanTime = true): string
    {
        if (empty($this->updated_at)) return '<small><i>Never Updated</i></small>';

        return $isHumanTime
            ? humanTime($this->updated_at)
            : frontendDateTime($this->updated_at);
    }

    public function getRolesBadges()
    {
        $roles = $this->roles()->pluck('name');

        if (empty($roles)) return canEmpty(null);

        $html = '';

        foreach ($this->roles()->pluck('name') as $role) {
            $html .= '<span class="badge">' . replaceUnderscores($role) . '</span>';
        }

        return $html;
    }

    /**
     * Send the password reset notification.
     *
     * @param  string  $token
     * @return void
     */
    public function sendPasswordResetNotification($token): void
    {
        $this->notify(new ResetPasswordNotification($token));
    }

    /**
     * Send an invitation email to the user, with a link to register.
     *
     * @param string $role The role to invite the user to.
     * @return void
     */
    public function sendInvitationEmail($invite, $role): void
    {
        $this->notify(new SendEmailInvitationNotification($invite, $role));
    }

     public function sendOtpVerificationEmail()
    {
        $this->notify(new SendOtpEmailNotification());
    }
}
