<?php

declare(strict_types=1);

namespace app\common\service;

use think\Cache;
use think\Exception;

/**
 * 域名检测频率限制
 * 每分钟最多 2 次，超限后冷却 120 秒
 */
class DomainDetectRateLimitService
{
    /** 统计窗口（秒） */
    private const WINDOW_SECONDS = 60;

    /** 窗口内最大检测次数 */
    private const MAX_ATTEMPTS = 2;

    /** 超限后冷却时间（秒） */
    private const COOLDOWN_SECONDS = 120;

    /**
     * 校验是否允许检测，不允许时抛出异常
     *
     * @param int $adminId  管理员 ID
     * @param int $domainId 域名记录 ID
     * @throws Exception
     */
    public function assertCanDetect(int $adminId, int $domainId): void
    {
        $now = time();
        $baseKey = 'domain_detect:' . $adminId . ':' . $domainId;
        $cooldownKey = $baseKey . ':cooldown';
        $attemptsKey = $baseKey . ':attempts';

        $cooldownUntil = (int)Cache::get($cooldownKey, 0);
        if ($cooldownUntil > $now) {
            $wait = $cooldownUntil - $now;
            throw new Exception(sprintf('检测过于频繁，请 %d 秒后再试', $wait));
        }

        $timestamps = Cache::get($attemptsKey);
        if (!is_array($timestamps)) {
            $timestamps = [];
        }

        $timestamps = array_values(array_filter($timestamps, static function ($ts) use ($now): bool {
            return is_int($ts) && ($now - $ts) < self::WINDOW_SECONDS;
        }));

        if (count($timestamps) >= self::MAX_ATTEMPTS) {
            Cache::set($cooldownKey, $now + self::COOLDOWN_SECONDS, self::COOLDOWN_SECONDS);
            Cache::set($attemptsKey, $timestamps, self::WINDOW_SECONDS + self::COOLDOWN_SECONDS);
            throw new Exception(sprintf(
                '一分钟最多检测 %d 次，请 %d 秒后再试',
                self::MAX_ATTEMPTS,
                self::COOLDOWN_SECONDS
            ));
        }

        $timestamps[] = $now;
        Cache::set($attemptsKey, $timestamps, self::WINDOW_SECONDS + self::COOLDOWN_SECONDS);
    }
}
