<?php

declare(strict_types=1);

namespace App\Model;

use Hyperf\Database\Model\Relations\BelongsTo;
use Hyperf\Database\Model\Relations\HasMany;
use Hyperf\DbConnection\Model\Model;
use OpenApi\Attributes as OA;
use Qbhy\HyperfAuth\Authenticatable;

/**
 * @property int $id
 * @property string $username
 * @property string $password
 * @property string $email
 * @property int $status
 * @property int $role_id
 * @property bool $api_key_enabled
 * @property array $ext
 * @property string $refresh_token
 * @property \Carbon\Carbon $refresh_token_expires_at
 * @property \Carbon\Carbon $created_at
 * @property \Carbon\Carbon $updated_at
 */
#[OA\Schema(
    schema: 'User',
    type: 'object',
    properties: [
        new OA\Property(property: 'id', type: 'integer', example: 1),
        new OA\Property(property: 'username', type: 'string', example: 'user_1234'),
        new OA\Property(property: 'email', type: 'string', example: 'user@example.com'),
        new OA\Property(property: 'status', type: 'integer', example: 1),
        new OA\Property(property: 'role_id', type: 'integer', nullable: true, example: 1),
        new OA\Property(property: 'api_key_enabled', type: 'boolean', example: false),
        new OA\Property(property: 'ext', type: 'object', nullable: true, example: ['nickname' => 'user']),
        new OA\Property(
            property: 'role',
            type: 'object',
            nullable: true,
            properties: [
                new OA\Property(property: 'id', type: 'integer', example: 1),
                new OA\Property(property: 'name', type: 'string', example: 'administrator'),
            ]
        ),
        new OA\Property(property: 'refresh_token_expires_at', type: 'string', format: 'date-time', nullable: true),
        new OA\Property(property: 'created_at', type: 'string', format: 'date-time'),
        new OA\Property(property: 'updated_at', type: 'string', format: 'date-time'),
    ]
)]
class User extends Model implements Authenticatable
{
    /**
     * The table associated with the model.
     */
    protected ?string $table = 'users';

    /**
     * The attributes that are mass assignable.
     */
    protected array $fillable = [
        'username',
        'password',
        'email',
        'status',
        'role_id',
        'api_key_enabled',
        'ext',
        'refresh_token',
        'refresh_token_expires_at',
    ];

    /**
     * The attributes that should be hidden for serialization.
     */
    protected array $hidden = [
        'password',
        'refresh_token',
    ];

    /**
     * The attributes that should be cast to native types.
     */
    protected array $casts = [
        'id' => 'integer',
        'status' => 'integer',
        'role_id' => 'integer',
        'api_key_enabled' => 'boolean',
        'ext' => 'array',
        'refresh_token_expires_at' => 'datetime',
        'created_at' => 'datetime',
        'updated_at' => 'datetime',
    ];

    /**
     * Get the name of the unique identifier for the user.
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Retrieve a user by their unique identifier.
     */
    public static function retrieveById($key): ?Authenticatable
    {
        return static::query()->find($key);
    }

    /**
     * Set the user's password (auto-hash).
     */
    public function setPasswordAttribute($value): void
    {
        $this->attributes['password'] = password_hash($value, PASSWORD_DEFAULT);
    }

    /**
     * Verify the user's password.
     */
    public function verifyPassword(string $password): bool
    {
        return password_verify($password, $this->password);
    }

    /**
     * 角色关联
     */
    public function role(): BelongsTo
    {
        return $this->belongsTo(Role::class, 'role_id');
    }

    /**
     * 用户的 API Keys
     */
    public function apiKeys(): HasMany
    {
        return $this->hasMany(ApiKey::class, 'user_id');
    }

    public function isAdministrator(): bool
    {
        return $this->role?->name === 'administrator';
    }

    public function isDeveloper(): bool
    {
        return $this->role?->name === 'developer';
    }

    public function isAccessor(): bool
    {
        return $this->role?->name === 'accessor';
    }

    public function developedPlatforms(): HasMany
    {
        return $this->hasMany(Platform::class, 'developer_id');
    }

    /**
     * 用户的数据访问范围
     */
    public function dataScopes(): HasMany
    {
        return $this->hasMany(UserDataScope::class, 'user_id');
    }

    // @TODO 重新实现删除用户时平台归属转移逻辑（Hyperf 不支持 static::deleting 事件绑定）

    /**
     * Check if refresh token is valid.
     */
    public function isRefreshTokenValid(): bool
    {
        if (!$this->refresh_token || !$this->refresh_token_expires_at) {
            return false;
        }

        return $this->refresh_token_expires_at->isFuture();
    }
}
