#include <QtTest>
#include <QGuiApplication>
#include <QQuickItem>
#include <QScopedPointer>
#include <QTemporaryDir>

#include "../../../src/model/edit-shortcut-model.h"
#include "../../../src/model/shortcut-config.h"
#include "../../../src/model/shortcut-model.h"

using namespace UkuiShortcut;

namespace {
QScopedPointer<QTemporaryDir> g_tempDir;

void setupConfigEnvironment()
{
    if (!g_tempDir) {
        g_tempDir.reset(new QTemporaryDir);
    }

    const QByteArray configHome = g_tempDir->path().toUtf8();
    qputenv("XDG_CONFIG_HOME", configHome);
    qputenv("HOME", configHome);
}
}

class editShortcutModel_test : public QObject
{
    Q_OBJECT

private slots:
    void initTestCase();

    void test_addModel_filtersAddedShortcuts();
    void test_notAddModel_filtersNotAddedShortcuts();
    void test_updateItemRect_assignsRectsAndRows();
    void test_updateItemRect_wrapsRowsWhenColumnSpanExceedsColumns();
    void test_findTargetItem_hitsLeftHalf();
    void test_findTargetItem_hitsRightHalf();
    void test_findTargetItem_returnsMinusOneWhenOutside();
    void test_findTargetItem_rightToLeftCoordinates();
    void test_moveItem_updatesAddedOrder();
    void test_setOrder_movesItemAcrossStates();
    void test_changeState_movesItemToBeginningWhenRequested();

private:
    ShortcutModel *m_sourceModel = nullptr;
    EditShortcutModel *m_addModel = nullptr;
    EditShortcutModel *m_notAddModel = nullptr;

    QQuickItem *createChildItem(QQuickItem *parent, int columnSpan) const;
};

void editShortcutModel_test::initTestCase()
{
    m_sourceModel = new ShortcutModel(this);
    m_addModel = m_sourceModel->getModel(PluginStateType::Add);
    m_notAddModel = m_sourceModel->getModel(PluginStateType::NotAdd);

    QVERIFY(m_sourceModel != nullptr);
    QVERIFY(m_addModel != nullptr);
    QVERIFY(m_notAddModel != nullptr);
}

void editShortcutModel_test::test_addModel_filtersAddedShortcuts()
{
    QVERIFY(m_addModel->rowCount() > 0);

    for (int row = 0; row < m_addModel->rowCount(); ++row) {
        const QModelIndex index = m_addModel->index(row, 0);
        QCOMPARE(index.data(ShortcutModel::AddState).toInt(), static_cast<int>(PluginStateType::Add));
        QCOMPARE(index.data(ShortcutModel::FixState).toInt(), static_cast<int>(PluginStateType::NotFix));
    }
}

void editShortcutModel_test::test_notAddModel_filtersNotAddedShortcuts()
{
    QVERIFY(m_notAddModel->rowCount() > 0);

    for (int row = 0; row < m_notAddModel->rowCount(); ++row) {
        const QModelIndex index = m_notAddModel->index(row, 0);
        QCOMPARE(index.data(ShortcutModel::AddState).toInt(), static_cast<int>(PluginStateType::NotAdd));
        QCOMPARE(index.data(ShortcutModel::FixState).toInt(), static_cast<int>(PluginStateType::NotFix));
    }
}

QQuickItem *editShortcutModel_test::createChildItem(QQuickItem *parent, int columnSpan) const
{
    auto *item = new QQuickItem(parent);
    item->setProperty("columnSpan", columnSpan);
    return item;
}

void editShortcutModel_test::test_updateItemRect_assignsRectsAndRows()
{
    QQuickItem flowItem;
    flowItem.setWidth(500);

    createChildItem(&flowItem, 1);
    createChildItem(&flowItem, 2);
    createChildItem(&flowItem, 1);
    createChildItem(&flowItem, 0);

    m_addModel->updateItemRect(&flowItem);

    const QList<QQuickItem *> items = flowItem.childItems();
    QCOMPARE(items.at(0)->property("targetRect").toRect(), QRect(0, 0, 80, 72));
    QCOMPARE(items.at(1)->property("targetRect").toRect(), QRect(92, 0, 172, 72));
    QCOMPARE(items.at(2)->property("targetRect").toRect(), QRect(276, 0, 80, 72));
    QVERIFY(!items.at(3)->property("targetRect").isValid());
    QCOMPARE(flowItem.property("rows").toInt(), 1);
}

