<?php
declare(strict_types=1);

/**
 * 按 IP 统计连续密码错误次数，超过阈值锁定一段时间，跟具体账号无关（锁的是 IP，不是某个用户名）
 */
final class LoginThrottle
{
    private const FAIL_THRESHOLD = 6;
    private const LOCK_MINUTES = 60;

    /**
     * 当前 IP 是否被锁定，锁定中返回剩余分钟数，没锁定返回 null
     */
    public static function lockedMinutesLeft(string $ip): ?int
    {
        $row = Db::instance()->get('admin_login_attempt', ['admin_login_attempt_locked_until'], ['admin_login_attempt_ip' => $ip]);

        if (!$row || !$row['admin_login_attempt_locked_until']) {
            return null;
        }

        $secondsLeft = strtotime($row['admin_login_attempt_locked_until']) - time();

        return $secondsLeft > 0 ? (int) ceil($secondsLeft / 60) : null;
    }

    /**
     * 记一次密码错误，达到阈值就锁定该 IP
     */
    public static function recordFailure(string $ip): void
    {
        $db = Db::instance();
        $row = $db->get('admin_login_attempt', '*', ['admin_login_attempt_ip' => $ip]);

        $failCount = ($row['admin_login_attempt_fail_count'] ?? 0) + 1;
        $lockedUntil = $failCount >= self::FAIL_THRESHOLD
            ? date('Y-m-d H:i:s', time() + self::LOCK_MINUTES * 60)
            : null;

        if ($row) {
            $db->update('admin_login_attempt', [
                'admin_login_attempt_fail_count'   => $failCount,
                'admin_login_attempt_locked_until' => $lockedUntil,
            ], ['admin_login_attempt_ip' => $ip]);
        } else {
            $db->insert('admin_login_attempt', [
                'admin_login_attempt_ip'           => $ip,
                'admin_login_attempt_fail_count'   => $failCount,
                'admin_login_attempt_locked_until' => $lockedUntil,
            ]);
        }
    }

    /**
     * 登录成功后清零该 IP 的失败次数
     */
    public static function recordSuccess(string $ip): void
    {
        $db = Db::instance();
        if ($db->get('admin_login_attempt', ['admin_login_attempt_id'], ['admin_login_attempt_ip' => $ip])) {
            $db->update('admin_login_attempt', [
                'admin_login_attempt_fail_count'   => 0,
                'admin_login_attempt_locked_until' => null,
            ], ['admin_login_attempt_ip' => $ip]);
        }
    }
}
