#pragma once
/// \file spsc_ring.hpp — Ring buffer lock-free 1 producer / 1 consumer.
/// Dùng làm xương sống truyền snapshot & input giữa các thread (docs/01-architecture.md §3).
/// - Producer duy nhất gọi push(), consumer duy nhất gọi pop().
/// - Full ring ⇒ push trả false (producer retry theo chính sách của tầng trên).
/// - Capacity phải là lũy thừa của 2 để dùng bitmask thay modulo.

#include <array>
#include <atomic>
#include <bit>
#include <cstddef>
#include <type_traits>

namespace ares {

template <typename T, std::size_t Capacity>
class SpscRing {
    static_assert(std::is_nothrow_move_assignable_v<T> || std::is_copy_assignable_v<T>,
                  "T must be copy or move assignable");
    static_assert(Capacity != 0 && (Capacity & (Capacity - 1)) == 0,
                  "Capacity must be a power of two");

public:
    /// Gọi từ producer thread. Trả false nếu ring đầy.
    bool push(const T& value) noexcept {
        const std::size_t h = head_.load(std::memory_order_relaxed);
        const std::size_t t = tail_.load(std::memory_order_acquire);
        if (h - t == Capacity) {
            return false; // full
        }
        slots_[h & kMask] = value;
        head_.store(h + 1, std::memory_order_release);
        return true;
    }

    /// Gọi từ consumer thread. Trả false nếu ring rỗng.
    bool pop(T& out) noexcept {
        const std::size_t t = tail_.load(std::memory_order_relaxed);
        const std::size_t h = head_.load(std::memory_order_acquire);
        if (t == h) {
            return false; // empty
        }
        out = slots_[t & kMask];
        tail_.store(t + 1, std::memory_order_release);
        return true;
    }

    /// xấp xỉ (không đồng bộ hoàn hảo) — chỉ dùng cho telemetry
    [[nodiscard]] std::size_t size_approx() const noexcept {
        return head_.load(std::memory_order_acquire) - tail_.load(std::memory_order_acquire);
    }

    [[nodiscard]] static constexpr std::size_t capacity() noexcept { return Capacity; }

private:
    static constexpr std::size_t kMask = Capacity - 1;

    // Tách head/tail sang 2 cache line riêng để tránh false sharing giữa 2 thread
    alignas(std::hardware_destructive_interference_size)
        std::atomic<std::size_t> head_{0};
    alignas(std::hardware_destructive_interference_size)
        std::atomic<std::size_t> tail_{0};
    std::array<T, Capacity> slots_{};
};

static_assert(std::has_single_bit(std::size_t{256}));

} // namespace ares
