/*
 * Copyright (C) 2015-present ScyllaDB
 */

/*
 * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
 */


#include "UUID.hh"
#include <seastar/net/byteorder.hh>
#include <random>
#include <boost/algorithm/string/erase.hpp>
#include <string>
#include <fmt/ostream.h>
#include <seastar/core/format.hh>
#include <seastar/core/sstring.hh>
#include "marshal_exception.hh"

namespace utils {

UUID
make_random_uuid() noexcept {
    static thread_local std::mt19937_64 engine(std::random_device().operator()());
    static thread_local std::uniform_int_distribution<uint64_t> dist;
    uint64_t msb, lsb;
    msb = dist(engine);
    lsb = dist(engine);
    msb &= ~uint64_t(0x0f << 12);
    msb |= 0x4 << 12; // version 4
    lsb &= ~(uint64_t(0x3) << 62);
    lsb |= uint64_t(0x2) << 62; // IETF variant
    return UUID(msb, lsb);
}

std::ostream& operator<<(std::ostream& out, const UUID& uuid) {
    fmt::print(out, "{}", uuid);
    return out;
}

std::istream& operator>>(std::istream& in, UUID& uuid) {
    sstring uuid_string;
    in >> uuid_string;
    uuid = UUID(uuid_string);
    return in;
}

UUID::UUID(std::string_view uuid) {
    sstring uuid_string(uuid.begin(), uuid.end());
    boost::erase_all(uuid_string, "-");
    auto size = uuid_string.size() / 2;
    if (size != 16) {
        throw marshal_exception(seastar::format("UUID string size mismatch: '{}'", uuid));
    }
    sstring most = sstring(uuid_string.begin(), uuid_string.begin() + size);
    sstring least = sstring(uuid_string.begin() + size, uuid_string.end());
    int base = 16;
    try {
        std::size_t pos = 0;
        this->most_sig_bits = std::stoull(most, &pos, base);
        if (pos != most.size()) {
            throw std::invalid_argument("");
        }
        this->least_sig_bits = std::stoull(least, &pos, base);
        if (pos != least.size()) {
            throw std::invalid_argument("");
        }
    } catch (const std::logic_error&) {
        throw marshal_exception(seastar::format("invalid UUID: '{}'", uuid));
    }
}

uint32_t uuid_xor_to_uint32(const UUID& uuid) {
    size_t h = std::hash<utils::UUID>{}(uuid);
    return uint32_t(h);
}

}
