#include <QSqlQuery>
#include <QSqlError>
#include "mailitemdao.h"
#include <optional>

bool MailItemDao::insert(const MailItem& item)
{
    QSqlDatabase& db = DatabaseManager::instance().database();
    QSqlQuery query(db);
    query.prepare(
        "INSERT INTO MailCopy (folderId, messageId, subject, sender, recipient, date, read, flagged, hasAttachment, size, fileId, uid) "
        "VALUES (:folderId, :messageId, :subject, :sender, :recipient, :date, :read, :flagged, :hasAttachment, :size, :fileId, :uid)"
    );
    query.bindValue(":folderId", item.folderId());
    query.bindValue(":messageId", item.messageId());
    query.bindValue(":subject", item.subject());
    query.bindValue(":sender", item.sender());
    query.bindValue(":recipient", item.recipient());
    query.bindValue(":date", item.date());
    query.bindValue(":read", item.isRead() ? 1 : 0);
    query.bindValue(":flagged", item.isFlagged() ? 1 : 0);
    query.bindValue(":hasAttachment", !item.attachments().isEmpty() ? 1 : 0);
    query.bindValue(":size", item.size());
    query.bindValue(":fileId", item.fileId());
    query.bindValue(":uid", item.uid());

    if (!query.exec()) {
        qWarning() << "Failed to insert mail item:" << query.lastError().text();
        return false;
    }
    return true;
}

bool MailItemDao::update(const MailItem& item)
{
    QSqlDatabase& db = DatabaseManager::instance().database();
    QSqlQuery query(db);
    query.prepare(
        "UPDATE MailCopy SET "
        "folderId = :folderId, "
        "messageId = :messageId, "
        "subject = :subject, "
        "sender = :sender, "
        "recipient = :recipient, "
        "date = :date, "
        "read = :read, "
        "flagged = :flagged, "
        "hasAttachment = :hasAttachment, "
        "size = :size, "
        "fileId = :fileId, "
        "uid = :uid "
        "WHERE id = :id"
    );
    query.bindValue(":id", item.id());
    query.bindValue(":folderId", item.folderId());
    query.bindValue(":messageId", item.messageId());
    query.bindValue(":subject", item.subject());
    query.bindValue(":sender", item.sender());
    query.bindValue(":recipient", item.recipient());
    query.bindValue(":date", item.date());
    query.bindValue(":read", item.isRead() ? 1 : 0);
    query.bindValue(":flagged", item.isFlagged() ? 1 : 0);
    query.bindValue(":hasAttachment", !item.attachments().isEmpty() ? 1 : 0);
    query.bindValue(":size", item.size());
    query.bindValue(":fileId", item.fileId());
    query.bindValue(":uid", item.uid());

    if (!query.exec()) {
        qWarning() << "Failed to update mail item:" << query.lastError().text();
        return false;
    }
    return true;
}

bool MailItemDao::remove(qint64 id)
{
    QSqlDatabase& db = DatabaseManager::instance().database();
    QSqlQuery query(db);
    query.prepare("DELETE FROM MailCopy WHERE id = :id");
    query.bindValue(":id", id);

    if (!query.exec()) {
        qWarning() << "Failed to delete mail item:" << query.lastError().text();
        return false;
    }
    return true;
}

std::optional<MailItem> MailItemDao::findById(qint64 id)
{
    QSqlDatabase& db = DatabaseManager::instance().database();
    QSqlQuery query(db);
    query.prepare("SELECT id, folderId, messageId, subject, sender, recipient, date, read, flagged, hasAttachment, size, fileId, uid FROM MailCopy WHERE id = :id");
    query.bindValue(":id", id);

    if (!query.exec()) {
        qWarning() << "Failed to find mail item by id:" << query.lastError().text();
        return std::nullopt;
    }

    if (query.next()) {
        MailItem item;
        item.setId(query.value(0).toInt());
        item.setFolderId(query.value(1).toInt());
        item.setMessageId(query.value(2).toString());
        item.setSubject(query.value(3).toString());
        item.setSender(query.value(4).toString());
        item.setRecipient(query.value(5).toString());
        item.setDate(query.value(6).toDateTime());
        item.setRead(query.value(7).toBool());
        item.setFlagged(query.value(8).toBool());
        item.setSize(query.value(10).toLongLong());
        item.setFileId(query.value(11).toString());
        item.setUid(query.value(12).toLongLong());
        return item;
    }
    return std::nullopt;
}

