<?php

declare(strict_types=1);

namespace apexl\Io\modules\user\entities;

use apexl\Io\enums\Casts;
use apexl\Io\Traits\HasCasts;
use apexl\Io\includes\Entity;
use DateTimeImmutable;

/**
 * A single enrolled second-factor method for a user (Plan 007).
 *
 * The secret is held encrypted at rest (`secret` + base64 `secret_iv`); never
 * read `secret` directly — go through TwoFactorSecretCipher. `is_active` is
 * false until a verify-to-confirm completes. Brute-force lockout state lives on
 * `failed_attempts` / `locked_until`.
 *
 * @property ?int $id
 * @property int $user_id
 * @property string $type
 * @property ?string $secret
 * @property ?string $secret_iv
 * @property bool $is_active
 * @property int $failed_attempts
 * @property ?DateTimeImmutable $locked_until
 * @property ?string $label
 * @property ?DateTimeImmutable $last_used_at
 * @property ?DateTimeImmutable $created
 * @property ?string $created_by
 * @property ?DateTimeImmutable $modified
 * @property ?string $modified_by
 */
class TwoFactorMethodEntity extends Entity
{
    use HasCasts;

    final public const string TABLE = 'user_two_factor_methods';

    public function __construct()
    {
        parent::__construct(self::TABLE);
        $this->setSensitiveData(['secret', 'secret_iv']);
    }

    public function casts(): array
    {
        return [
            'id' => Casts::INT,
            'user_id' => Casts::INT,
            'is_active' => Casts::BOOL,
            'failed_attempts' => Casts::INT,
            'locked_until' => Casts::DATETIME_STAMP,
            'last_used_at' => Casts::DATETIME_STAMP,
            'created' => Casts::DATETIME_STAMP,
            'modified' => Casts::DATETIME_STAMP,
        ];
    }

    public function isLocked(?DateTimeImmutable $now = null): bool
    {
        $now ??= new DateTimeImmutable();

        return $this->locked_until instanceof DateTimeImmutable && $this->locked_until > $now;
    }
}
