#ifndef HOSTS_POLICIES_H
#define HOSTS_POLICIES_H

#include <cstdint>
#include <vector>
#include <unordered_map>
#include <cassert>
#include "core/types.h"

namespace dofs {

// Abstract publisher policy: map logical update groups → concrete multicast mask.
struct PubBasePolicy {
        virtual ~PubBasePolicy() = default;
        virtual PacketGroups select_multicast_groups(PacketGroups update_groups_mask) =
                0;
};

// Round-robin policy over bit ranges per logical group.
class PubRRPolicy final : public PubBasePolicy {
public:
        struct ReplicaRange {
                uint32_t update_group; // logical group id you use in your workload file
                uint8_t  low_bit;      // inclusive, < 128
                uint8_t  high_bit;     // inclusive, < 128
        };

        explicit PubRRPolicy(std::vector<ReplicaRange> ranges)
                : _ranges(std::move(ranges)) {
                validate_and_build();
        }

        PacketGroups select_multicast_groups(PacketGroups update_groups_mask) override {
                PacketGroups result = 0;

                for (auto const& r : _ranges) {
                        if (!group_present(update_groups_mask, r.update_group))
                                continue;

                        const uint8_t width = static_cast<uint8_t>(r.high_bit - r.low_bit + 1);
                        uint8_t &cursor = _rr_cursor[r.update_group];
                        uint8_t chosen = static_cast<uint8_t>(r.low_bit + (cursor % width));
                        cursor = static_cast<uint8_t>((cursor + 1) % width);
                        result |= (PacketGroups(1) << chosen);
                }

                return result;
        }

private:
        std::vector<ReplicaRange> _ranges;
        std::unordered_map<uint32_t, uint8_t> _rr_cursor; // per logical group

        static bool group_present(PacketGroups mask, uint32_t gid) noexcept {
                // convention: logical update groups are not bits, they’re ids you pass in;
                // presence is determined outside—if you want bitwise presence, adapt here later.
                // For now, assume any nonzero mask means all configured groups are requested.
                (void)gid;
                return mask != 0;
        }

        void validate_and_build() {
                for (auto const& r : _ranges) {
                        assert(r.low_bit <= r.high_bit);
                        assert(r.high_bit < 128 && r.low_bit < 128);
                        (void)r;
                        // Non-overlap across different logical groups is allowed.
                }

                // init all cursors to 0 for determinism.
                for (auto const& r : _ranges)
                        _rr_cursor[r.update_group] = 0;
        }
};

struct SubBasePolicy {
        virtual ~SubBasePolicy() = default;
};

struct SubDummyPolicy final : public SubBasePolicy {
        ~SubDummyPolicy() override = default;
};

} // namespace dofs

#endif // HOSTS_POLICIES_H
