/*********************************************************************
 * \file   UUID.hpp
 *
 * \author Romain BOULLARD
 * \date   October 2025
 *********************************************************************/
#ifndef BIGFOOT_SYSTEM_UUID_UUID_HPP
#define BIGFOOT_SYSTEM_UUID_UUID_HPP
#include <uuid.h>

namespace Bigfoot
{
class UUID
{
  public:
    static constexpr std::uint32_t UUID_BYTE_SIZE = 16;

    UUID();

    /**
     * Constructor.
     *
     * \param p_string String to create the UUID, should be a UUID V4
     */
    explicit UUID(const std::string_view p_string);

    /**
     * Constructor
     *
     * \param p_raw The raw UUID
     */
    explicit UUID(const std::span<const std::byte, UUID_BYTE_SIZE> p_raw);

    UUID(const UUID& p_uuid) = default;
    UUID(UUID&& p_uuid) noexcept = default;

    /**
     * Gets the raw bytes of a UUID
     *
     * \return A view to the raw bytes
     */
    [[nodiscard]]
    std::span<const std::byte, UUID_BYTE_SIZE> ToBytes() const;

    /**
     * Gets the string representation of a UUID
     *
     * \return A string representing the UUID
     */
    [[nodiscard]]
    std::string ToString() const;

    ~UUID() = default;

    operator bool() const;

    UUID& operator=(const UUID& p_uuid) = default;
    UUID& operator=(UUID&& p_uuid) noexcept = default;

    [[nodiscard]]
    bool operator==(const UUID& p_uuid) const;
    [[nodiscard]]
    bool operator<(const UUID& p_uuid) const;

  private:
    /**
     * Get a random generator
     *
     * \return A random generator
     */
    static std::mt19937 GetRandomGenerator();

    /**
     * Get a UUID generator
     *
     * \return A UUID generator
     */
    static uuids::uuid_random_generator GetUUIDGenerator();

  public:
    /**
     * Null UUID.
     */
    static UUID NULL_UUID;

  private:
    friend struct std::hash<UUID>;

    /**
     * The uuid.
     */
    uuids::uuid m_uuid;

    /**
     * Random generator for the UUID
     */
    inline static std::mt19937 ms_randomGenerator = GetRandomGenerator();

    /**
     * UUID generator
     */
    inline static uuids::uuid_random_generator ms_uuidGenerator = GetUUIDGenerator();
};
}   // namespace Bigfoot

/****************************************************************************************/

template<>
struct std::hash<Bigfoot::UUID>
{
    std::size_t operator()(const Bigfoot::UUID& p_uuid) const noexcept
    {
        return std::hash<uuids::uuid>()(p_uuid.m_uuid);
    }
};

template<>
struct std::formatter<Bigfoot::UUID, char>
{
    constexpr auto parse(std::format_parse_context& ctx)
    {
        return ctx.begin();
    }

    template<typename FormatContext>
    auto format(const Bigfoot::UUID& p_uuid, FormatContext& ctx) const
    {
        return std::format_to(ctx.out(), "{}", p_uuid.ToString());
    }
};

/****************************************************************************************/

#if defined BIGFOOT_NOT_OPTIMIZED
#include <quill/DeferredFormatCodec.h>

template<>
struct fmtquill::formatter<Bigfoot::UUID>
{
    constexpr auto parse(format_parse_context& ctx)
    {
        return ctx.begin();
    }

    auto format(const Bigfoot::UUID& p_uuid, format_context& ctx) const
    {
        return fmtquill::format_to(ctx.out(), "{}", p_uuid.ToString());
    }
};

template<>
struct quill::Codec<Bigfoot::UUID>: quill::DeferredFormatCodec<Bigfoot::UUID>
{
};
#endif

/****************************************************************************************/

namespace Flat
{
namespace Bigfoot
{
struct UUID;
}
}   // namespace Flat

/****************************************************************************************/

namespace flatbuffers
{
Flat::Bigfoot::UUID Pack(const Bigfoot::UUID& p_uuid);
Bigfoot::UUID UnPack(const Flat::Bigfoot::UUID& p_flatUUID);
}   // namespace flatbuffers

#endif
