<?php

declare(strict_types=1);

namespace App\Services;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;

/**
 * Трекинг активных сессий пользователя для вкладки «Безопасность»
 * (UI-аудит 21.06.2026). Использует существующую таблицу user_sessions.
 *
 * Запись при входе; отзыв = удаление строки + удаление сессии из Redis по
 * session_id (после этого следующий запрос с устройства разлогинится).
 *
 * NB: в token_hash хранится сам session_id (нужен для удаления из Redis) —
 * как делает database-драйвер сессий Laravel (PK = raw session id). БД защищена
 * ролями/RLS-периметром; сессии короткоживущие (expires_at).
 *
 * Best-effort: ошибки трекинга НЕ ломают вход/выход (только лог).
 */
class UserSessionTracker
{
    /** Записать/обновить запись текущей сессии после успешного входа. */
    public function record(Request $request, int $userId): void
    {
        try {
            $sid = $request->session()->getId();
            DB::table('user_sessions')->updateOrInsert(
                ['token_hash' => $sid],
                [
                    'user_id' => $userId,
                    'ip_address' => $request->ip(),
                    'user_agent' => substr((string) $request->userAgent(), 0, 1000),
                    'last_active_at' => now(),
                    'created_at' => now(),
                    'expires_at' => now()->addMinutes((int) config('session.lifetime', 120)),
                ],
            );
        } catch (\Throwable $e) {
            Log::warning('user_session.record_failed', ['error' => $e->getMessage()]);
        }
    }

    /**
     * Отозвать сессию пользователя: удалить из Redis (реальный выход) + строку.
     * Возвращает true, если строка принадлежит юзеру и отозвана.
     */
    public function revoke(int $userId, int $sessionRowId): bool
    {
        $row = DB::table('user_sessions')
            ->where('id', $sessionRowId)
            ->where('user_id', $userId)
            ->first(['id', 'token_hash']);

        if ($row === null) {
            return false;
        }

        try {
            Redis::connection(config('session.connection') ?: null)->del($row->token_hash);
        } catch (\Throwable $e) {
            Log::warning('user_session.redis_del_failed', ['error' => $e->getMessage()]);
        }

        DB::table('user_sessions')->where('id', $row->id)->delete();

        return true;
    }

    /** Убрать текущую сессию из списка при logout. */
    public function revokeCurrent(Request $request): void
    {
        try {
            DB::table('user_sessions')
                ->where('token_hash', $request->session()->getId())
                ->delete();
        } catch (\Throwable $e) {
            Log::warning('user_session.revoke_current_failed', ['error' => $e->getMessage()]);
        }
    }
}
