/**
 * SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org>
 *
 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
 */

#include <QVariant>
#include <QVector>

#include "database.h"
#include "debug.h"
#include "entriesmodel.h"
#include "fetcher.h"

EntriesModel::EntriesModel(QObject *parent)
    : QAbstractListModel(parent)
{
    connect(&Fetcher::instance(), &Fetcher::feedUpdated, this, [this](const QString &url) {
        if (m_feedUrl.isEmpty() || m_feedUrl == url) {
            loadEntries();
        }
    });
    connect(&Database::instance(), &Database::entryReadChanged, this, [this](const QString &entryId, bool read) {
        for (auto i = 0; i < m_entries.size(); i++) {
            if (m_entries[i].id == entryId) {
                m_entries[i].read = read;
                dataChanged(index(i, 0), index(i, 0), {ReadRole});
                break;
            }
        }
    });
    connect(&Database::instance(), &Database::feedReadChanged, this, [this]() {
        loadEntries();
    });
    connect(&Database::instance(), &Database::entryFavoriteChanged, this, [this](const QString &entryId, bool favorite) {
        for (auto i = 0; i < m_entries.size(); i++) {
            if (m_entries[i].id == entryId) {
                m_entries[i].favorite = favorite;
                dataChanged(index(i, 0), index(i, 0), {FavoritesRole});
                break;
            }
        }
    });
    loadEntries();
}

QVariant EntriesModel::data(const QModelIndex &index, int role) const
{
    const auto &entry = m_entries[index.row()];
    if (role == IdRole) {
        return entry.id;
    }
    if (role == ReadRole) {
        return entry.read;
    }
    if (role == TitleRole) {
        return entry.title;
    }
    if (role == ContentRole) {
        return entry.content;
    }
    if (role == FeedTitleRole) {
        return entry.feedTitle;
    }
    if (role == AuthorsRole) {
        return entry.authors;
    }
    if (role == CreatedRole) {
        return entry.created;
    }
    if (role == UpdatedRole) {
        return entry.updated;
    }
    if (role == LinkRole) {
        return entry.link;
    }
    if (role == BaseUrlRole) {
        return QUrl(entry.link).adjusted(QUrl::RemovePath).toString();
    }
    if (role == FavoritesRole) {
        return entry.favorite;
    }
    return QVariant();
}

QHash<int, QByteArray> EntriesModel::roleNames() const
{
    return {
        {IdRole, "id"},
        {ReadRole, "read"},
        {TitleRole, "title"},
        {ContentRole, "content"},
        {FeedTitleRole, "feedTitle"},
        {AuthorsRole, "authors"},
        {CreatedRole, "created"},
        {UpdatedRole, "updated"},
        {LinkRole, "link"},
        {BaseUrlRole, "baseUrl"},
        {ReadRole, "read"},
        {FavoritesRole, "favorite"},
    };
}

QStringList EntriesModel::getFeeds() const
{
    QStringList feedList;
    if (m_feedUrl.length() > 0) {
        feedList += m_feedUrl;
        return feedList;
    }

    QSqlQuery feedQuery;
    if (m_groupName.length() > 0) {
        feedQuery.prepare(QStringLiteral("SELECT url FROM Feeds WHERE groupName=:groupName;"));
        feedQuery.bindValue(QStringLiteral(":groupName"), m_groupName);
    } else {
        feedQuery.prepare(QStringLiteral("SELECT url FROM Feeds;"));
    }
    Database::instance().execute(feedQuery);
    while (feedQuery.next()) {
        feedList += feedQuery.value(QStringLiteral("url")).toString();
    }

    return feedList;
}

int EntriesModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent)
    QSqlQuery query;
    QStringList feedList = getFeeds();
    QStringList questionmarks(feedList.size(), QStringLiteral("?"));
    query.prepare(QStringLiteral("SELECT COUNT() FROM Entries WHERE feed IN (%1);").arg(questionmarks.join(QStringLiteral(","))));
    for (QString feed : feedList) {
        query.addBindValue(feed);
    }

    Database::instance().execute(query);
    if (!query.next()) {
        qCWarning(ALLIGATOR) << "Failed to query feed count";
    }
    return query.value(0).toInt();
}

void EntriesModel::loadEntries()
{
    QSqlQuery entryQuery;
    beginResetModel();
    m_entries.clear();

    QStringList feedList = getFeeds();
    QStringList questionmarks(feedList.size(), QStringLiteral("?"));

    entryQuery.prepare(QStringLiteral("SELECT * FROM Entries WHERE feed IN (%1) ORDER BY updated DESC;").arg(questionmarks.join(QStringLiteral(","))));
    for (QString feed : feedList) {
        entryQuery.addBindValue(feed);
    }

    Database::instance().execute(entryQuery);
    while (entryQuery.next()) {
        Entry entry;
        QSqlQuery authorQuery;
        QStringList authorList;
        authorQuery.prepare(QStringLiteral("SELECT * FROM Authors WHERE id=:id;"));
        authorQuery.bindValue(QStringLiteral(":id"), entryQuery.value(QStringLiteral("id")).toString());
        Database::instance().execute(authorQuery);

        while (authorQuery.next()) {
            authorList += authorQuery.value(QStringLiteral("name")).toString();
        }

        if (authorList.size() > 0) {
            entry.authors = authorList[0];
        }

        QSqlQuery feedQuery;
        feedQuery.prepare(QStringLiteral("SELECT * FROM Feeds WHERE url=:feed"));
        feedQuery.bindValue(QStringLiteral(":feed"), entryQuery.value(QStringLiteral("feed")).toString());
        Database::instance().execute(feedQuery);
        feedQuery.next();
        QString name = feedQuery.value(QStringLiteral("name")).toString();
        QString displayName = feedQuery.value(QStringLiteral("displayName")).toString();
        entry.feedTitle = !displayName.isEmpty() ? displayName : name;

        entry.created.setSecsSinceEpoch(entryQuery.value(QStringLiteral("created")).toInt());
        entry.updated.setSecsSinceEpoch(entryQuery.value(QStringLiteral("updated")).toInt());
        entry.id = entryQuery.value(QStringLiteral("id")).toString();
        entry.title = entryQuery.value(QStringLiteral("title")).toString();
        entry.content = entryQuery.value(QStringLiteral("content")).toString();
        entry.link = entryQuery.value(QStringLiteral("link")).toString();
        entry.read = entryQuery.value(QStringLiteral("read")).toBool();
        entry.favorite = entryQuery.value((QStringLiteral("favorite"))).toBool();

        m_entries.append(entry);
    }
    endResetModel();
}

QString EntriesModel::feedUrl() const
{
    return m_feedUrl;
}

void EntriesModel::setFeedUrl(const QString &feedUrl)
{
    if (feedUrl == m_feedUrl) {
        return;
    }
    m_feedUrl = feedUrl;
    Q_EMIT feedUrlChanged();
    loadEntries();
}

QString EntriesModel::groupName() const
{
    return m_groupName;
}

void EntriesModel::setGroupName(const QString &groupName)
{
    if (groupName == m_groupName) {
        return;
    }
    m_groupName = groupName;
    Q_EMIT groupNameChanged();
    loadEntries();
}
