#pragma once

// ==========================================================
// time_utils.hpp - Monotonic Time, Rate Limiting & Cooldowns
// C++23 Core Engine - AutoPOE2 (DOC-49 & Rule 16)
// ==========================================================

#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).
 * 
 * Sử dụng std::chrono::steady_clock, an toàn tuyệt đối trước việc hệ điều hành thay đổi
 * múi giờ hoặc đồng bộ NTP, bảo đảm tính đơn điệu không suy giảm.
 * 
 * @return uint64_t Mốc thời gian đơn điệu tính bằng mili-giây.
 * 
 * @code
 * uint64_t startMs = common::NowMs();
 * @endcode
 */
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) chống spam hành vi trên Hot Path 120Hz.
 */
class RateLimiter {
public:
    constexpr explicit RateLimiter(uint64_t intervalMs = 0) noexcept
        : m_intervalMs(intervalMs) {}

    /**
     * @brief Kiểm tra xem đã đủ thời gian giãn cách để thực thi chưa (không cập nhật timestamp).
     * @param nowMs Mốc thời gian hiện tại (nếu truyền 0 sẽ tự động gọi NowMs()).
     * @return true nếu khoảng cách >= intervalMs, false nếu còn trong khoảng chờ.
     */
    [[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);
    }

    /**
     * @brief Thử thực thi: nếu CanExecute == true thì cập nhật timestamp thực thi và trả về true.
     * @param nowMs Mốc thời gian hiện tại (nếu truyền 0 sẽ tự động gọi NowMs()).
     * @return true nếu đã kích hoạt thực thi thành công.
     */
    bool TryExecute(uint64_t nowMs = 0) noexcept {
        uint64_t now = (nowMs > 0) ? nowMs : NowMs();
        if (CanExecute(now)) {
            m_lastExecMs = now;
            return true;
        }
        return false;
    }

    /**
     * @brief Kiểm tra và cập nhật tiến trình nếu đủ điều kiện rate limit.
     */
    bool CanProceed(uint64_t nowMs = 0) noexcept {
        return TryExecute(nowMs);
    }

    /**
     * @brief Ghi nhận một lần thực thi cưỡng bức tại thời điểm chỉ định.
     */
    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) {}

    /**
     * @brief Kích hoạt đếm hồi chiêu với thời lượng xác định.
     * @param durationMs Thời gian hồi chiêu tính bằng mili-giây.
     * @param nowMs Mốc thời gian bắt đầu (mặc định NowMs()).
     */
    void Start(uint64_t durationMs, uint64_t nowMs = 0) noexcept {
        uint64_t now = (nowMs > 0) ? nowMs : NowMs();
        m_startMs = now;
        m_durationMs = durationMs;
    }

    /**
     * @brief Kích hoạt hồi chiêu theo thời lượng mặc định đã thiết lập.
     */
    void Trigger(uint64_t nowMs = 0) noexcept {
        uint64_t now = (nowMs > 0) ? nowMs : NowMs();
        m_startMs = now;
    }

    /**
     * @brief Kiểm tra xem kỹ năng/hành vi đã hồi xong chưa.
     */
    [[nodiscard]] bool IsReady(uint64_t nowMs = 0) const noexcept {
        uint64_t now = (nowMs > 0) ? nowMs : NowMs();
        return (now >= m_startMs + m_durationMs);
    }

    /**
     * @brief Lấy thời gian còn lại trước khi hồi chiêu hoàn tất.
     * @return uint64_t Số mili-giây còn lại (0 nếu đã sẵn sàng).
     */
    [[nodiscard]] uint64_t RemainingMs(uint64_t nowMs = 0) const noexcept {
        uint64_t now = (nowMs > 0) ? nowMs : NowMs();
        if (now >= m_startMs + m_durationMs) return 0;
        return (m_startMs + m_durationMs) - now;
    }

    void Reset() noexcept {
        m_startMs = 0;
        m_durationMs = 0;
    }

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

/**
 * @brief Đồng hồ bấm giờ chính xác cao (Stopwatch) đo microsecond/millisecond.
 */
class Stopwatch {
public:
    Stopwatch() noexcept { Start(); }

    void Start() noexcept {
        m_startTime = std::chrono::steady_clock::now();
        m_running = true;
    }

    void Stop() noexcept {
        if (m_running) {
            m_stopTime = std::chrono::steady_clock::now();
            m_running = false;
        }
    }

    void Reset() noexcept {
        m_running = false;
        m_startTime = {};
        m_stopTime = {};
    }

    [[nodiscard]] uint64_t ElapsedMs() const noexcept {
        auto end = m_running ? std::chrono::steady_clock::now() : m_stopTime;
        return static_cast<uint64_t>(
            std::chrono::duration_cast<std::chrono::milliseconds>(end - m_startTime).count()
        );
    }

    [[nodiscard]] uint64_t ElapsedMicroseconds() const noexcept {
        auto end = m_running ? std::chrono::steady_clock::now() : m_stopTime;
        return static_cast<uint64_t>(
            std::chrono::duration_cast<std::chrono::microseconds>(end - m_startTime).count()
        );
    }

    [[nodiscard]] double ElapsedSeconds() const noexcept {
        return static_cast<double>(ElapsedMicroseconds()) / 1'000'000.0;
    }

private:
    std::chrono::steady_clock::time_point m_startTime{};
    std::chrono::steady_clock::time_point m_stopTime{};
    bool m_running = false;
};

} // namespace common

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