#include "pop3synchronizer.h"
#include <QDebug>
#include <QDateTime>
#include <QRegularExpression>
#include "../../core/mailitem.h"
#include "../../db/dao/mailitemdao.h"
#include "../../db/dao/folderdao.h"
#include "../../core/models/account.h"

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

bool Pop3Synchronizer::initialize(const Account &account)
{
    m_account = account;
    const auto &settings = account.connectionSettings();
    m_host = settings.incomingHost;
    m_port = settings.incomingPort;
    m_useSsl = settings.incomingSsl;
    m_username = settings.username;
    m_password = settings.password;
    // For simplicity, we do not actually connect here; connection will be made per operation.
    qDebug() << "POP3 synchronizer initialized for" << m_username << "at" << m_host;
    return true;
}

bool Pop3Synchronizer::syncFolder(const Folder &folder)
{
    Q_UNUSED(folder);
    // POP3 does not have folder sync; we just return true.
    return true;
}

QVector<Folder> Pop3Synchronizer::getFolders() const
{
    QVector<Folder> folders;
    Folder inbox;
    inbox.setId(0); // Assuming folder ID 0 for Inbox (should be looked up)
    inbox.setAccountId(m_account.id());
    inbox.setName(QStringLiteral("Inbox"));
    inbox.setParentFolderId(QString());
    folders.append(inbox);
    return folders;
}

QVector<MailItem> Pop3Synchronizer::fetchMailItems(const QString &folderId, qint64 sinceUid)
{
    Q_UNUSED(sinceUid);
    QVector<MailItem> items;
    // Connect to POP3 server
    QSslSocket socket;
    if (m_useSsl)
        socket.connectToHostEncrypted(m_host, m_port);
    else
        socket.connectToHost(m_host, m_port);
    if (!socket.waitForConnected(5000)) {
        qWarning() << "Failed to connect to POP3 server:" << socket.errorString();
        return items;
    }
    if (m_useSsl && !socket.waitForEncrypted(5000)) {
        qWarning() << "TLS handshake failed:" << socket.errorString();
        socket.disconnectFromHost();
        return items;
    }
    // Read greeting
    QString greeting;
    if (!socket.waitForReadyRead(5000)) {
        qWarning() << "No greeting from POP3 server";
        socket.disconnectFromHost();
        return items;
    }
    greeting = socket.readLine();
    if (!greeting.startsWith(QLatin1Char('+'))) {
        qWarning() << "Invalid POP3 greeting:" << greeting;
        socket.disconnectFromHost();
        return items;
    }
    // Login
    QString userCmd = QStringLiteral("USER %1\r\n").arg(m_username);
    QString passCmd = QStringLiteral("PASS %1\r\n").arg(m_password);
    QString resp;
    if (!sendCommand(userCmd, resp) || !resp.startsWith(QLatin1Char('+'))) {
        qWarning() << "USER command failed:" << resp;
        socket.disconnectFromHost();
        return items;
    }
    if (!sendCommand(passCmd, resp) || !resp.startsWith(QLatin1Char('+'))) {
        qWarning() << "PASS command failed:" << resp;
        socket.disconnectFromHost();
        return items;
    }
    // Get list of messages (STAT and LIST)
    QString statResp;
    if (!sendCommand(QStringLiteral("STAT\r\n"), statResp) || !statResp.startsWith(QLatin1Char('+'))) {
        qWarning() << "STAT failed:" << statResp;
        socket.disconnectFromHost();
        return items;
    }
    // Parse STAT: "+<count> <size>"
    QStringList statParts = statResp.split(QRegularExpression("\\s+"));
    int count = 0;
    if (statParts.size() >= 2)
        count = statParts[1].toInt();
    // For simplicity, we fetch all messages (since POP3 doesn't support UID since easily without UIDL mapping)
    // We'll use UIDL to get unique IDs, then RETR for each.
    QString uidlResp;
    if (!sendCommand(QStringLiteral("UIDL\r\n"), uidlResp) || !uidlResp.startsWith(QLatin1Char('+'))) {
        qWarning() << "UIDL failed:" << uidlResp;
        socket.disconnectFromHost();
        return items;
    }
    // UIDL response lines: each line: "+<msg-id> <uid>"
    QStringList lines = uidlResp.split(QRegularExpression("\\r?\\n"));
    for (const QString &line : lines) {
        if (line.startsWith(QLatin1Char('.')) || line.isEmpty())
            continue;
        // Remove leading '+'
        QString l = line;
        if (l.startsWith(QLatin1Char('+')))
            l.remove(0,1);
        QStringList parts = l.split(QRegularExpression("\\s+"));
        if (parts.size() < 2)
            continue;
        QString msgIdStr = parts[0];
        QString uid = parts[1];
        bool ok;
        int msgId = msgIdStr.toInt(&ok);
        if (!ok)
            continue;
        // Retrieve message
        QString retrCmd = QStringLiteral("RETR %1\r\n").arg(msgIdStr);
        QString retrResp;
        if (!sendCommand(retrCmd, retrResp) || !retrResp.startsWith(QLatin1Char('+'))) {
            qWarning() << "RETR failed for msg" << msgIdStr;
            continue;
        }
        // Read message lines until a line with a single dot.
        QByteArray msgData;
        while (true) {
            if (!socket.waitForReadyRead(5000))
                break;
            QByteArray line = socket.readLine();
            if (line == QByteArray(".\r\n"))
                break;
            msgData.append(line);
        }
        // Parse rudimentary headers: we can extract Subject, From, Date.
        // For simplicity, we'll set placeholder values.
        QString subject = QStringLiteral("(No Subject)");
        QString from = QStringLiteral("unknown@example.com");
        QDateTime date = QDateTime::currentDateTimeUtc();
        bool seen = false; // POP3 doesn't have read flag; we assume unread unless we store locally.
        bool flagged = false;
        MailItem item;
        item.setFolderId(folderId.toInt());
        item.setSubject(subject);
        item.setSender(from);
        item.setDate(date);
        item.setRead(seen);
        item.setFlagged(flagged);
        // We could store the UID in a custom field, but MailItem has uid field (qint64) - we can store the numeric UID if possible.
        // For now, we leave uid 0.
        // Insert into DB
        if (MailItemDao::insert(item)) {
            QSqlQuery qry;
            qry.exec(QStringLiteral("SELECT last_insert_rowid()"));
            qint64 newId = -1;
            if (qry.next())
                newId = qry.value(0).toLongLong();
            item.setId(newId);
            items.append(item);
            qDebug() << "Fetched and stored msgId" << msgIdStr << "as mail id" << newId;
        } else {
            qWarning() << "Failed to insert mail item for msgId" << msgIdStr;
        }
    }
    // Quit
    sendCommand(QStringLiteral("QUIT\r\n"), resp);
    socket.disconnectFromHost();
    return items;
}

