#pragma once

#include <cstddef>
#include <cstdint>
#include <span>
#include <string>
#include <vector>

namespace radar::ipc {

class ShmRing {
  public:
    ShmRing() = default;
    ~ShmRing();

    ShmRing(const ShmRing&) = delete;
    auto operator=(const ShmRing&) -> ShmRing& = delete;

    ShmRing(ShmRing&& other) noexcept;
    auto operator=(ShmRing&& other) noexcept -> ShmRing&;

    [[nodiscard]] static auto open_or_create(
        const std::string& name,
        std::uint32_t capacity,
        std::uint32_t slot_size_bytes
    ) -> ShmRing;

    // Opens ring by name and validates wire-format header fields.
    [[nodiscard]] static auto open_existing(const std::string& name) -> ShmRing;
    // Removes shared memory object by name (safe if it does not exist).
    static void unlink_ring(const std::string& name);

    [[nodiscard]] auto is_open() const -> bool;
    [[nodiscard]] auto capacity() const -> std::uint32_t;
    [[nodiscard]] auto slot_size_bytes() const -> std::uint32_t;
    [[nodiscard]] auto dropped_count() const -> std::uint64_t;

    // Pushes payload with overwrite-oldest policy on overflow.
    [[nodiscard]] auto push(std::span<const std::uint8_t> payload) -> bool;
    // Pops the next payload if available.
    [[nodiscard]] auto pop(std::vector<std::uint8_t>& payload) -> bool;

  private:
    struct Header;
    struct SlotHeader;

    int fd_ = -1;
    std::size_t mapped_size_ = 0;
    void* mapped_ = nullptr;
    Header* header_ = nullptr;

    // Slot geometry helpers.
    [[nodiscard]] auto slot_stride_bytes() const -> std::size_t;
    [[nodiscard]] auto slot_header(std::uint64_t sequence) const -> SlotHeader*;
    [[nodiscard]] auto slot_payload(SlotHeader* slot) const -> std::uint8_t*;

    void close();
    static void validate_name(const std::string& name);
};

}  // namespace radar::ipc