void editShortcutModel_test::test_updateItemRect_wrapsRowsWhenColumnSpanExceedsColumns()
{
    QQuickItem flowItem;
    flowItem.setWidth(500);

    createChildItem(&flowItem, 3);
    createChildItem(&flowItem, 2);
    createChildItem(&flowItem, 1);

    m_addModel->updateItemRect(&flowItem);

    const QList<QQuickItem *> items = flowItem.childItems();
    QCOMPARE(items.at(0)->property("targetRect").toRect(), QRect(0, 0, 264, 72));
    QCOMPARE(items.at(1)->property("targetRect").toRect(), QRect(0, 84, 172, 72));
    QCOMPARE(items.at(2)->property("targetRect").toRect(), QRect(184, 84, 80, 72));
    QCOMPARE(flowItem.property("rows").toInt(), 2);
}

void editShortcutModel_test::test_findTargetItem_hitsLeftHalf()
{
    QQuickItem flowItem;
    flowItem.setWidth(500);
    createChildItem(&flowItem, 2);
    m_addModel->updateItemRect(&flowItem);

    QObject result;
    m_addModel->findTargetItem(20, 20, &result);

    QCOMPARE(result.property("index").toInt(), 0);
    QCOMPARE(result.property("offset").toInt(), 0);
}

void editShortcutModel_test::test_findTargetItem_hitsRightHalf()
{
    QQuickItem flowItem;
    flowItem.setWidth(500);
    createChildItem(&flowItem, 2);
    m_addModel->updateItemRect(&flowItem);

    QObject result;
    m_addModel->findTargetItem(150, 20, &result);

    QCOMPARE(result.property("index").toInt(), 0);
    QCOMPARE(result.property("offset").toInt(), 1);
}

void editShortcutModel_test::test_findTargetItem_returnsMinusOneWhenOutside()
{
    QQuickItem flowItem;
    flowItem.setWidth(500);
    createChildItem(&flowItem, 1);
    m_addModel->updateItemRect(&flowItem);

    QObject result;
    m_addModel->findTargetItem(300, 200, &result);

    QCOMPARE(result.property("index").toInt(), -1);
    QCOMPARE(result.property("offset").toInt(), 0);
}

void editShortcutModel_test::test_findTargetItem_rightToLeftCoordinates()
{
    QQuickItem flowItem;
    flowItem.setWidth(400);
    createChildItem(&flowItem, 1);
    m_addModel->updateItemRect(&flowItem);

    qGuiApp->setLayoutDirection(Qt::RightToLeft);

    QObject result;
    m_addModel->findTargetItem(390, 20, &result);

    QCOMPARE(result.property("index").toInt(), 0);
    QCOMPARE(result.property("offset").toInt(), -1);

    qGuiApp->setLayoutDirection(Qt::LeftToRight);
}

void editShortcutModel_test::test_moveItem_updatesAddedOrder()
{
    QVERIFY(m_addModel->rowCount() >= 2);

    const QString firstId = m_addModel->index(0, 0).data(ShortcutModel::PluginId).toString();
    const QString secondId = m_addModel->index(1, 0).data(ShortcutModel::PluginId).toString();
    const int targetPluginIndex = m_addModel->index(1, 0).data(ShortcutModel::PluginIndex).toInt();

    m_addModel->moveItem(0, 1);

    QCOMPARE(ShortcutConfig::instance().getShortcutIndex(firstId, PluginStateType::Add), targetPluginIndex);
    QVERIFY(ShortcutConfig::instance().getShortcutIndex(secondId, PluginStateType::Add) >= 0);
}

void editShortcutModel_test::test_setOrder_movesItemAcrossStates()
{
    QVERIFY(m_notAddModel->rowCount() > 0);

    const QString pluginId = m_notAddModel->index(0, 0).data(ShortcutModel::PluginId).toString();
    const int targetPluginIndex = m_addModel->index(0, 0).data(ShortcutModel::PluginIndex).toInt();

    m_addModel->setOrder(pluginId, 0);

    QCOMPARE(static_cast<int>(ShortcutConfig::instance().getShortcutState(pluginId)), static_cast<int>(PluginStateType::Add));
    QCOMPARE(ShortcutConfig::instance().getShortcutIndex(pluginId, PluginStateType::Add), targetPluginIndex);
}

void editShortcutModel_test::test_changeState_movesItemToBeginningWhenRequested()
{
    QVERIFY(m_addModel->rowCount() > 0);

    const QString pluginId = m_addModel->index(0, 0).data(ShortcutModel::PluginId).toString();

    m_addModel->changeState(pluginId, PluginStateType::Add, false);

    QCOMPARE(static_cast<int>(ShortcutConfig::instance().getShortcutState(pluginId)), static_cast<int>(PluginStateType::NotAdd));
    QCOMPARE(ShortcutConfig::instance().getShortcutIndex(pluginId, PluginStateType::NotAdd), 0);
}

int main(int argc, char *argv[])
{
    setupConfigEnvironment();
    QGuiApplication app(argc, argv);

    editShortcutModel_test test;
    return QTest::qExec(&test, argc, argv);
}

#include "edit-shortcut-model-test.moc"
