/*
    Copyright 2009 Constantin Berzan <exit3219@gmail.com>

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Library General Public License as published by
    the Free Software Foundation; either version 2 of the License, or (at your
    option) any later version.

    This library is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
    License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to the
    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#include "dupetest.h"

#include <QDBusInterface>
#include <QDBusReply>

#include <QDebug>

#include <AkonadiCore/Control>
#include <AkonadiCore/AgentInstance>
#include <AkonadiCore/AgentManager>
#include <AkonadiCore/CollectionFetchJob>
#include <AkonadiCore/ItemDeleteJob>
#include <AkonadiCore/ItemFetchJob>
#include <AkonadiCore/ItemFetchScope>
#include <AkonadiCore/qtest_akonadi.h>
#include <AkonadiCore/collectionpathresolver.h>

#include <mailtransport/messagequeuejob.h>
#include <mailtransport/transport.h>
#include <mailtransport/transportmanager.h>

#include <kmime/kmime_message.h>

static const int TIMEOUT_SECONDS = 60;
static const int MAXCOUNT = 99; // must be 2-digit!

using namespace Akonadi;
using namespace KMime;
using namespace MailTransport;

void DupeTest::initTestCase()
{
    QVERIFY(Control::start());
    QTest::qWait(1000);   // give the MDA time to start

    qRegisterMetaType<Akonadi::Item>();
    qRegisterMetaType<Akonadi::Collection>();

    // we need a default Akonadi transport
    int tid = TransportManager::self()->defaultTransportId();
    Transport *t = TransportManager::self()->transportById(tid);
    QVERIFY(t);
    QCOMPARE(t->type(), int(Transport::EnumType::Akonadi));

    // set the sink collection
    const QString rid = t->host();
    const AgentInstance agent = AgentManager::self()->instance(rid);
    QVERIFY(agent.isValid());
    CollectionPathResolver *resolver = new CollectionPathResolver(QStringLiteral("sink"), this);
    QVERIFY(resolver->exec());
    sink = Collection(resolver->collection());
    QVERIFY(sink.isValid());
    QDBusInterface conf(QLatin1String("org.freedesktop.Akonadi.Resource.") + rid,
                        QStringLiteral("/Settings"), QStringLiteral("org.kde.Akonadi.MailTransportDummy.Settings"));
    QVERIFY(conf.isValid());
    QDBusReply<void> reply = conf.call(QStringLiteral("setSink"), sink.id());
    QVERIFY(reply.isValid());
    agent.reconfigure();

    // set up monitor
    monitor = new Monitor(this);
    monitor->setCollectionMonitored(sink);
    monitor->itemFetchScope().fetchFullPayload();
}

void DupeTest::testDupes_data()
{
    QTest::addColumn<QString>("message");   // the prefix of the message to send (-msg## is appended)
    QTest::addColumn<int>("count");   // how many copies to send
    QTest::addColumn<int>("delay");   // number of ms to wait before sending next copy

    QTest::newRow("1-nodelay") << "\n1-nodelay" << 1 << 0;
    QTest::newRow("2-nodelay") << "\n2-nodelay" << 2 << 0;
    QTest::newRow("5-nodelay") << "\n5-nodelay" << 5 << 0;
    QTest::newRow("10-nodelay") << "\n10-nodelay" << 10 << 0;
    QTest::newRow("20-nodelay") << "\n20-nodelay" << 20 << 0;
    QTest::newRow("50-nodelay") << "\n50-nodelay" << 50 << 0;
    QTest::newRow("99-nodelay") << "\n99-nodelay" << 99 << 0;
    QTest::newRow("2-veryshortdelay") << "\n2-veryshortdelay" << 2 << 20;
    QTest::newRow("5-veryshortdelay") << "\n5-veryshortdelay" << 5 << 20;
    QTest::newRow("10-veryshortdelay") << "\n10-veryshortdelay" << 10 << 20;
    QTest::newRow("20-veryshortdelay") << "\n20-veryshortdelay" << 20 << 20;
    QTest::newRow("50-veryshortdelay") << "\n50-veryshortdelay" << 50 << 20;
    QTest::newRow("99-veryshortdelay") << "\n99-veryshortdelay" << 99 << 20;
    QTest::newRow("2-shortdelay") << "\n2-shortdelay" << 2 << 100;
    QTest::newRow("5-shortdelay") << "\n5-shortdelay" << 5 << 100;
    QTest::newRow("10-shortdelay") << "\n10-shortdelay" << 10 << 100;
    QTest::newRow("20-shortdelay") << "\n20-shortdelay" << 20 << 100;
    QTest::newRow("50-shortdelay") << "\n50-shortdelay" << 50 << 100;
    QTest::newRow("99-shortdelay") << "\n99-shortdelay" << 99 << 99;
    QTest::newRow("2-longdelay") << "\n2-longdelay" << 2 << 1000;
    QTest::newRow("5-longdelay") << "\n5-longdelay" << 5 << 1000;
    QTest::newRow("5-verylongdelay") << "\n5-verylongdelay" << 5 << 4000;
    Q_ASSERT(99 <= MAXCOUNT);
    Q_ASSERT(MAXCOUNT < 100);   // 2-digit

    // TODO I'm not sure more items means a better test
    // TODO test large items
    // TODO test modifying items while they are being sent...
}

void DupeTest::testDupes()
{
    QFETCH(QString, message);
    QFETCH(int, count);
    QFETCH(int, delay);

    // clean sink
    ItemFetchJob *fjob = new ItemFetchJob(sink, this);
    AKVERIFYEXEC(fjob);
    if (fjob->items().count() > 0) {
        // this test is needed because ItemDeleteJob gives error if no items are found
        ItemDeleteJob *djob = new ItemDeleteJob(sink, this);
        AKVERIFYEXEC(djob);
    }
    fjob = new ItemFetchJob(sink, this);
    AKVERIFYEXEC(fjob);
    QCOMPARE(fjob->items().count(), 0);

    // queue messages
    Q_ASSERT(monitor);
    QSignalSpy *addSpy = new QSignalSpy(monitor, SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection)));
    qDebug() << "Queuing" << count << "messages...";
    for (int i = 0; i < count; i++) {
        //qDebug() << "Queuing message" << i + 1 << "of" << count;

        Message::Ptr msg = Message::Ptr(new Message);
        msg->setContent(QString::fromLatin1("%1-msg%2\n").arg(message).arg(i + 1, 2, 10, QLatin1Char('0')).toLatin1());

        MessageQueueJob *job = new MessageQueueJob(this);
        job->setMessage(msg);
        job->transportAttribute().setTransportId(TransportManager::self()->defaultTransportId());
        // default dispatch mode
        // default sent-mail collection
        job->addressAttribute().setFrom(QStringLiteral("naiba"));
        job->addressAttribute().setTo(QStringList() << QStringLiteral("dracu"));
        //AKVERIFYEXEC( job );
        job->start();
        QTest::qWait(delay);
    }
    qDebug() << "Queued" << count << "messages.";

    // wait for the MDA to send them
    int seconds = 0;
    while (true) {
        seconds++;
        QTest::qWait(1000);
        qDebug() << seconds << "seconds elapsed." << addSpy->count() << "messages got to sink.";
        if (addSpy->count() >= count) {
            break;
        }

#if 0
        if (seconds >= TIMEOUT_SECONDS) {
            qDebug() << "Timeout, gdb master!";
            QTest::qWait(1000 * 1000);
        }
#endif
        QVERIFY2(seconds < TIMEOUT_SECONDS, "Timeout");
    }

    // TODO I should verify that the MDA has actually finished its work and has an empty queue
    QTest::qWait(2000);

    // verify what has been sent
    fjob = new ItemFetchJob(sink, this);
    fjob->fetchScope().fetchFullPayload();
    AKVERIFYEXEC(fjob);
    const Item::List items = fjob->items();
    int found[ MAXCOUNT ];
    for (int i = 0; i < count; i++) {
        found[i] = 0;
    }
    for (int i = 0; i < items.count(); i++) {
        QVERIFY(items[i].hasPayload<Message::Ptr>());
        Message::Ptr msg = items[i].payload<Message::Ptr>();
        const QByteArray content = msg->encodedContent();
        //qDebug() << "i" << i << "content" << content;
        int loc = content.indexOf("-msg");
        QVERIFY(loc >= 0);
        bool ok;
        int who = content.mid(loc + 4, 2).toInt(&ok);
        QVERIFY(ok);
        //qDebug() << "identified msg" << who;
        QVERIFY(who > 0 && who <= count);
        found[ who - 1 ]++;
    }
    for (int i = 0; i < count; i++) {
        if (found[i] > 1) {
            qDebug() << "found duplicate message" << i + 1 << "(" << found[i] << "times )";
        } else if (found[i] < 1) {
            qDebug() << "didn't find message" << i + 1;
        }
        QCOMPARE(found[i], 1);
    }
    QCOMPARE(addSpy->count(), count);
    QCOMPARE(items.count(), count);
}

QTEST_AKONADIMAIN(DupeTest)

