<?php

declare(strict_types=1);

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Carbon;

/**
 * Токен impersonation (по ТЗ §22.7 / Ю-1).
 *
 * SaaS-admin запрашивает «войти как клиент» с reason (≥30 chars). Генерируется
 * 6-значный код, bcrypt-хеш сохраняется тут, plain отправляется на
 * `tenant.contact_email`. TTL 15 мин, 5 попыток ввода, далее invalidate.
 *
 * Без RLS — таблица доступна только из crm_admin_user (BYPASSRLS).
 *
 * @property int $id
 * @property int $tenant_id
 * @property int $requested_by
 * @property string $code_hash
 * @property string $reason
 * @property string $sent_to_email
 * @property Carbon $expires_at
 * @property Carbon|null $used_at
 * @property int|null $session_id
 * @property Carbon|null $session_ended_at
 * @property int $failed_attempts
 * @property Carbon|null $invalidated_at
 * @property int|null $second_approver_id
 * @property Carbon|null $second_approval_at
 * @property Carbon $created_at
 * @property string|null $session_token_hash
 *
 * @mixin IdeHelperImpersonationToken
 */
class ImpersonationToken extends Model
{
    /** schema-таблица не имеет updated_at. */
    public const UPDATED_AT = null;

    protected $fillable = [
        'tenant_id',
        'requested_by',
        'code_hash',
        'reason',
        'sent_to_email',
        'expires_at',
        'used_at',
        'session_id',
        'session_ended_at',
        'failed_attempts',
        'invalidated_at',
        'second_approver_id',
        'second_approval_at',
        'session_token_hash',
    ];

    protected function casts(): array
    {
        return [
            'expires_at' => 'datetime',
            'used_at' => 'datetime',
            'session_ended_at' => 'datetime',
            'invalidated_at' => 'datetime',
            'second_approval_at' => 'datetime',
            'created_at' => 'datetime',
        ];
    }

    public function isExpired(): bool
    {
        return $this->expires_at->isPast();
    }

    public function isUsable(): bool
    {
        return $this->used_at === null
            && $this->invalidated_at === null
            && $this->failed_attempts < 5
            && ! $this->isExpired();
    }

    /** Сессия impersonation активна: код подтверждён, не завершена, не инвалидирована, в пределах TTL минут. */
    public function isSessionActive(int $ttlMinutes = 60): bool
    {
        return $this->used_at !== null
            && $this->session_ended_at === null
            && $this->invalidated_at === null
            && $this->used_at->copy()->addMinutes($ttlMinutes)->isFuture();
    }

    public function sessionExpiresAt(int $ttlMinutes = 60): ?Carbon
    {
        return $this->used_at?->copy()->addMinutes($ttlMinutes);
    }

    /** @return BelongsTo<Tenant, $this> */
    public function tenant(): BelongsTo
    {
        return $this->belongsTo(Tenant::class);
    }
}
