#pragma once

// ==========================================================
// time_utils.hpp - Monotonic Time & Rate Limiting
// C++23 Standalone Visual Tool - Poe2VisualTool
// ==========================================================

#include <chrono>
#include <cstdint>

namespace common {

/**
 * @brief Lấy mốc thời gian đơn điệu (monotonic) tính theo mili-giây (ms).
 */
inline uint64_t NowMs() noexcept {
    return static_cast<uint64_t>(
        std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::steady_clock::now().time_since_epoch()
        ).count()
    );
}

/**
 * @brief Bộ điều tiết tần suất thực thi (Rate Limiter).
 */
class RateLimiter {
public:
    constexpr explicit RateLimiter(uint64_t intervalMs = 0) noexcept
        : m_intervalMs(intervalMs) {}

    [[nodiscard]] bool CanExecute(uint64_t nowMs = 0) const noexcept {
        uint64_t now = (nowMs > 0) ? nowMs : NowMs();
        return (now >= m_lastExecMs) && (now - m_lastExecMs >= m_intervalMs);
    }

    bool TryExecute(uint64_t nowMs = 0) noexcept {
        uint64_t now = (nowMs > 0) ? nowMs : NowMs();
        if (CanExecute(now)) {
            m_lastExecMs = now;
            return true;
        }
        return false;
    }

    bool CanProceed(uint64_t nowMs = 0) noexcept {
        return TryExecute(nowMs);
    }

    void RecordExecution(uint64_t nowMs = 0) noexcept {
        m_lastExecMs = (nowMs > 0) ? nowMs : NowMs();
    }

    void SetInterval(uint64_t intervalMs) noexcept { m_intervalMs = intervalMs; }
    [[nodiscard]] constexpr uint64_t Interval() const noexcept { return m_intervalMs; }
    [[nodiscard]] constexpr uint64_t LastExecutionTime() const noexcept { return m_lastExecMs; }
    void Reset() noexcept { m_lastExecMs = 0; }

private:
    uint64_t m_intervalMs = 0;
    uint64_t m_lastExecMs = 0;
};

/**
 * @brief Đồng hồ đếm thời gian hồi chiêu (Cooldown Timer).
 */
class CooldownTimer {
public:
    constexpr CooldownTimer() noexcept = default;
    constexpr explicit CooldownTimer(uint64_t defaultDurationMs) noexcept
        : m_durationMs(defaultDurationMs) {}

    void Trigger(uint64_t durationMs = 0) noexcept {
        uint64_t dur = (durationMs > 0) ? durationMs : m_durationMs;
        m_expireMs = NowMs() + dur;
    }

    [[nodiscard]] bool IsReady(uint64_t nowMs = 0) const noexcept {
        uint64_t now = (nowMs > 0) ? nowMs : NowMs();
        return now >= m_expireMs;
    }

    void Reset() noexcept { m_expireMs = 0; }

private:
    uint64_t m_durationMs = 0;
    uint64_t m_expireMs = 0;
};

} // namespace common

using common::NowMs;
using common::RateLimiter;
using common::CooldownTimer;