QVector<MailItem> MailItemDao::findAll()
{
    QVector<MailItem> items;
    QSqlDatabase& db = DatabaseManager::instance().database();
    QSqlQuery query(db);
    if (!query.exec("SELECT id, folderId, messageId, subject, sender, recipient, date, read, flagged, hasAttachment, size, fileId, uid FROM MailCopy")) {
        qWarning() << "Failed to fetch all mail items:" << query.lastError().text();
        return items;
    }

    while (query.next()) {
        MailItem item;
        item.setId(query.value(0).toInt());
        item.setFolderId(query.value(1).toInt());
        item.setMessageId(query.value(2).toString());
        item.setSubject(query.value(3).toString());
        item.setSender(query.value(4).toString());
        item.setRecipient(query.value(5).toString());
        item.setDate(query.value(6).toDateTime());
        item.setRead(query.value(7).toBool());
        item.setFlagged(query.value(8).toBool());
        item.setSize(query.value(10).toLongLong());
        item.setFileId(query.value(11).toString());
        item.setUid(query.value(12).toLongLong());
        items.append(item);
    }
    return items;
}

QVector<MailItem> MailItemDao::findByFolderId(int folderId)
{
    QVector<MailItem> items;
    QSqlDatabase& db = DatabaseManager::instance().database();
    QSqlQuery query(db);
    query.prepare("SELECT id, folderId, messageId, subject, sender, recipient, date, read, flagged, hasAttachment, size, fileId, uid FROM MailCopy WHERE folderId = :folderId");
    query.bindValue(":folderId", folderId);

    if (!query.exec()) {
        qWarning() << "Failed to fetch mail items by folder id:" << query.lastError().text();
        return items;
    }

    while (query.next()) {
        MailItem item;
        item.setId(query.value(0).toInt());
        item.setFolderId(query.value(1).toInt());
        item.setMessageId(query.value(2).toString());
        item.setSubject(query.value(3).toString());
        item.setSender(query.value(4).toString());
        item.setRecipient(query.value(5).toString());
        item.setDate(query.value(6).toDateTime());
        item.setRead(query.value(7).toBool());
        item.setFlagged(query.value(8).toBool());
        item.setSize(query.value(10).toLongLong());
        item.setFileId(query.value(11).toString());
        item.setUid(query.value(12).toLongLong());
        items.append(item);
    }
    return items;
}

QVector<MailItem> MailItemDao::findByFolderIdSinceUid(int folderId, qint64 sinceUid)
{
    QVector<MailItem> items;
    QSqlDatabase& db = DatabaseManager::instance().database();
    QSqlQuery query(db);
    query.prepare("SELECT id, folderId, messageId, subject, sender, recipient, date, read, flagged, hasAttachment, size, fileId, uid FROM MailCopy WHERE folderId = :folderId AND uid > :sinceUid");
    query.bindValue(":folderId", folderId);
    query.bindValue(":sinceUid", sinceUid);

    if (!query.exec()) {
        qWarning() << "Failed to fetch mail items by folder id since uid:" << query.lastError().text();
        return items;
    }

    while (query.next()) {
        MailItem item;
        item.setId(query.value(0).toInt());
        item.setFolderId(query.value(1).toInt());
        item.setMessageId(query.value(2).toString());
        item.setSubject(query.value(3).toString());
        item.setSender(query.value(4).toString());
        item.setRecipient(query.value(5).toString());
        item.setDate(query.value(6).toDateTime());
        item.setRead(query.value(7).toBool());
        item.setFlagged(query.value(8).toBool());
        item.setSize(query.value(10).toLongLong());
        item.setFileId(query.value(11).toString());
        item.setUid(query.value(12).toLongLong());
        items.append(item);
    }
    return items;
}
std::optional<qint64> MailItemDao::maxUidForFolder(int folderId)
{
    QSqlDatabase& db = DatabaseManager::instance().database();
    QSqlQuery query(db);
    query.prepare("SELECT MAX(uid) FROM MailCopy WHERE folderId = :folderId");
    query.bindValue(":folderId", folderId);

    if (!query.exec()) {
        qWarning() << "Failed to get max uid for folder:" << query.lastError().text();
        return std::nullopt;
    }

    if (query.next()) {
        if (query.isNull(0)) {
            return std::nullopt;
        }
        return query.value(0).toLongLong();
    }
    return std::nullopt;
}

QVector<qint64> MailItemDao::uidsForFolder(int folderId)
{
    QVector<qint64> uids;
    QSqlDatabase& db = DatabaseManager::instance().database();
    QSqlQuery query(db);
    query.prepare("SELECT uid FROM MailCopy WHERE folderId = :folderId");
    query.bindValue(":folderId", folderId);

    if (!query.exec()) {
        qWarning() << "Failed to get uids for folder:" << query.lastError().text();
        return uids;
    }

    while (query.next()) {
        qint64 uid = query.value(0).toLongLong();
        if (uid > 0) {
            uids.append(uid);
        }
    }
    return uids;
}
