#include <QtTest>
#include <QSignalSpy>
#include <QStandardItemModel>

#include "../../../src/notification/notification-group-model.h"
#include "../../../src/notification/notification-model.h"

using namespace UkuiNotification;

namespace UkuiNotification {

static QList<uint> g_removedIds;
static NotificationModel *g_fakeModel = reinterpret_cast<NotificationModel *>(0x1);

NotificationModel *NotificationModel::instance()
{
    return g_fakeModel;
}

void NotificationModel::removeNotification(uint id)
{
    g_removedIds.append(id);
}

} // namespace UkuiNotification

class notificationGroupModel_test : public QObject
{
    Q_OBJECT

private slots:
    void test_setSourceModel_buildsGroups();
    void test_setSourceModel_sameModel_noChange();
    void test_setSourceModel_nullModel();
    void test_setData_updatesExpandState();
    void test_differentApp_createsSeparateGroup();
    void test_rowsInserted_updatesGrouping();
    void test_rowsRemoved_updatesGroups();
    void test_mapAndParentFunctions();
    void test_hasChildrenAndCounts();
    void test_dataChanged_reemitsForChild();
    void test_dataChanged_regroupsOnAppNameChange();
    void test_clearGroup_callsRemoveNotification();
    void test_stripRichText();
};

static QStandardItem *createGroupItem(const QString &appName, uint id, bool noFold = false)
{
    auto *item = new QStandardItem(appName);
    item->setData(id, NotificationItem::Id);
    item->setData(appName, NotificationItem::AppName);
    item->setData(noFold, NotificationItem::NoFold);
    item->setData(QString("<b>%1</b>").arg(appName), NotificationItem::Body);
    return item;
}

void notificationGroupModel_test::test_setSourceModel_buildsGroups()
{
    QStandardItemModel sourceModel;
    sourceModel.appendRow(createGroupItem("mail", 1));
    sourceModel.appendRow(createGroupItem("mail", 2));
    sourceModel.appendRow(createGroupItem("chat", 3));

    NotificationGroupModel model;
    model.setSourceModel(&sourceModel);

    QCOMPARE(model.rowCount(QModelIndex()), 2);
    QCOMPARE(model.data(model.index(0, 0, QModelIndex()), NotificationItem::GroupName).toString(), QString("chat"));
    QCOMPARE(model.data(model.index(0, 0, QModelIndex()), NotificationItem::GroupCount).toInt(), 1);
    QCOMPARE(model.data(model.index(1, 0, QModelIndex()), NotificationItem::GroupName).toString(), QString("mail"));
    QCOMPARE(model.rowCount(model.index(1, 0, QModelIndex())), 2);
}

void notificationGroupModel_test::test_setSourceModel_sameModel_noChange()
{
    QStandardItemModel sourceModel;
    sourceModel.appendRow(createGroupItem("mail", 1));

    NotificationGroupModel model;
    model.setSourceModel(&sourceModel);
    const int rowCount = model.rowCount(QModelIndex());

    model.setSourceModel(&sourceModel);

    QCOMPARE(model.rowCount(QModelIndex()), rowCount);
}

void notificationGroupModel_test::test_setSourceModel_nullModel()
{
    QStandardItemModel sourceModel;
    sourceModel.appendRow(createGroupItem("mail", 1));

    NotificationGroupModel model;
    model.setSourceModel(&sourceModel);
    model.setSourceModel(nullptr);

    QCOMPARE(model.rowCount(QModelIndex()), 0);
    QCOMPARE(model.columnCount(QModelIndex()), 1);
}

void notificationGroupModel_test::test_setData_updatesExpandState()
{
    QStandardItemModel sourceModel;
    sourceModel.appendRow(createGroupItem("mail", 1));

    NotificationGroupModel model;
    model.setSourceModel(&sourceModel);

    QModelIndex groupIndex = model.index(0, 0, QModelIndex());
    QVERIFY(model.setData(groupIndex, true, NotificationItem::GroupIsExpand));
    QCOMPARE(model.data(groupIndex, NotificationItem::GroupIsExpand).toBool(), true);
}

void notificationGroupModel_test::test_differentApp_createsSeparateGroup()
{
    QStandardItemModel sourceModel;
    sourceModel.appendRow(createGroupItem("mail", 1));
    sourceModel.appendRow(createGroupItem("chat", 2));

    NotificationGroupModel model;
    model.setSourceModel(&sourceModel);

    QCOMPARE(model.rowCount(QModelIndex()), 2);
    QCOMPARE(model.data(model.index(0, 0, QModelIndex()), NotificationItem::GroupCount).toInt(), 1);
    QCOMPARE(model.data(model.index(1, 0, QModelIndex()), NotificationItem::GroupCount).toInt(), 1);
}

