// src/services/imap/imapsynchronizer_mailio.cpp
// mailio-based IMAP synchronizer - replaces custom IMAP implementation

#if defined(USE_MAILIO_IMAP) && USE_MAILIO_IMAP

#include "imapsynchronizer_mailio.h"
#include <mailio/imap.hpp>
#include <mailio/message.hpp>
#include <mailio/mime.hpp>
#include <QDebug>
#include <QDateTime>
#include <QRegularExpression>
#include "../../core/mailitem.h"
#include "../../core/models/account.h"
#include "../../core/models/folder.h"
#include "../../db/dao/folderdao.h"
#include "../../db/dao/mailitemdao.h"
#include "../../services/mimestorage.h"

using namespace mailio;

ImapSynchronizerMailio::ImapSynchronizerMailio(QObject* parent)
    : Synchronizer(parent)
{}

bool ImapSynchronizerMailio::initialize(const Account& account)
{
    m_account = account;
    const Account::ConnectionSettings& settings = account.connectionSettings();
    m_host = settings.incomingHost.toStdString();
    m_port = settings.incomingPort;
    m_useSsl = settings.incomingSsl;
    m_username = settings.username.toStdString();
    m_password = settings.password.toStdString();
    m_authMethod = settings.authMethod.toStdString();
    return true;
}

bool ImapSynchronizerMailio::syncFolder(const Folder& folder)
{
    try {
        // Connect to IMAP server
        imap conn(m_host, m_port);
        if (m_useSsl) {
            conn.connect_ssl();
        } else {
            conn.connect();
        }

        // Authenticate
        if (m_authMethod == "oauth2" || m_authMethod == "xoauth2") {
            conn.authenticate("XOAUTH2", m_username, m_password); // m_password holds access token
        } else {
            conn.authenticate(m_username, m_password, imap::auth_method_t::LOGIN);
        }

        // Select folder
        conn.select(folder.name().toStdString());

        // Get all UIDs on server
        std::vector<unsigned long> serverUids = conn.fetch_uids();
        if (serverUids.empty()) {
            conn.close();
            return true;
        }

        // Get local UIDs
        QVector<qint64> localUids = MailItemDao::getUidsForFolder(folder.id());
        QSet<qint64> serverUidSet;
        for (auto uid : serverUids) serverUidSet.insert(static_cast<qint64>(uid));

        // Detect deleted messages
        bool success = true;
        for (qint64 localUid : localUids) {
            if (!serverUidSet.contains(localUid)) {
                if (!MailItemDao::removeByUid(folder.id(), localUid)) {
                    qWarning() << "Failed to delete local mail item UID" << localUid;
                    success = false;
                } else {
                    qDebug() << "Removed local mail item UID" << localUid << "(deleted on server)";
                }
            }
        }

        // Fetch new/changed messages (UID > max local UID)
        qint64 sinceUid = MailItemDao::maxUidForFolder(folder.id()).value_or(0);
        std::vector<unsigned long> newUids;
        for (auto uid : serverUids) {
            if (static_cast<qint64>(uid) > sinceUid) {
                newUids.push_back(uid);
            }
        }

        if (!newUids.empty()) {
            // Fetch in batches
            const size_t batchSize = 50;
            for (size_t i = 0; i < newUids.size(); i += batchSize) {
                size_t end = std::min(i + batchSize, newUids.size());
                std::vector<unsigned long> batch(newUids.begin() + i, newUids.begin() + end);

                // Fetch full messages
                std::vector<message> messages = conn.fetch_messages(batch, message::fetch_mode_t::FULL);

                for (const auto& msg : messages) {
                    if (!persistFetchedMessage(folder, msg)) {
                        success = false;
                    }
                }
            }
        }

        // Update flags for existing messages (in batches)
        if (!serverUids.empty()) {
            const size_t batchSize = 100;
            for (size_t i = 0; i < serverUids.size(); i += batchSize) {
                size_t end = std::min(i + batchSize, serverUids.size());
                std::vector<unsigned long> batch(serverUids.begin() + i, serverUids.begin() + end);

                // Fetch only FLAGS
                std::vector<message> flagMsgs = conn.fetch_messages(batch, message::fetch_mode_t::FLAGS);
                
                for (const auto& msg : flagMsgs) {
                    updateLocalFlags(folder.id(), msg);
                }
            }
        }

        conn.close();
        return success;

    } catch (const mailio::dialog_error& e) {
        qWarning() << "IMAP dialog error:" << QString::fromStdString(e.what());
        return false;
    } catch (const std::exception& e) {
        qWarning() << "IMAP error:" << QString::fromStdString(e.what());
        return false;
    }
}

