// WARNING: This file is automatically generated. Any changes will be lost.
#include "twitch-eventsub-ws/payloads/channel-chat-message-v1.hpp"

#include "twitch-eventsub-ws/chrono.hpp"  // IWYU pragma: keep
#include "twitch-eventsub-ws/detail/errors.hpp"
#include "twitch-eventsub-ws/detail/variant.hpp"  // IWYU pragma: keep

#include <boost/json.hpp>

namespace chatterino::eventsub::lib::payload::channel_chat_message::v1 {

boost::json::result_for<Badge, boost::json::value>::type tag_invoke(
    boost::json::try_value_to_tag<Badge> /* tag */,
    const boost::json::value &jvRoot)
{
    if (!jvRoot.is_object())
    {
        EVENTSUB_BAIL_HERE(error::Kind::ExpectedObject);
    }
    const auto &root = jvRoot.get_object();

    const auto *jvsetID = root.if_contains("set_id");
    if (jvsetID == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto setID = boost::json::try_value_to<std::string>(*jvsetID);

    if (setID.has_error())
    {
        return setID.error();
    }

    const auto *jvid = root.if_contains("id");
    if (jvid == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto id = boost::json::try_value_to<std::string>(*jvid);

    if (id.has_error())
    {
        return id.error();
    }

    const auto *jvinfo = root.if_contains("info");
    if (jvinfo == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto info = boost::json::try_value_to<std::string>(*jvinfo);

    if (info.has_error())
    {
        return info.error();
    }

    return Badge{
        .setID = std::move(setID.value()),
        .id = std::move(id.value()),
        .info = std::move(info.value()),
    };
}

boost::json::result_for<Cheer, boost::json::value>::type tag_invoke(
    boost::json::try_value_to_tag<Cheer> /* tag */,
    const boost::json::value &jvRoot)
{
    if (!jvRoot.is_object())
    {
        EVENTSUB_BAIL_HERE(error::Kind::ExpectedObject);
    }
    const auto &root = jvRoot.get_object();

    static_assert(
        std::is_trivially_copyable_v<
            std::remove_reference_t<decltype(std::declval<Cheer>().bits)>>);
    const auto *jvbits = root.if_contains("bits");
    if (jvbits == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto bits = boost::json::try_value_to<int>(*jvbits);

    if (bits.has_error())
    {
        return bits.error();
    }

    return Cheer{
        .bits = bits.value(),
    };
}

boost::json::result_for<Reply, boost::json::value>::type tag_invoke(
    boost::json::try_value_to_tag<Reply> /* tag */,
    const boost::json::value &jvRoot)
{
    if (!jvRoot.is_object())
    {
        EVENTSUB_BAIL_HERE(error::Kind::ExpectedObject);
    }
    const auto &root = jvRoot.get_object();

    const auto *jvparentMessageID = root.if_contains("parent_message_id");
    if (jvparentMessageID == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto parentMessageID =
        boost::json::try_value_to<std::string>(*jvparentMessageID);

    if (parentMessageID.has_error())
    {
        return parentMessageID.error();
    }

    const auto *jvparentUserID = root.if_contains("parent_user_id");
    if (jvparentUserID == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto parentUserID = boost::json::try_value_to<std::string>(*jvparentUserID);

    if (parentUserID.has_error())
    {
        return parentUserID.error();
    }

    const auto *jvparentUserLogin = root.if_contains("parent_user_login");
    if (jvparentUserLogin == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto parentUserLogin =
        boost::json::try_value_to<std::string>(*jvparentUserLogin);

    if (parentUserLogin.has_error())
    {
        return parentUserLogin.error();
    }

    const auto *jvparentUserName = root.if_contains("parent_user_name");
    if (jvparentUserName == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto parentUserName =
        boost::json::try_value_to<std::string>(*jvparentUserName);

    if (parentUserName.has_error())
    {
        return parentUserName.error();
    }

    const auto *jvparentMessageBody = root.if_contains("parent_message_body");
    if (jvparentMessageBody == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto parentMessageBody =
        boost::json::try_value_to<std::string>(*jvparentMessageBody);

    if (parentMessageBody.has_error())
    {
        return parentMessageBody.error();
    }

    const auto *jvthreadMessageID = root.if_contains("thread_message_id");
    if (jvthreadMessageID == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto threadMessageID =
        boost::json::try_value_to<std::string>(*jvthreadMessageID);

    if (threadMessageID.has_error())
    {
        return threadMessageID.error();
    }

    const auto *jvthreadUserID = root.if_contains("thread_user_id");
    if (jvthreadUserID == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto threadUserID = boost::json::try_value_to<std::string>(*jvthreadUserID);

    if (threadUserID.has_error())
    {
        return threadUserID.error();
    }

    const auto *jvthreadUserLogin = root.if_contains("thread_user_login");
    if (jvthreadUserLogin == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto threadUserLogin =
        boost::json::try_value_to<std::string>(*jvthreadUserLogin);

    if (threadUserLogin.has_error())
    {
        return threadUserLogin.error();
    }

    const auto *jvthreadUserName = root.if_contains("thread_user_name");
    if (jvthreadUserName == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto threadUserName =
        boost::json::try_value_to<std::string>(*jvthreadUserName);

    if (threadUserName.has_error())
    {
        return threadUserName.error();
    }

    return Reply{
        .parentMessageID = std::move(parentMessageID.value()),
        .parentUserID = std::move(parentUserID.value()),
        .parentUserLogin = std::move(parentUserLogin.value()),
        .parentUserName = std::move(parentUserName.value()),
        .parentMessageBody = std::move(parentMessageBody.value()),
        .threadMessageID = std::move(threadMessageID.value()),
        .threadUserID = std::move(threadUserID.value()),
        .threadUserLogin = std::move(threadUserLogin.value()),
        .threadUserName = std::move(threadUserName.value()),
    };
}

boost::json::result_for<Event, boost::json::value>::type tag_invoke(
    boost::json::try_value_to_tag<Event> /* tag */,
    const boost::json::value &jvRoot)
{
    if (!jvRoot.is_object())
    {
        EVENTSUB_BAIL_HERE(error::Kind::ExpectedObject);
    }
    const auto &root = jvRoot.get_object();

    const auto *jvbroadcasterUserID = root.if_contains("broadcaster_user_id");
    if (jvbroadcasterUserID == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto broadcasterUserID =
        boost::json::try_value_to<std::string>(*jvbroadcasterUserID);

    if (broadcasterUserID.has_error())
    {
        return broadcasterUserID.error();
    }

    const auto *jvbroadcasterUserLogin =
        root.if_contains("broadcaster_user_login");
    if (jvbroadcasterUserLogin == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto broadcasterUserLogin =
        boost::json::try_value_to<std::string>(*jvbroadcasterUserLogin);

    if (broadcasterUserLogin.has_error())
    {
        return broadcasterUserLogin.error();
    }

    const auto *jvbroadcasterUserName =
        root.if_contains("broadcaster_user_name");
    if (jvbroadcasterUserName == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto broadcasterUserName =
        boost::json::try_value_to<std::string>(*jvbroadcasterUserName);

    if (broadcasterUserName.has_error())
    {
        return broadcasterUserName.error();
    }

    const auto *jvchatterUserID = root.if_contains("chatter_user_id");
    if (jvchatterUserID == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto chatterUserID =
        boost::json::try_value_to<std::string>(*jvchatterUserID);

    if (chatterUserID.has_error())
    {
        return chatterUserID.error();
    }

    const auto *jvchatterUserLogin = root.if_contains("chatter_user_login");
    if (jvchatterUserLogin == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto chatterUserLogin =
        boost::json::try_value_to<std::string>(*jvchatterUserLogin);

    if (chatterUserLogin.has_error())
    {
        return chatterUserLogin.error();
    }

    const auto *jvchatterUserName = root.if_contains("chatter_user_name");
    if (jvchatterUserName == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto chatterUserName =
        boost::json::try_value_to<std::string>(*jvchatterUserName);

    if (chatterUserName.has_error())
    {
        return chatterUserName.error();
    }

    const auto *jvcolor = root.if_contains("color");
    if (jvcolor == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto color = boost::json::try_value_to<std::string>(*jvcolor);

    if (color.has_error())
    {
        return color.error();
    }

    std::vector<Badge> vbadges;
    const auto *jvbadges = root.if_contains("badges");
    if (jvbadges != nullptr && !jvbadges->is_null())
    {
        auto badges = boost::json::try_value_to<std::vector<Badge>>(*jvbadges);
        if (badges.has_error())
        {
            return badges.error();
        }
        else
        {
            vbadges = std::move(badges.value());
        }
    }
    const auto *jvmessageID = root.if_contains("message_id");
    if (jvmessageID == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto messageID = boost::json::try_value_to<std::string>(*jvmessageID);

    if (messageID.has_error())
    {
        return messageID.error();
    }

    const auto *jvmessageType = root.if_contains("message_type");
    if (jvmessageType == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto messageType = boost::json::try_value_to<std::string>(*jvmessageType);

    if (messageType.has_error())
    {
        return messageType.error();
    }

    const auto *jvmessage = root.if_contains("message");
    if (jvmessage == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto message = boost::json::try_value_to<chat::Message>(*jvmessage);

    if (message.has_error())
    {
        return message.error();
    }

    static_assert(
        std::is_trivially_copyable_v<
            std::remove_reference_t<decltype(std::declval<Event>().cheer)>>);
    std::optional<Cheer> cheer = std::nullopt;
    const auto *jvcheer = root.if_contains("cheer");
    if (jvcheer != nullptr && !jvcheer->is_null())
    {
        auto tcheer = boost::json::try_value_to<Cheer>(*jvcheer);

        if (tcheer.has_error())
        {
            return tcheer.error();
        }
        cheer = tcheer.value();
    }

    std::optional<Reply> reply = std::nullopt;
    const auto *jvreply = root.if_contains("reply");
    if (jvreply != nullptr && !jvreply->is_null())
    {
        auto treply = boost::json::try_value_to<Reply>(*jvreply);

        if (treply.has_error())
        {
            return treply.error();
        }
        reply = std::move(treply.value());
    }

    std::optional<std::string> channelPointsCustomRewardID = std::nullopt;
    const auto *jvchannelPointsCustomRewardID =
        root.if_contains("channel_points_custom_reward_id");
    if (jvchannelPointsCustomRewardID != nullptr &&
        !jvchannelPointsCustomRewardID->is_null())
    {
        auto tchannelPointsCustomRewardID =
            boost::json::try_value_to<std::string>(
                *jvchannelPointsCustomRewardID);

        if (tchannelPointsCustomRewardID.has_error())
        {
            return tchannelPointsCustomRewardID.error();
        }
        channelPointsCustomRewardID =
            std::move(tchannelPointsCustomRewardID.value());
    }

    return Event{
        .broadcasterUserID = std::move(broadcasterUserID.value()),
        .broadcasterUserLogin = std::move(broadcasterUserLogin.value()),
        .broadcasterUserName = std::move(broadcasterUserName.value()),
        .chatterUserID = std::move(chatterUserID.value()),
        .chatterUserLogin = std::move(chatterUserLogin.value()),
        .chatterUserName = std::move(chatterUserName.value()),
        .color = std::move(color.value()),
        .badges = std::move(vbadges),
        .messageID = std::move(messageID.value()),
        .messageType = std::move(messageType.value()),
        .message = std::move(message.value()),
        .cheer = cheer,
        .reply = std::move(reply),
        .channelPointsCustomRewardID = std::move(channelPointsCustomRewardID),
    };
}

boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
    boost::json::try_value_to_tag<Payload> /* tag */,
    const boost::json::value &jvRoot)
{
    if (!jvRoot.is_object())
    {
        EVENTSUB_BAIL_HERE(error::Kind::ExpectedObject);
    }
    const auto &root = jvRoot.get_object();

    const auto *jvsubscription = root.if_contains("subscription");
    if (jvsubscription == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto subscription =
        boost::json::try_value_to<subscription::Subscription>(*jvsubscription);

    if (subscription.has_error())
    {
        return subscription.error();
    }

    const auto *jvevent = root.if_contains("event");
    if (jvevent == nullptr)
    {
        EVENTSUB_BAIL_HERE(error::Kind::FieldMissing);
    }

    auto event = boost::json::try_value_to<Event>(*jvevent);

    if (event.has_error())
    {
        return event.error();
    }

    return Payload{
        .subscription = std::move(subscription.value()),
        .event = std::move(event.value()),
    };
}

}  // namespace chatterino::eventsub::lib::payload::channel_chat_message::v1
