/*
 * Copyright (C) 2022 Ubports Foundation
 *
 * This file is part of lomiri-history-service.
 *
 * lomiri-history-service is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 3.
 *
 * lomiri-history-service 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <QCryptographicHash>
#include <QtCore/QObject>
#include <QtTest/QtTest>

#include "config.h"
#include "mmsstoragemanager_p.h"
#include "types.h"

Q_DECLARE_METATYPE(History::EventType)

class MmsStorageManagerTest : public QObject
{
    Q_OBJECT

private Q_SLOTS:
    void initTestCase();
      void testMmsStoragePath();
      void testSaveAttachment();
      void testRemoveAttachments();
      void testRemoveAttachmentsFromThreads();
      void testRemoveAttachmentsFromEvents();
      void testRemoveAttachmentsFromFilePaths();
      void cleanup();

private:
      QString storeFile(const QString &accountId, const QString &threadId, const QString &eventId);
};

void MmsStorageManagerTest::initTestCase()
{
    qRegisterMetaType<History::EventType>();
}

void MmsStorageManagerTest::cleanup()
{
    QString accountPath = History::MmsStorageManager::instance()->mmsStoragePath("accountId");
    QDir(accountPath).removeRecursively();
}

void MmsStorageManagerTest::testMmsStoragePath()
{
    QString normalizedAccountId = QString(QCryptographicHash::hash("accountId", QCryptographicHash::Md5).toHex());
    QString normalizedThreadId = QString(QCryptographicHash::hash("threadId", QCryptographicHash::Md5).toHex());
    QString normalizedEventId = QString(QCryptographicHash::hash("eventId", QCryptographicHash::Md5).toHex());

    QString rootPath = History::MmsStorageManager::instance()->storageRootPath();

    QString threadPath = History::MmsStorageManager::instance()->mmsStoragePath("accountId", "threadId");
    QString expectedSubPath = QString("/%1/%2").arg(normalizedAccountId, normalizedThreadId);
    QString expectedPath = QString("%1%2").arg(rootPath).arg(expectedSubPath);
    QCOMPARE(threadPath,  expectedPath);
    threadPath = History::MmsStorageManager::instance()->mmsStoragePath("accountId", "threadId", "");
    QCOMPARE(threadPath,  expectedPath);


    QString eventPath = History::MmsStorageManager::instance()->mmsStoragePath("accountId", "threadId", "eventId");
    expectedSubPath = QString("/%1/%2/%3").arg(normalizedAccountId, normalizedThreadId, normalizedEventId);
    expectedPath = QString("%1%2").arg(rootPath).arg(expectedSubPath);
    QCOMPARE(eventPath,  expectedPath);
}

void MmsStorageManagerTest::testSaveAttachment()
{
    QFile file(QString("%1/dialer-app.png").arg(TEST_DATA_DIR));
    qint64 size = file.size();
    QByteArray ba;
    file.open(QIODevice::ReadOnly);
    ba = file.readAll();

    Tp::MessagePart part;
    part["identifier"] = QDBusVariant("dialer-app.png");
    part["content"] = QDBusVariant(ba);

    QString path = History::MmsStorageManager::instance()->saveAttachment(part, "accountId", "threadId", "eventId");
    QString eventStorage = History::MmsStorageManager::instance()->mmsStoragePath("accountId", "threadId", "eventId");
    QString expectedPath = QString("%1/%2").arg(eventStorage).arg("dialer-app.png");
    QFile storedFile(expectedPath);
    QCOMPARE(path, expectedPath);
    QCOMPARE(storedFile.size(), size);

    // check if filename strip chars
    part["identifier"] = QDBusVariant("<dial/er-app2.png>");
    path = History::MmsStorageManager::instance()->saveAttachment(part, "accountId", "threadId", "eventId");
    eventStorage = History::MmsStorageManager::instance()->mmsStoragePath("accountId", "threadId", "eventId");
    expectedPath = QString("%1/%2").arg(eventStorage).arg("dialer-app2.png");
    QFile storedFile2(expectedPath);
    QCOMPARE(path, expectedPath);
    QCOMPARE(storedFile2.size(), size);

    // if empty identifier, filename should be a simple int value from the internal counter
    part["identifier"] = QDBusVariant("");

    QDir dir(eventStorage);
    dir.setFilter( QDir::AllEntries | QDir::NoDotAndDotDot );
    int dirCount = dir.count();

    dirCount++;
    path = History::MmsStorageManager::instance()->saveAttachment(part, "accountId", "threadId", "eventId");
    expectedPath = QString("%1/%2").arg(eventStorage).arg(QString::number(dirCount));
    QCOMPARE(path, expectedPath);

    // make sure filenames are incremented
    dirCount++;
    path = History::MmsStorageManager::instance()->saveAttachment(part, "accountId", "threadId", "eventId");
    expectedPath = QString("%1/%2").arg(eventStorage).arg(QString::number(dirCount));
    QCOMPARE(path, expectedPath);

    dirCount++;
    part["identifier"] = QDBusVariant("<>");
    path = History::MmsStorageManager::instance()->saveAttachment(part, "accountId", "threadId", "eventId");
    expectedPath = QString("%1/%2").arg(eventStorage).arg(QString::number(dirCount));
    QCOMPARE(path, expectedPath);

}


void MmsStorageManagerTest::testRemoveAttachments()
{

    QString attachment1 = storeFile("accountId", "threadId", "eventId1");
    QVERIFY(QFile(attachment1).exists());
    QString attachment2 = storeFile("accountId", "threadId", "eventId2");
    QVERIFY(QFile(attachment2).exists());

    History::MmsStorageManager::instance()->removeAttachments("accountId", "threadId");
    // thread folder should be removed
    QVERIFY(!QFileInfo(attachment2).dir().exists());

    attachment1 = storeFile("accountId", "threadId", "eventId1");
    QVERIFY(QFile(attachment1).exists());
    attachment2 = storeFile("accountId", "threadId", "eventId2");
    QVERIFY(QFile(attachment2).exists());

    History::MmsStorageManager::instance()->removeAttachments("accountId", "threadId", "eventId1");
    QVERIFY(!QFile(attachment1).exists());
}

void MmsStorageManagerTest::testRemoveAttachmentsFromThreads()
{
    QString attachment1 = storeFile("accountId", "threadId1", "eventId1");
    QVERIFY(QFile(attachment1).exists());
    QString attachment2 = storeFile("accountId", "threadId2", "eventId2");
    QVERIFY(QFile(attachment2).exists());

    QList<QVariantMap> threads;
    QVariantMap thread;
    thread[History::FieldType] = QVariant::fromValue(History::EventTypeText);
    thread[History::FieldAccountId] = QVariant("accountId");
    thread[History::FieldThreadId] = QVariant("threadId1");

    QVariantMap thread2;
    thread2[History::FieldType] = QVariant::fromValue(History::EventTypeText);
    thread2[History::FieldAccountId] = QVariant("accountId");
    thread2[History::FieldThreadId] = QVariant("threadId2");

    threads << thread << thread2;

    History::MmsStorageManager::instance()->removeAttachmentsFromThreads(threads);
    // no more threads directories
    QString accountPath = History::MmsStorageManager::instance()->mmsStoragePath("accountId");
    QVERIFY(QDir(accountPath).isEmpty());
}

void MmsStorageManagerTest::testRemoveAttachmentsFromEvents()
{
    QString attachment1 = storeFile("accountId", "threadId1", "eventId1");
    QVERIFY(QFile(attachment1).exists());
    QString attachment2 = storeFile("accountId", "threadId2", "eventId2");
    QVERIFY(QFile(attachment2).exists());

    QList<QVariantMap> events;
    QVariantMap event;
    event[History::FieldType] = QVariant::fromValue(History::EventTypeText);
    event[History::FieldAccountId] = QVariant("accountId");
    event[History::FieldThreadId] = QVariant("threadId1");
    event[History::FieldEventId] = QVariant("eventId1");

    events << event;

    History::MmsStorageManager::instance()->removeAttachmentsFromEvents(events);
    QVERIFY(QFileInfo(attachment1).dir().isEmpty());
    QVERIFY(QFileInfo(attachment2).exists());

    QVariantMap event2;
    event2[History::FieldType] = QVariant::fromValue(History::EventTypeText);
    event2[History::FieldAccountId] = QVariant("accountId");
    event2[History::FieldThreadId] = QVariant("threadId2");
    event2[History::FieldEventId] = QVariant("eventId2");

    events << event2;

    History::MmsStorageManager::instance()->removeAttachmentsFromEvents(events);
    // no more events
    QVERIFY(QFileInfo(attachment1).dir().isEmpty());
    QVERIFY(QFileInfo(attachment2).dir().isEmpty());
}

void MmsStorageManagerTest::testRemoveAttachmentsFromFilePaths()
{
    QList<QString> filePaths;
    int maxFile = 20;
    for (int i = 0; i < maxFile; ++i) {
        QString attachment = storeFile("accountId", "threadId", QString("eventId%1").arg(i));
        filePaths << attachment;
    }
    QString threadPath = History::MmsStorageManager::instance()->mmsStoragePath("accountId", "threadId");
    auto dirInfoList = QDir(threadPath).entryInfoList(QDir::NoDotAndDotDot|QDir::AllEntries);
    QCOMPARE(dirInfoList.size(), maxFile);

    History::MmsStorageManager::instance()->removeAttachments(filePaths);
    // the thread directory should have been deleted too
    QVERIFY(QFileInfo(threadPath).dir().isEmpty());
}

QString MmsStorageManagerTest::storeFile(const QString &accountId, const QString &threadId, const QString &eventId)
{
    // prepare some attachments, store them
    QString mmsStoragePath = History::MmsStorageManager::instance()->mmsStoragePath(accountId, threadId, eventId);
    QString attachmentPath = mmsStoragePath + "/dialer-app.png";

    QDir dir(mmsStoragePath);
    if (!dir.mkpath(mmsStoragePath)) {
        qWarning() << "fail to create" << mmsStoragePath;
    }
    QFile::copy(QString("%1/dialer-app.png").arg(TEST_DATA_DIR), attachmentPath);
    return attachmentPath;
}

QTEST_MAIN(MmsStorageManagerTest)
#include "MmsStorageManagerTest.moc"