QVector<Folder> ImapSynchronizerMailio::getFolders() const
{
    QVector<Folder> folders;
    try {
        imap conn(m_host, m_port);
        if (m_useSsl) conn.connect_ssl(); else conn.connect();
        
        if (m_authMethod == "oauth2" || m_authMethod == "xoauth2") {
            conn.authenticate("XOAUTH2", m_username, m_password);
        } else {
            conn.authenticate(m_username, m_password, imap::auth_method_t::LOGIN);
        }

        std::vector<std::string> mailboxNames = conn.list_mailboxes();
        for (const auto& name : mailboxNames) {
            Folder folder;
            folder.setName(QString::fromStdString(name));
            QString lower = name.toLower();
            if (lower == "inbox") folder.setInbox(true);
            else if (lower == "sent") folder.setSent(true);
            else if (lower == "drafts") folder.setDrafts(true);
            else if (lower == "trash" || lower == "deleted items") folder.setTrash(true);
            folders.append(folder);
        }
        conn.close();
    } catch (const std::exception& e) {
        qWarning() << "getFolders error:" << QString::fromStdString(e.what());
    }
    return folders;
}

QVector<MailItem> ImapSynchronizerMailio::fetchMailItems(const QString& folderId, qint64 sinceUid)
{
    QVector<MailItem> items;
    try {
        imap conn(m_host, m_port);
        if (m_useSsl) conn.connect_ssl(); else conn.connect();
        
        if (m_authMethod == "oauth2" || m_authMethod == "xoauth2") {
            conn.authenticate("XOAUTH2", m_username, m_password);
        } else {
            conn.authenticate(m_username, m_password, imap::auth_method_t::LOGIN);
        }

        conn.select(folderId.toStdString());
        std::vector<unsigned long> uids = conn.fetch_uids();
        
        std::vector<unsigned long> newUids;
        for (auto uid : uids) {
            if (static_cast<qint64>(uid) > sinceUid) newUids.push_back(uid);
        }

        if (!newUids.empty()) {
            std::vector<message> messages = conn.fetch_messages(newUids, message::fetch_mode_t::FULL);
            for (const auto& msg : messages) {
                MailItem item = parseMailioMessage(msg);
                item.setFolderId(folderId.toInt());
                items.append(item);
            }
        }
        conn.close();
    } catch (const std::exception& e) {
        qWarning() << "fetchMailItems error:" << QString::fromStdString(e.what());
    }
    return items;
}

bool ImapSynchronizerMailio::appendMailItem(const QString& folderId, const MailItem& item)
{
    try {
        imap conn(m_host, m_port);
        if (m_useSsl) conn.connect_ssl(); else conn.connect();
        
        if (m_authMethod == "oauth2" || m_authMethod == "xoauth2") {
            conn.authenticate("XOAUTH2", m_username, m_password);
        } else {
            conn.authenticate(m_username, m_password, imap::auth_method_t::LOGIN);
        }

        conn.select(folderId.toStdString());
        
        // Convert MailItem to mailio::message
        message msg = createMailioMessage(item);
        conn.append_message(folderId.toStdString(), msg);
        
        conn.close();
        return true;
    } catch (const std::exception& e) {
        qWarning() << "appendMailItem error:" << QString::fromStdString(e.what());
        return false;
    }
}

