// SPDX-FileCopyrightText: 2022 Contributors to Chatterino <https://chatterino.com>
//
// SPDX-License-Identifier: MIT

#include "providers/seventv/eventapi/Dispatch.hpp"

#include "providers/seventv/SeventvEmotes.hpp"
#include "util/QMagicEnum.hpp"

#include <QJsonArray>

#include <utility>

using namespace Qt::Literals;

namespace chatterino::seventv::eventapi {

Dispatch::Dispatch(QJsonObject obj)
    : type(qmagicenum::enumCast<SubscriptionType>(obj["type"].toString())
               .value_or(SubscriptionType::INVALID))
    , body(obj["body"].toObject())
    , id(this->body["id"].toString())
    , actorName(this->body["actor"].toObject()["display_name"].toString())
{
}

EmoteAddDispatch::EmoteAddDispatch(const Dispatch &dispatch, QJsonObject emote)
    : emoteSetID(dispatch.id)
    , actorName(dispatch.actorName)
    , emoteJson(std::move(emote))
    , emoteID(this->emoteJson["id"].toString())
{
}

bool EmoteAddDispatch::validate() const
{
    bool validValues =
        !this->emoteSetID.isEmpty() && !this->emoteJson.isEmpty();
    if (!validValues)
    {
        return false;
    }
    bool validActiveEmote = this->emoteJson.contains("id") &&
                            this->emoteJson.contains("name") &&
                            this->emoteJson.contains("data");
    if (!validActiveEmote)
    {
        return false;
    }
    auto emoteData = this->emoteJson["data"].toObject();
    return emoteData.contains("name") && emoteData.contains("host") &&
           emoteData.contains("owner");
}

EmoteRemoveDispatch::EmoteRemoveDispatch(const Dispatch &dispatch,
                                         QJsonObject emote)
    : emoteSetID(dispatch.id)
    , actorName(dispatch.actorName)
    , emoteName(emote["name"].toString())
    , emoteID(emote["id"].toString())
{
}

bool EmoteRemoveDispatch::validate() const
{
    return !this->emoteSetID.isEmpty() && !this->emoteName.isEmpty() &&
           !this->emoteID.isEmpty();
}

EmoteUpdateDispatch::EmoteUpdateDispatch(const Dispatch &dispatch,
                                         QJsonObject oldValue,
                                         QJsonObject value)
    : emoteSetID(dispatch.id)
    , actorName(dispatch.actorName)
    , emoteID(value["id"].toString())
    , oldEmoteName(oldValue["name"].toString())
    , emoteName(value["name"].toString())
{
}

bool EmoteUpdateDispatch::validate() const
{
    return !this->emoteSetID.isEmpty() && !this->emoteID.isEmpty() &&
           !this->oldEmoteName.isEmpty() && !this->emoteName.isEmpty() &&
           this->oldEmoteName != this->emoteName;
}

UserConnectionUpdateDispatch::UserConnectionUpdateDispatch(
    const Dispatch &dispatch, const QJsonObject &update, size_t connectionIndex)
    : userID(dispatch.id)
    , actorName(dispatch.actorName)
    , oldEmoteSetID(update["old_value"].toObject()["id"].toString())
    , emoteSetID(update["value"].toObject()["id"].toString())
    , connectionIndex(connectionIndex)
{
}

bool UserConnectionUpdateDispatch::validate() const
{
    return !this->userID.isEmpty() && !this->oldEmoteSetID.isEmpty() &&
           !this->emoteSetID.isEmpty();
}

CosmeticCreateDispatch::CosmeticCreateDispatch(const Dispatch &dispatch)
    : data(dispatch.body["object"]["data"].toObject())
    , kind(qmagicenum::enumCast<CosmeticKind>(
               dispatch.body["object"]["kind"].toString())
               .value_or(CosmeticKind::INVALID))
{
}

bool CosmeticCreateDispatch::validate() const
{
    return !this->data.empty() && this->kind != CosmeticKind::INVALID;
}

TwitchUser::TwitchUser(const QJsonObject &connection)
    : id(connection["id"_L1].toString())
    , userName(connection["username"_L1].toString())
{
}

KickUser::KickUser(const QJsonObject &connection)
    : id(connection["id"_L1].toString().toULongLong())
    , userName(connection["username"_L1].toString().toLower())
{
}

EntitlementCreateDeleteDispatch::EntitlementCreateDeleteDispatch(
    const Dispatch &dispatch)
{
    const auto obj = dispatch.body["object"].toObject();
    this->refID = obj["ref_id"].toString();
    this->kind = qmagicenum::enumCast<CosmeticKind>(obj["kind"].toString())
                     .value_or(CosmeticKind::INVALID);

    const auto userObj = obj["user"_L1].toObject();
    this->seventvUsername = userObj["username"_L1].toString();
    const auto userConnections = userObj["connections"_L1].toArray();
    for (const auto &connectionJson : userConnections)
    {
        const auto connection = connectionJson.toObject();
        auto platform = connection["platform"_L1].toString();
        if (platform == u"TWITCH")
        {
            this->connections.emplace_back(TwitchUser(connection));
        }
        else if (platform == u"KICK")
        {
            this->connections.emplace_back(KickUser(connection));
        }
    }
}

bool EntitlementCreateDeleteDispatch::validate() const
{
    return !this->connections.empty() && !this->refID.isEmpty() &&
           this->kind != CosmeticKind::INVALID;
}

EmoteSetCreateDispatch::EmoteSetCreateDispatch(const QJsonObject &emoteSet)
    : emoteSetID(emoteSet["id"].toString())
    , isPersonalOrCommercial(SeventvEmoteSetFlags{
          static_cast<SeventvEmoteSetFlag>(emoteSet["flags"].toInt()),
      }
                                 .hasAny(SeventvEmoteSetFlag::Personal,
                                         SeventvEmoteSetFlag::Commercial))
{
}

bool EmoteSetCreateDispatch::validate() const
{
    return !this->emoteSetID.isEmpty();
}

}  // namespace chatterino::seventv::eventapi