void notificationGroupModel_test::test_rowsInserted_updatesGrouping()
{
    QStandardItemModel sourceModel;
    sourceModel.appendRow(createGroupItem("mail", 1));

    NotificationGroupModel model;
    model.setSourceModel(&sourceModel);

    QSignalSpy spy(&model, &QAbstractItemModel::rowsInserted);
    sourceModel.insertRow(1, createGroupItem("mail", 2));

    QVERIFY(spy.count() >= 1);
    QCOMPARE(model.rowCount(QModelIndex()), 1);
    QCOMPARE(model.data(model.index(0, 0, QModelIndex()), NotificationItem::GroupCount).toInt(), 2);
}

void notificationGroupModel_test::test_rowsRemoved_updatesGroups()
{
    QStandardItemModel sourceModel;
    sourceModel.appendRow(createGroupItem("mail", 1));
    sourceModel.appendRow(createGroupItem("mail", 2));

    NotificationGroupModel model;
    model.setSourceModel(&sourceModel);

    sourceModel.removeRow(0);

    QCOMPARE(model.rowCount(QModelIndex()), 1);
    QCOMPARE(model.data(model.index(0, 0, QModelIndex()), NotificationItem::GroupCount).toInt(), 1);
}

void notificationGroupModel_test::test_mapAndParentFunctions()
{
    QStandardItemModel sourceModel;
    sourceModel.appendRow(createGroupItem("mail", 1));
    sourceModel.appendRow(createGroupItem("mail", 2));

    NotificationGroupModel model;
    model.setSourceModel(&sourceModel);

    QModelIndex groupIndex = model.index(0, 0, QModelIndex());
    QModelIndex childIndex = model.index(0, 0, groupIndex);
    QModelIndex sourceChild = sourceModel.index(1, 0);

    QVERIFY(groupIndex.isValid());
    QVERIFY(childIndex.isValid());
    QCOMPARE(model.parent(childIndex), groupIndex);
    QCOMPARE(model.mapToSource(childIndex), sourceChild);
    QCOMPARE(model.mapFromSource(sourceChild), childIndex);
}

void notificationGroupModel_test::test_hasChildrenAndCounts()
{
    QStandardItemModel sourceModel;
    sourceModel.appendRow(createGroupItem("mail", 1));
    sourceModel.appendRow(createGroupItem("mail", 2));

    NotificationGroupModel model;
    model.setSourceModel(&sourceModel);

    QModelIndex groupIndex = model.index(0, 0, QModelIndex());
    QModelIndex childIndex = model.index(0, 0, groupIndex);

    QVERIFY(model.hasChildren(QModelIndex()));
    QVERIFY(model.hasChildren(groupIndex));
    QCOMPARE(model.hasChildren(childIndex), false);
    QCOMPARE(model.rowCount(childIndex), 0);
    QCOMPARE(model.columnCount(groupIndex), 1);
}

void notificationGroupModel_test::test_dataChanged_reemitsForChild()
{
    QStandardItemModel sourceModel;
    sourceModel.appendRow(createGroupItem("mail", 1));
    sourceModel.appendRow(createGroupItem("mail", 2));

    NotificationGroupModel model;
    model.setSourceModel(&sourceModel);
    QSignalSpy spy(&model, &QAbstractItemModel::dataChanged);

    sourceModel.setData(sourceModel.index(1, 0), QString("updated"), NotificationItem::Summary);

    QVERIFY(spy.count() >= 1);
}

void notificationGroupModel_test::test_dataChanged_regroupsOnAppNameChange()
{
    QStandardItemModel sourceModel;
    sourceModel.appendRow(createGroupItem("mail", 1));
    sourceModel.appendRow(createGroupItem("mail", 2));

    NotificationGroupModel model;
    model.setSourceModel(&sourceModel);

    sourceModel.setData(sourceModel.index(1, 0), QString("chat"), NotificationItem::AppName);

    QCOMPARE(model.rowCount(QModelIndex()), 2);
}

void notificationGroupModel_test::test_clearGroup_callsRemoveNotification()
{
    g_removedIds.clear();

    QStandardItemModel sourceModel;
    sourceModel.appendRow(createGroupItem("mail", 1));
    sourceModel.appendRow(createGroupItem("mail", 2));

    NotificationGroupModel model;
    model.setSourceModel(&sourceModel);
    model.clearGroup(model.index(0, 0, QModelIndex()));

    QCOMPARE(g_removedIds.size(), 2);
}

void notificationGroupModel_test::test_stripRichText()
{
    NotificationGroupModel model;

    QCOMPARE(model.stripRichText("<p>Hello <b>Qt</b></p>"), QString("Hello Qt"));
    QCOMPARE(model.stripRichText("plain"), QString("plain"));
}

QTEST_MAIN(notificationGroupModel_test)

#include "notification-group-model-test.moc"