bool ImapSynchronizerMailio::updateMailItemFlags(const QString& folderId, const QString& itemUid, bool read, bool flagged)
{
    try {
        imap conn(m_host, m_port);
        if (m_useSsl) conn.connect_ssl(); else conn.connect();
        
        if (m_authMethod == "oauth2" || m_authMethod == "xoauth2") {
            conn.authenticate("XOAUTH2", m_username, m_password);
        } else {
            conn.authenticate(m_username, m_password, imap::auth_method_t::LOGIN);
        }

        conn.select(folderId.toStdString());
        
        unsigned long uid = itemUid.toULongLong();
        if (read) conn.add_flag(uid, flag_t::SEEN); else conn.remove_flag(uid, flag_t::SEEN);
        if (flagged) conn.add_flag(uid, flag_t::FLAGGED); else conn.remove_flag(uid, flag_t::FLAGGED);
        
        conn.close();
        
        // Update local DB
        auto itemOpt = MailItemDao::findByUid(folderId.toInt(), uid);
        if (itemOpt.has_value()) {
            MailItem item = itemOpt.value();
            item.setRead(read);
            item.setFlagged(flagged);
            return MailItemDao::update(item);
        }
        return true;
    } catch (const std::exception& e) {
        qWarning() << "updateMailItemFlags error:" << QString::fromStdString(e.what());
        return false;
    }
}

bool ImapSynchronizerMailio::deleteMailItem(const QString& folderId, const QString& itemUid)
{
    try {
        imap conn(m_host, m_port);
        if (m_useSsl) conn.connect_ssl(); else conn.connect();
        
        if (m_authMethod == "oauth2" || m_authMethod == "xoauth2") {
            conn.authenticate("XOAUTH2", m_username, m_password);
        } else {
            conn.authenticate(m_username, m_password, imap::auth_method_t::LOGIN);
        }

        conn.select(folderId.toStdString());
        conn.remove_message(itemUid.toULongLong());
        conn.expunge();
        conn.close();
        
        return MailItemDao::removeByUid(folderId.toInt(), itemUid.toLongLong());
    } catch (const std::exception& e) {
        qWarning() << "deleteMailItem error:" << QString::fromStdString(e.what());
        return false;
    }
}

// Private helpers

bool ImapSynchronizerMailio::persistFetchedMessage(const Folder& folder, const message& msg)
{
    try {
        MailItem item = parseMailioMessage(msg);
        item.setFolderId(folder.id());
        
        MimeStorageService storage;
        QVector<QString> storedPaths;
        ParsedMimeMessage parsed;
        
        // Convert mailio message to raw MIME for storage
        std::string rawMime = msg.to_string();
        QByteArray rawMimeData = QByteArray::fromStdString(rawMime);
        
        if (!storage.parseMessage(rawMimeData, parsed)
            || !storage.storeMessage(QString::number(m_account.id()), QString::number(folder.id()),
                                     item, rawMimeData, &storedPaths)) {
            qWarning() << "Failed to store MIME message";
            return false;
        }
        
        if (!MailItemDao::upsert(item)) {
            storage.deleteEmlFile(item.fileId());
            return false;
        }
        
        // Store attachments
        QVector<StoredAttachmentRecord> records;
        for (size_t i = 0; i < parsed.attachments.size(); ++i) {
            const auto& att = parsed.attachments[i];
            StoredAttachmentRecord record;
            record.fileName = QString::fromStdString(att.fileName);
            record.mimeType = QString::fromStdString(att.mimeType);
            record.contentId = QString::fromStdString(att.contentId);
            record.size = att.data.size();
            if (i < storedPaths.size()) record.storedPath = storedPaths[i];
            records.append(record);
        }
        
        if (!MailItemDao::replaceAttachments(item.id(), records)) {
            storage.deleteEmlFile(item.fileId());
            return false;
        }
        
        return true;
    } catch (const std::exception& e) {
        qWarning() << "persistFetchedMessage error:" << QString::fromStdString(e.what());
        return false;
    }
}

