<?php

declare(strict_types=1);

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Symfony\Component\HttpFoundation\Response;

final class UpdateLastVisit
{
    private const SESSION_KEY = 'last_visit.logged_at';

    private const THROTTLE_SECONDS = 300;

    private static ?bool $usersTableHasLastVisitAt = null;

    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);

        $user = $request->user();
        if (! $user) {
            return $response;
        }

        if (! $this->usersTableHasLastVisitAt()) {
            return $response;
        }

        $now = now();
        $session = $request->hasSession() ? $request->session() : null;
        $lastLoggedAt = $session?->get(self::SESSION_KEY);

        if (is_numeric($lastLoggedAt) && ((int) $lastLoggedAt + self::THROTTLE_SECONDS) > $now->getTimestamp()) {
            return $response;
        }

        $lastVisitAt = $user->last_visit_at;
        if ($lastVisitAt !== null && method_exists($lastVisitAt, 'getTimestamp')) {
            if (($lastVisitAt->getTimestamp() + self::THROTTLE_SECONDS) > $now->getTimestamp()) {
                $session?->put(self::SESSION_KEY, $lastVisitAt->getTimestamp());

                return $response;
            }
        }

        DB::table('users')
            ->where('id', $user->id)
            ->update([
                'last_visit_at' => $now,
                'updated_at' => $now,
            ]);

        $user->forceFill(['last_visit_at' => $now]);
        $session?->put(self::SESSION_KEY, $now->getTimestamp());

        return $response;
    }

    private function usersTableHasLastVisitAt(): bool
    {
        return self::$usersTableHasLastVisitAt ??= Schema::hasColumn('users', 'last_visit_at');
    }
}