bool Pop3Synchronizer::appendMailItem(const QString &folderId, const MailItem &item)
{
    Q_UNUSED(folderId);
    Q_UNUSED(item);
    // POP3 does not support uploading; we can implement via SMTP elsewhere.
    return false;
}

bool Pop3Synchronizer::updateMailItemFlags(const QString &folderId, const QString &itemUid, bool read, bool flagged)
{
    Q_UNUSED(folderId);
    Q_UNUSED(itemUid);
    Q_UNUSED(read);
    Q_UNUSED(flagged);
    // POP3 does not support flag updates.
    return false;
}

bool Pop3Synchronizer::deleteMailItem(const QString &folderId, const QString &itemUid)
{
    Q_UNUSED(folderId);
    Q_UNUSED(itemUid);
    // POP3 does not support deleting by UID easily without mapping; we could implement DELE by message number.
    // For simplicity, we return false.
    return false;
}

QString Pop3Synchronizer::readResponse()
{
    if (!m_socket)
        return QString();
    if (!m_socket->waitForReadyRead(5000))
        return QString();
    return QString::fromUtf8(m_socket->readLine()).trimmed();
}

bool Pop3Synchronizer::sendCommand(const QString &cmd, QString &response)
{
    if (!m_socket)
        return false;
    m_socket->write(cmd.toUtf8());
    if (!m_socket->waitForBytesWritten(5000))
        return false;
    response = readResponse();
    return !response.isEmpty();
}
