<?php

declare(strict_types=1);

namespace App\Services;

use PDO;
use RuntimeException;

final class LoginThrottleService
{
    private const MAX_ATTEMPTS = 5;
    private const LOCK_MINUTES = 15;

    public function __construct(private PDO $pdo)
    {
    }

    public function assertNotLocked(string $username, string $ipAddress): void
    {
        $row = $this->findRow($username, $ipAddress);
        if (!$row) {
            return;
        }

        $lockedUntil = trim((string) ($row['locked_until'] ?? ''));
        if ($lockedUntil !== '' && strtotime($lockedUntil) > time()) {
            throw new RuntimeException('تم حظر تسجيل الدخول مؤقتًا. حاول مرة أخرى بعد 15 دقيقة.');
        }
    }

    public function recordFailure(string $username, string $ipAddress): void
    {
        if (!table_exists($this->pdo, 'login_attempts')) {
            return;
        }

        $row = $this->findRow($username, $ipAddress);
        if ($row) {
            $attempts = (int) ($row['attempts'] ?? 0) + 1;
            $lockedUntil = $attempts >= self::MAX_ATTEMPTS
                ? date('Y-m-d H:i:s', time() + (self::LOCK_MINUTES * 60))
                : null;

            $st = $this->pdo->prepare('UPDATE login_attempts SET attempts=?, last_attempt_at=NOW(), locked_until=? WHERE id=?');
            $st->execute([$attempts, $lockedUntil, (int) $row['id']]);
            return;
        }

        $lockedUntil = self::MAX_ATTEMPTS <= 1
            ? date('Y-m-d H:i:s', time() + (self::LOCK_MINUTES * 60))
            : null;

        $st = $this->pdo->prepare('INSERT INTO login_attempts (username, ip_address, attempts, last_attempt_at, locked_until) VALUES (?, ?, 1, NOW(), ?)');
        $st->execute([$username, $ipAddress, $lockedUntil]);
    }

    public function clear(string $username, string $ipAddress): void
    {
        if (!table_exists($this->pdo, 'login_attempts')) {
            return;
        }

        $this->pdo->prepare('DELETE FROM login_attempts WHERE username=? AND ip_address=?')->execute([$username, $ipAddress]);
    }

    private function findRow(string $username, string $ipAddress): array|false
    {
        if (!table_exists($this->pdo, 'login_attempts')) {
            return false;
        }

        $st = $this->pdo->prepare('SELECT id, attempts, locked_until FROM login_attempts WHERE username=? AND ip_address=? LIMIT 1');
        $st->execute([$username, $ipAddress]);
        return $st->fetch();
    }
}
