#pragma once

#include <atomic>
#include <cstddef>
#include <memory>
#include <optional>

namespace softbus::core {

template <typename T>
class LockFreeQueue {
public:
    explicit LockFreeQueue(std::size_t capacity)
        : capacity_(capacity)
        , buffer_(std::make_unique<Slot[]>(capacity))
        , head_(0)
        , tail_(0)
    {
        for (std::size_t i = 0; i < capacity_; ++i) {
            buffer_[i].sequence.store(i, std::memory_order_relaxed);
        }
    }

    bool push(T item)
    {
        std::size_t pos = tail_.load(std::memory_order_relaxed);
        for (;;) {
            Slot& slot = buffer_[pos % capacity_];
            const std::size_t seq = slot.sequence.load(std::memory_order_acquire);
            const intptr_t diff = static_cast<intptr_t>(seq) - static_cast<intptr_t>(pos);
            if (diff == 0) {
                if (tail_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) {
                    slot.data = std::move(item);
                    slot.sequence.store(pos + 1, std::memory_order_release);
                    return true;
                }
            } else if (diff < 0) {
                return false;
            } else {
                pos = tail_.load(std::memory_order_relaxed);
            }
        }
    }

    std::optional<T> pop()
    {
        std::size_t pos = head_.load(std::memory_order_relaxed);
        for (;;) {
            Slot& slot = buffer_[pos % capacity_];
            const std::size_t seq = slot.sequence.load(std::memory_order_acquire);
            const intptr_t diff = static_cast<intptr_t>(seq) - static_cast<intptr_t>(pos + 1);
            if (diff == 0) {
                if (head_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) {
                    T item = std::move(slot.data);
                    slot.sequence.store(pos + capacity_, std::memory_order_release);
                    return item;
                }
            } else if (diff < 0) {
                return std::nullopt;
            } else {
                pos = head_.load(std::memory_order_relaxed);
            }
        }
    }

    bool empty() const
    {
        const std::size_t h = head_.load(std::memory_order_acquire);
        const std::size_t t = tail_.load(std::memory_order_acquire);
        return h >= t;
    }

    std::size_t capacity() const { return capacity_; }

private:
    struct Slot {
        std::atomic<std::size_t> sequence{0};
        T data{};
    };

    const std::size_t capacity_;
    std::unique_ptr<Slot[]> buffer_;
    std::atomic<std::size_t> head_;
    std::atomic<std::size_t> tail_;
};

} // namespace softbus::core
