#include <QTest>

#include "music_manager.h"

namespace tests {
namespace unittests {

/**
 * @brief Unit Tester class for the musiclist-related functions.
 */
class MusicListManager : public QObject
{
    Q_OBJECT

  public:
    MusicManager *m_music_manager;

  private slots:
    /**
     * @brief Initialises every tests with creating a new MusicManager with a small sample root list.
     */
    void init();

    /**
     * @brief Tests the registration of areas in the music manager.
     */
    void registerArea();

    /**
     * @brief Tests toggling the enabling/disabling of the prepend behaviour of our root list.
     */
    void toggleRootEnabled();

    /**
     * @brief The data function for validateSong()
     */
    void validateSong_data();

    /**
     * @brief Tests validation of song candidates.
     */
    void validateSong();

    /**
     * @brief Tests the addition of custom music.
     */
    void addCustomSong();

    /**
     * @brief Tests the addition of a custom category.
     */
    void addCustomCategory();

    /**
     * @brief Test the sanitisation of the custom list when root prepend is reenabled.
     */
    void sanitiseCustomList();

    /**
     * @brief Tests the removing of custom songs and categories.
     */
    void removeCategorySong();

    /**
     * @brief Tests the retrieval of song information.
     */
    void songInformation();

    /**
     * @brief Tests the retrieval of the full musiclist for an area.
     */
    void musiclist();
};

void MusicListManager::init()
{
    QMap<QString, QPair<QString, int>> l_test_list;
    l_test_list.insert("==Music==", {"==Music==", 0});
    l_test_list.insert("Announce The Truth (AJ).opus", {"Announce The Truth (AJ).opus", 59});
    l_test_list.insert("Announce The Truth (JFA).opus", {"Announce The Truth (JFA).opus", 98});

    QStringList l_list = {};
    l_list << "==Music=="
           << "Announce The Truth (AJ).opus"
           << "Announce The Truth (JFA).opus";

    m_music_manager = new MusicManager({"my.cdn.com", "your.cdn.com"}, l_test_list, l_list, nullptr);
}

void MusicListManager::registerArea()
{
    {
        // We register a single area with the music manager of ID 0.
        // Creation should work as there are no other areas yet.
        bool l_creation_success = m_music_manager->registerArea(0);
        QCOMPARE(l_creation_success, true);
    }
    {
        // Someone tries to register the same area again!
        // This should fail as this area already exists.
        bool l_creation_success = m_music_manager->registerArea(0);
        QCOMPARE(l_creation_success, false);
    }
}

void MusicListManager::toggleRootEnabled()
{
    {
        // We register an area of ID0 and toggle the inclusion of global list.
        m_music_manager->registerArea(0);
        QCOMPARE(m_music_manager->toggleRootEnabled(0), false);
    }
    {
        // We toggle it again. It should return true now.
        // Since this is now true, we should have the root list with customs cleared.
        QCOMPARE(m_music_manager->toggleRootEnabled(0), true);
    }
}

void MusicListManager::validateSong_data()
{
    // Songname can also be the realname.
    QTest::addColumn<QString>("songname");
    QTest::addColumn<bool>("expectedResult");

    QTest::addRow("Songname - No extension") << "Announce The Truth (AA)" << false;
    QTest::addRow("Songname - Valid Extension") << "Announce The Truth (AA).opus" << true;
    QTest::addRow("Songname - Invalid Extension") << "Announce The Truth (AA).aac" << false;
    QTest::addRow("URL - Valid primary") << "https://my.cdn.com/mysong.opus" << true;
    QTest::addRow("URL - Valid secondary") << "https://your.cdn.com/mysong.opus" << true;
    QTest::addRow("URL - Invalid extension") << "https://my.cdn.com/mysong.aac." << false;
    QTest::addRow("URL - Invalid prefix") << "ftp://my.cdn.com/mysong.opus" << false;
    QTest::addRow("URL - Invalid missing prefix") << "my.cdn.com/mysong.opus" << false;
    QTest::addRow("URL - Invalid CDN") << "https://myipgrabber.com/mysong.opus" << false;
    QTest::addRow("URL - Subdomain Attack") << "https://my.cdn.com.fakedomain.com/mysong.opus" << false;
}

void MusicListManager::validateSong()
{
    QFETCH(QString, songname);
    QFETCH(bool, expectedResult);

    bool l_result = m_music_manager->validateSong(songname, {"my.cdn.com", "your.cdn.com"});
    QCOMPARE(expectedResult, l_result);
}

void MusicListManager::addCustomSong()
{
    {
        // Dummy register.
        m_music_manager->registerArea(0);

        // No custom songs, so musiclist = root_list.size()
        QCOMPARE(m_music_manager->musiclist(0).size(), 3);
    }
    {
        // Add a song that's valid. The musiclist is now root_list.size() + custom_list.size()
        m_music_manager->addCustomSong("mysong", "mysong.opus", 0, 0);
        QCOMPARE(m_music_manager->musiclist(0).size(), 4);
    }
    {
        // Add a song that's part of the root list. This should fail and not increase the size.
        bool l_result = m_music_manager->addCustomSong("Announce The Truth (AJ)", "Announce The Truth (AJ).opus", 0, 0);
        QCOMPARE(l_result, false);
        QCOMPARE(m_music_manager->musiclist(0).size(), 4);
    }
    {
        // Disable the root list. Musiclist is now custom_list.size()
        m_music_manager->toggleRootEnabled(0);
        QCOMPARE(m_music_manager->musiclist(0).size(), 1);
    }
    {
        // Add an item that is in the root list into the custom list. Size is still custom_list.size()
        bool l_result = m_music_manager->addCustomSong("Announce The Truth (AJ)", "Announce The Truth (AJ).opus", 0, 0);
        QCOMPARE(l_result, true);
        QCOMPARE(m_music_manager->musiclist(0).size(), 2);
    }
    {
        bool l_result = m_music_manager->addCustomSong("Announce The Truth (AJ)2", "https://my.cdn.com/mysong.opus", 0, 0);
        QCOMPARE(l_result, true);
        QCOMPARE(m_music_manager->musiclist(0).size(), 3);
    }
}

void MusicListManager::addCustomCategory()
{
    {
        // Dummy register.
        m_music_manager->registerArea(0);

        // Add category to the custom list. Category marker are added manually.
        bool l_result = m_music_manager->addCustomCategory("Music2", 0);
        QCOMPARE(l_result, true);
        QCOMPARE(m_music_manager->musiclist(0).size(), 4);
        QCOMPARE(m_music_manager->musiclist(0).at(3), "==Music2==");
    }
    {
        // Add a category that already exists on root. This should fail and not increase the size of our list.
        bool l_result = m_music_manager->addCustomCategory("Music", 0);
        QCOMPARE(l_result, false);
        QCOMPARE(m_music_manager->musiclist(0).size(), 4);
    }
    {
        // We disable the root list. We now insert the category again.
        m_music_manager->toggleRootEnabled(0);
        bool l_result = m_music_manager->addCustomCategory("Music", 0);
        QCOMPARE(l_result, true);
        QCOMPARE(m_music_manager->musiclist(0).size(), 2);
        QCOMPARE(m_music_manager->musiclist(0).at(1), "==Music==");
    }
    {
        // Global now enabled. We add a song with three ===.
        m_music_manager->toggleRootEnabled(0);
        bool l_result = m_music_manager->addCustomCategory("===Music===", 0);
        QCOMPARE(l_result, true);
        QCOMPARE(m_music_manager->musiclist(0).size(), 5);
        QCOMPARE(m_music_manager->musiclist(0).at(4), "===Music===");
    }
}

void MusicListManager::sanitiseCustomList()
{
    // Prepare a dummy area with root list disabled.Insert both non-root and root elements.
    m_music_manager->registerArea(0);
    m_music_manager->toggleRootEnabled(0);
    m_music_manager->addCustomCategory("Music", 0);
    m_music_manager->addCustomCategory("Music2", 0);
    m_music_manager->addCustomSong("Announce The Truth (AJ)", "Announce The Truth (AJ).opus", 0, 0);
    m_music_manager->addCustomSong("mysong", "mysong.opus", 0, 0);

    // We now only have custom elements.
    QCOMPARE(m_music_manager->musiclist(0).size(), 4);

    // We reenable the root list. Sanisation should only leave the non-root elements in the custom list.
    m_music_manager->toggleRootEnabled(0);
    QCOMPARE(m_music_manager->musiclist(0).size(), 5);
    QCOMPARE(m_music_manager->musiclist(0).at(3), "==Music2==");
    QCOMPARE(m_music_manager->musiclist(0).at(4), "mysong.opus");
}

void MusicListManager::removeCategorySong()
{
    {
        // Prepare dummy area. Add both custom songs and categories.
        m_music_manager->registerArea(0);
        m_music_manager->addCustomCategory("Music2", 0);
        m_music_manager->addCustomSong("mysong", "mysong.opus", 0, 0);
        m_music_manager->addCustomCategory("Music3", 0);
        m_music_manager->addCustomSong("mysong2", "mysong.opus", 0, 0);
        QCOMPARE(m_music_manager->musiclist(0).size(), 7);
    }
    {
        // Delete a category that is not custom. This should fail.
        bool l_success = m_music_manager->removeCategorySong("==Music==", 0);
        QCOMPARE(l_success, false);
        QCOMPARE(m_music_manager->musiclist(0).size(), 7);
    }
    {
        // Correct category name, wrong format.
        bool l_success = m_music_manager->removeCategorySong("Music2", 0);
        QCOMPARE(l_success, false);
        QCOMPARE(m_music_manager->musiclist(0).size(), 7);
    }
    {
        // Correct category name. This should be removed.
        bool l_success = m_music_manager->removeCategorySong("==Music2==", 0);
        QCOMPARE(l_success, true);
        QCOMPARE(m_music_manager->musiclist(0).size(), 6);
    }
    {
        // Correct song name. This should be removed. This needs to be with the extension.
        bool l_success = m_music_manager->removeCategorySong("mysong2.opus", 0);
        QCOMPARE(l_success, true);
        QCOMPARE(m_music_manager->musiclist(0).size(), 5);
    }
}

void MusicListManager::songInformation()
{
    {
        // Prepare dummy area. Add both custom songs and categories.
        m_music_manager->registerArea(0);
        m_music_manager->addCustomCategory("Music2", 0);
        m_music_manager->addCustomSong("mysong", "realmysong.opus", 47, 0);
        m_music_manager->addCustomCategory("Music3", 0);
        m_music_manager->addCustomSong("mysong2", "mysong.opus", 42, 0);
    }
    {
        QPair<QString, int> l_song_information = m_music_manager->songInformation("mysong.opus", 0);
        QCOMPARE(l_song_information.first, "realmysong.opus");
        QCOMPARE(l_song_information.second, 47);
    }
    {
        QPair<QString, int> l_song_information = m_music_manager->songInformation("Announce The Truth (AJ).opus", 0);
        QCOMPARE(l_song_information.first, "Announce The Truth (AJ).opus");
        QCOMPARE(l_song_information.second, 59);
    }
}

void MusicListManager::musiclist()
{
    {
        // Prepare dummy area. Add both custom songs and categories.
        m_music_manager->registerArea(0);
        m_music_manager->addCustomCategory("Music2", 0);
        m_music_manager->addCustomSong("mysong", "realmysong.opus", 47, 0);
        m_music_manager->addCustomCategory("Music3", 0);
        m_music_manager->addCustomSong("mysong2", "mysong.opus", 42, 0);
    }
    {
        QCOMPARE(m_music_manager->musiclist(0).size(), 7);
    }
}

}
}

QTEST_APPLESS_MAIN(tests::unittests::MusicListManager)

#include "tst_unittest_music_manager.moc"