#include "PeerRegistry.hpp"

#include <fcntl.h>
#include <sys/file.h>
#include <unistd.h>

#include <sstream>
#include <stdexcept>

namespace tw::p2p {

PeerRegistry::PeerRegistry(std::string file_path)
    : m_file_path(std::move(file_path)) {}

void PeerRegistry::register_self(uint32_t id, const std::string& address) {
    int fd = ::open(m_file_path.c_str(), O_RDWR | O_CREAT, 0644);
    if (fd < 0) throw std::runtime_error("PeerRegistry: failed to open registry file");

    // Blocks until exclusive lock is granted.
    if (::flock(fd, LOCK_EX) != 0) {
        ::close(fd);
        throw std::runtime_error("PeerRegistry: flock failed");
    }

    std::string line = std::to_string(id) + "," + address + "\n";
    ::lseek(fd, 0, SEEK_END);
    ::write(fd, line.data(), line.size());

    ::flock(fd, LOCK_UN);
    ::close(fd);
}

std::vector<PeerRegistry::PeerInfo> PeerRegistry::read_all() const {
    std::vector<PeerInfo> result;

    int fd = ::open(m_file_path.c_str(), O_RDONLY);
    if (fd < 0) return result;

    ::flock(fd, LOCK_SH);

    std::string content;
    char buf[512];
    ssize_t n;
    while ((n = ::read(fd, buf, sizeof(buf))) > 0)
        content.append(buf, static_cast<size_t>(n));

    ::flock(fd, LOCK_UN);
    ::close(fd);

    std::istringstream stream(content);
    std::string line;
    while (std::getline(stream, line)) {
        if (line.empty()) continue;
        auto comma = line.find(',');
        if (comma == std::string::npos) continue;
        PeerInfo info;
        info.id      = static_cast<uint32_t>(std::stoul(line.substr(0, comma)));
        info.address = line.substr(comma + 1);
        result.push_back(std::move(info));
    }
    return result;
}

}
