<?php

namespace App\Models;

use App\Enums\LanguageEnum;
use App\Enums\UserTypeEnum;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

/**
 * @property int $id
 * @property string|null $name
 * @property string|null $email
 * @property \Illuminate\Support\Carbon|null $email_verified_at
 * @property string|null $password
 * @property UserTypeEnum $type
 * @property LanguageEnum $language
 * @property \Illuminate\Support\Carbon|null $created_at
 * @property \Illuminate\Support\Carbon|null $updated_at
 * @property string|null $phone
 * @property string|null $code
 * @property bool $validated_code
 * @property bool $registration_complete
 * @property-read \App\Models\Client|null $client
 * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\DeviceToken> $deviceTokens
 * @property-read int|null $device_tokens_count
 * @property-read \Illuminate\Notifications\DatabaseNotificationCollection<int, \Illuminate\Notifications\DatabaseNotification> $notifications
 * @property-read int|null $notifications_count
 * @property-read \Kalnoy\Nestedset\Collection<int, \App\Models\Permission> $permissions
 * @property-read int|null $permissions_count
 * @property-read \App\Models\Provider|null $provider
 * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\PushNotificationLog> $pushNotificationLogs
 * @property-read int|null $push_notification_logs_count
 * @property-read \Illuminate\Database\Eloquent\Collection<int, \Laravel\Sanctum\PersonalAccessToken> $tokens
 * @property-read int|null $tokens_count
 * @method static \Database\Factories\UserFactory factory($count = null, $state = [])
 * @method static \Illuminate\Database\Eloquent\Builder<static>|User newModelQuery()
 * @method static \Illuminate\Database\Eloquent\Builder<static>|User newQuery()
 * @method static \Illuminate\Database\Eloquent\Builder<static>|User query()
 * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereCode($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereCreatedAt($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereEmail($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereEmailVerifiedAt($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereId($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereLanguage($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereName($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|User wherePassword($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|User wherePhone($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereRegistrationComplete($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereType($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereUpdatedAt($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereValidatedCode($value)
 * @mixin \Eloquent
 */
class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    protected $guarded = ['id'];

    /**
     * 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',
            'password'              => 'hashed',
            'type'                  => UserTypeEnum::class,
            'language'              => LanguageEnum::class,
            'registration_complete' => 'boolean',
            'push_notifications_enabled' => 'boolean',
        ];
    }

    //

    public function isAdmin(): bool
    {
        return $this->type === UserTypeEnum::ADMIN;
    }

    public function canModerateProviders(): bool
    {
        return $this->isAdmin();
    }

    public function canModerateClients(): bool
    {
        return $this->isAdmin();
    }

    //

    public function client()
    {
        return $this->hasOne(Client::class, 'user_id');
    }

    public function provider()
    {
        return $this->hasOne(Provider::class, 'user_id');
    }

    //

    public function deviceTokens()
    {
        return $this->hasMany(DeviceToken::class);
    }

    public function pushNotificationLogs()
    {
        return $this->hasMany(PushNotificationLog::class);
    }

    //

    // Create a new access token for the user.

    public function createAccessToken(string $deviceId): string
    {
        return $this->createToken(
            name: "access_token_{$deviceId}",
            abilities: ['access'],
            expiresAt: Carbon::now()->addMinutes(15),
        )->plainTextToken;
    }

    // Create a new refresh token for the user.

    public function createRefreshToken(string $deviceId): string
    {
        return $this->createToken(
            name: "refresh_token_{$deviceId}",
            abilities: ['refresh'],
            expiresAt: Carbon::now()->addDays(30),
        )->plainTextToken;
    }

    // Delete all tokens (access and refresh) for a specific device.

    public function deleteTokensByDevice(string $deviceId): void
    {
        $this->tokens()
            ->where('name', 'like', "%_{$deviceId}")
            ->delete();
    }

    public function permissions(): BelongsToMany
    {
        return $this->belongsToMany(
            Permission::class,
            'user_type_permissions',
            'user_type',
            'permission_id',
        );
    }

    // Create a new access token for the user in the app.

    public function createAccessTokenApp(string $deviceId): string
    {
        return $this->createToken(
            name: "access_token_{$deviceId}",
            abilities: ['access'],
            expiresAt: Carbon::now()->addCentury(),
        )->plainTextToken;
    }

    // Create a new refresh token for the user in the app.

    public function createRefreshTokenApp(string $deviceId): string
    {
        return $this->createToken(
            name: "refresh_token_{$deviceId}",
            abilities: ['refresh'],
            expiresAt: Carbon::now()->addCentury(),
        )->plainTextToken;
    }
}