MailItem ImapSynchronizerMailio::parseMailioMessage(const message& msg)
{
    MailItem item;
    
    // UID is in the message metadata (mailio stores it separately)
    // For now, we'll use a placeholder - in real usage, track UID from fetch
    item.setUid(0); // Will be set by caller
    
    // Headers
    item.setMessageId(QString::fromStdString(msg.header().message_id()));
    item.setSubject(QString::fromStdString(msg.header().subject()).isEmpty() ? "(No Subject)" : QString::fromStdString(msg.header().subject()));
    
    // From
    std::vector<mailio::address> fromList = msg.header().from();
    if (!fromList.empty()) {
        item.setSender(QString::fromStdString(fromList[0].to_string()));
    }
    
    // To
    std::vector<mailio::address> toList = msg.header().to();
    QStringList toStrs;
    for (const auto& addr : toList) toStrs << QString::fromStdString(addr.to_string());
    item.setTo(toStrs.join(", "));
    item.setRecipient(item.to()); // compatibility
    
    // CC
    std::vector<mailio::address> ccList = msg.header().cc();
    QStringList ccStrs;
    for (const auto& addr : ccList) ccStrs << QString::fromStdString(addr.to_string());
    item.setCc(ccStrs.join(", "));
    
    // BCC - not typically in headers
    item.setBcc("");
    
    // Date
    std::time_t dateTime = msg.header().date();
    item.setDate(QDateTime::fromSecsSinceEpoch(dateTime));
    
    // Body - prefer HTML, fallback to text
    std::string htmlBody = msg.content_text(text_format_t::HTML);
    if (!htmlBody.empty()) {
        item.setBodyHtml(QString::fromStdString(htmlBody));
    } else {
        std::string plainBody = msg.content_text(text_format_t::PLAIN);
        item.setBodyHtml(QString("<pre>%1</pre>").arg(QString::fromStdString(plainBody).toHtmlEscaped()));
    }
    
    // Size
    item.setSize(msg.to_string().size());
    
    // Attachments
    QStringList attachmentNames;
    for (const auto& att : msg.attachments()) {
        attachmentNames << QString::fromStdString(att.descriptor().filename);
    }
    item.setAttachments(attachmentNames);
    
    // Flags - mailio doesn't expose IMAP flags directly in message
    // These would come from a separate FLAGS fetch
    item.setRead(false);
    item.setFlagged(false);
    
    return item;
}

void ImapSynchronizerMailio::updateLocalFlags(int folderId, const message& msg)
{
    // mailio message from FLAGS fetch doesn't have UID easily accessible
    // This would need a custom fetch that returns UID+FLAGS together
    // For now, skip - implement when needed
    Q_UNUSED(folderId);
    Q_UNUSED(msg);
}

message ImapSynchronizerMailio::createMailioMessage(const MailItem& item)
{
    message msg;
    
    // Headers
    msg.header().subject(item.subject().toStdString());
    msg.header().from({mailio::address(item.sender().toStdString())});
    
    QStringList toList = item.to().split(',', Qt::SkipEmptyParts);
    std::vector<mailio::address> toAddrs;
    for (const QString& addr : toList) toAddrs.push_back(mailio::address(addr.trimmed().toStdString()));
    msg.header().to(toAddrs);
    
    QStringList ccList = item.cc().split(',', Qt::SkipEmptyParts);
    std::vector<mailio::address> ccAddrs;
    for (const QString& addr : ccList) ccAddrs.push_back(mailio::address(addr.trimmed().toStdString()));
    msg.header().cc(ccAddrs);
    
    msg.header().date(QDateTime::currentDateTimeUtc().toSecsSinceEpoch());
    
    // Body
    if (!item.bodyHtml().isEmpty()) {
        msg.content_text(item.bodyHtml().toStdString(), text_format_t::HTML);
    }
    
    // Attachments would need to be added here
    
    return msg;
}

#endif // USE_MAILIO_IMAP