#include "gmailsynchronizer.h"
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QUrl>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QDebug>
#include <QUrlQuery>
#include <QDateTime>
#include <QTimer>
#include <QEventLoop>
#include <QNetworkRequest>
#include "../../core/events.h"
#include "../../core/eventbus.h"
#include "db/dao/folderdao.h"
#include "services/mimestorage.h"

GmailSynchronizer::GmailSynchronizer(QObject* parent)
    : Synchronizer(parent),
      m_networkManager(new QNetworkAccessManager(this)),
      m_historyTimer(new QTimer(this))
{
    // Conectar señales
    connect(m_networkManager, &QNetworkAccessManager::finished,
            this, &GmailSynchronizer::onGmailReplyFinished);
    connect(m_historyTimer, &QTimer::timeout,
            this, &GmailSynchronizer::onHistoryTimer);
    
    // Configurar timer para history sync cada 2 minutos (Gmail permite cambios frecuentes)
    m_historyTimer->setInterval(2 * 60 * 1000); // 2 minutos
}

bool GmailSynchronizer::initialize(const Account& account)
{
    // Guardar información de la cuenta
    m_account = account;
    
    // Para Gmail, usamos tokens de acceso
    m_accessToken = account.accessToken();
    m_refreshToken = account.refreshToken();
    m_tokenExpires = account.tokenExpires();
    
    if (m_accessToken.isEmpty()) {
        qWarning() << "Gmail account has no access token";
        return false;
    }
    
    // Publish AccountConnectedEvent
    WinoMail::Events::AccountConnectedEvent event;
    event.eventId = QString::number(QDateTime::currentMSecsSinceEpoch()) + "_" + QString::number(rand());
    event.timestamp = QDateTime::currentDateTimeUtc();
    event.accountId = account.id();
    event.provider = "gmail";
    PUBLISH(event);
    
    qDebug() << "Gmail Synchronizer initialized for account:" << account.email();
    return true;
}

bool GmailSynchronizer::syncFolder(const Folder& folder)
{
    if (!m_account.isTokenValid()) {
        if (!refreshAccessToken()) {
            qWarning() << "Failed to refresh token for Gmail sync";
            emit errorOccurred("Failed to refresh authentication token");
            return false;
        }
    }
    
    qDebug() << "Starting Gmail sync for folder (label):" << folder.name();
    
    // Publish SyncStartedEvent
    WinoMail::Events::SyncStartedEvent syncStartEvent;
    syncStartEvent.eventId = QString::number(QDateTime::currentMSecsSinceEpoch()) + "_" + QString::number(rand());
    syncStartEvent.timestamp = QDateTime::currentDateTimeUtc();
    syncStartEvent.accountId = m_account.id();
    syncStartEvent.folderId = QString::number(folder.id());
    PUBLISH(syncStartEvent);
    
    emit folderSyncStarted(QString::number(folder.id()));
    
    // Obtener elementos de correo desde Gmail API
    QVector<MailItem> items = fetchMailItems(QString::number(folder.id()));
    
    // En una implementación real, aquí compararíamos con la base de datos local
    // y emitiríamos las señales apropiadas para elementos nuevos/actualizados/eliminados
    
    if (!items.isEmpty()) {
        for (const MailItem& item : items) {
            emit mailItemAdded(item);
            // Publish MailItemAddedEvent
            WinoMail::Events::MailItemAddedEvent mailEvent;
            mailEvent.eventId = QString::number(QDateTime::currentMSecsSinceEpoch()) + "_" + QString::number(rand());
            mailEvent.timestamp = QDateTime::currentDateTimeUtc();
            mailEvent.item = item;
            PUBLISH(mailEvent);
        }
    }
    
    emit folderSyncFinished(QString::number(folder.id()), true);
    
    // Publish SyncFinishedEvent
    WinoMail::Events::SyncFinishedEvent syncFinishEvent;
    syncFinishEvent.eventId = QString::number(QDateTime::currentMSecsSinceEpoch()) + "_" + QString::number(rand());
    syncFinishEvent.timestamp = QDateTime::currentDateTimeUtc();
    syncFinishEvent.accountId = m_account.id();
    syncFinishEvent.folderId = QString::number(folder.id());
    syncFinishEvent.success = true;
    syncFinishEvent.errorMessage = "";
    PUBLISH(syncFinishEvent);
    
    return true;
}

QVector<Folder> GmailSynchronizer::getFolders() const
{
    // En una implementación real, llamaríamos a Gmail API para obtener las etiquetas (labels)
    // Por ahora, devolvemos algunas etiquetas básicas
    
    QVector<Folder> folders;
    // En Gmail, las carpetas se llaman "labels"
    folders.append(Folder(1, m_account.id(), "Inbox", "INBOX", true, false, false, false, 0, QDateTime::currentDateTime()));
    folders.append(Folder(2, m_account.id(), "Drafts", "DRAFTS", false, true, false, false, 0, QDateTime::currentDateTime()));
    folders.append(Folder(3, m_account.id(), "Sent Mail", "SENT", false, false, true, false, 0, QDateTime::currentDateTime()));
    folders.append(Folder(4, m_account.id(), "Trash", "TRASH", false, false, false, true, 0, QDateTime::currentDateTime()));
    folders.append(Folder(5, m_account.id(), "Spam", "SPAM", false, false, false, false, 0, QDateTime::currentDateTime()));
    folders.append(Folder(6, m_account.id(), "Starred", "STARRED", false, false, false, false, 0, QDateTime::currentDateTime()));
    folders.append(Folder(7, m_account.id(), "Important", "IMPORTANT", false, false, false, false, 0, QDateTime::currentDateTime()));
    
    return folders;
}

QVector<MailItem> GmailSynchronizer::fetchMailItems(const QString& folderId, 
                                                   qint64 sinceUid)
{
    Q_UNUSED(sinceUid); // En Gmail usamos history IDs en lugar de UIDs
    
    if (!m_account.isTokenValid()) {
        if (!refreshAccessToken()) {
            qWarning() << "Failed to refresh token for fetching mail items";
            return QVector<MailItem>();
        }
    }
    
    qDebug() << "Fetching complete Gmail messages for label:" << folderId;

    QString labelId = folderId;
    bool localFolderId = false;
    folderId.toInt(&localFolderId);
    if (localFolderId) {
        const auto folder = FolderDao::findById(folderId.toInt());
        if (folder) {
            labelId = folder->parentFolderId();
            if (labelId.isEmpty()) {
                const QString name = folder->name().toLower();
                if (name == QStringLiteral("inbox")) labelId = QStringLiteral("INBOX");
                else if (name == QStringLiteral("sent") || name == QStringLiteral("sent mail")) labelId = QStringLiteral("SENT");
                else if (name == QStringLiteral("drafts")) labelId = QStringLiteral("DRAFT");
                else if (name == QStringLiteral("trash")) labelId = QStringLiteral("TRASH");
                else if (name == QStringLiteral("spam")) labelId = QStringLiteral("SPAM");
                else labelId = folder->name();
            }
        }
    }

    QNetworkAccessManager network;
    auto get = [&](const QUrl &url, QByteArray &data, QString *contentType = nullptr) {
        QNetworkReply *reply = network.get(createAuthRequest(url.toString()));
        QEventLoop loop;
        QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
        loop.exec();
        const bool ok = reply->error() == QNetworkReply::NoError;
        if (ok) {
            if (contentType) *contentType = QString::fromLatin1(reply->header(QNetworkRequest::ContentTypeHeader).toByteArray());
            data = reply->readAll();
        } else {
            qWarning() << "Gmail request failed:" << reply->errorString() << url;
        }
        reply->deleteLater();
        return ok;
    };

    QVector<QJsonObject> messageRefs;
    QString pageToken;
    do {
        QUrl url(buildGmailUrl(QStringLiteral("messages")));
        QUrlQuery query;
        query.addQueryItem(QStringLiteral("labelIds"), labelId);
        query.addQueryItem(QStringLiteral("maxResults"), QStringLiteral("100"));
        if (!pageToken.isEmpty()) query.addQueryItem(QStringLiteral("pageToken"), pageToken);
        url.setQuery(query);

        QByteArray response;
        if (!get(url, response)) return {};
        QJsonParseError parseError;
        const QJsonDocument document = QJsonDocument::fromJson(response, &parseError);
        if (parseError.error != QJsonParseError::NoError || !document.isObject()) {
            qWarning() << "Invalid Gmail message list:" << parseError.errorString();
            return {};
        }
        const QJsonObject object = document.object();
        for (const QJsonValue &value : object.value(QStringLiteral("messages")).toArray())
            if (value.isObject()) messageRefs.append(value.toObject());
        pageToken = object.value(QStringLiteral("nextPageToken")).toString();
    } while (!pageToken.isEmpty());

    QVector<MailItem> items;
    MimeStorageService mimeStorage;
    for (const QJsonObject &reference : messageRefs) {
        const QString gmailId = reference.value(QStringLiteral("id")).toString();
        if (gmailId.isEmpty()) continue;
        QUrl rawUrl(buildGmailUrl(QStringLiteral("messages/%1").arg(gmailId)));
        QUrlQuery rawQuery;
        rawQuery.addQueryItem(QStringLiteral("format"), QStringLiteral("raw"));
        rawUrl.setQuery(rawQuery);
        QByteArray response;
        if (!get(rawUrl, response)) continue;

        QJsonParseError parseError;
        const QJsonDocument document = QJsonDocument::fromJson(response, &parseError);
        if (parseError.error != QJsonParseError::NoError || !document.isObject()) continue;
        const QJsonObject object = document.object();
        QByteArray encoded = object.value(QStringLiteral("raw")).toString().toLatin1();
        encoded.replace('-', '+');
        encoded.replace('_', '/');
        while (encoded.size() % 4) encoded.append('=');
        const QByteArray rawMime = QByteArray::fromBase64(encoded);
        ParsedMimeMessage parsed;
        if (rawMime.isEmpty() || !mimeStorage.parseMessage(rawMime, parsed)) continue;

        MailItem item;
        item.setFolderId(folderId.toInt());
        item.setMessageId(gmailId);
        item.setRawMime(rawMime);
        item.setSubject(parsed.subject.isEmpty() ? QStringLiteral("(No Subject)") : parsed.subject);
        item.setSender(parsed.from);
        item.setRecipient(parsed.to);
        item.setTo(parsed.to);
        item.setCc(parsed.cc);
        item.setBcc(parsed.bcc);
        item.setDate(parsed.date);
        item.setBodyHtml(parsed.bodyHtml);
        item.setSize(rawMime.size());
        item.setRead(true);
        QVector<QString> attachmentNames;
        for (const ParsedMimeAttachment &attachment : parsed.attachments) attachmentNames.append(attachment.fileName);
        item.setAttachments(attachmentNames);
        const QJsonArray labels = object.value(QStringLiteral("labelIds")).toArray();
        for (const QJsonValue &label : labels) {
            if (label.toString() == QStringLiteral("UNREAD")) item.setRead(false);
            if (label.toString() == QStringLiteral("STARRED")) item.setFlagged(true);
        }
        items.append(item);
    }
    return items;
}

bool GmailSynchronizer::appendMailItem(const QString& folderId, const MailItem& item)
{
    Q_UNUSED(folderId);
    Q_UNUSED(item);
    qWarning() << "Gmail appendMailItem is not used for sending; MailService sends via Gmail API";
    return false;
}

bool GmailSynchronizer::updateMailItemFlags(const QString& folderId, 
                                           const QString& itemUid,
                                           bool read, bool flagged)
{
    Q_UNUSED(folderId);
    Q_UNUSED(itemUid);
    Q_UNUSED(read);
    Q_UNUSED(flagged);
    qWarning() << "Gmail flag update is not available through this synchronizer";
    return false;
}

bool GmailSynchronizer::deleteMailItem(const QString& folderId, 
                                      const QString& itemUid)
{
    Q_UNUSED(folderId);
    Q_UNUSED(itemUid);
    qWarning() << "Gmail delete is not available through this synchronizer";
    return false;
}

void GmailSynchronizer::onGmailReplyFinished(QNetworkReply* reply)
{
    if (reply->error() != QNetworkReply::NoError) {
        qWarning() << "Gmail API error:" << reply->errorString();
        emit errorOccurred(reply->errorString());
        reply->deleteLater();
        return;
    }
    
    QByteArray responseData = reply->readAll();
    reply->deleteLater();
    
    QJsonParseError parseError;
    QJsonDocument doc = QJsonDocument::fromJson(responseData, &parseError);
    
    if (parseError.error != QJsonParseError::NoError) {
        qWarning() << "Failed to parse Gmail API response:" << parseError.errorString();
        emit errorOccurred("Failed to parse server response");
        return;
    }
    
    if (!doc.isObject()) {
        qWarning() << "Gmail API response is not an object";
        emit errorOccurred("Invalid server response");
        return;
    }
    
    QJsonObject obj = doc.object();
    
    // Procesar según el tipo de llamada
    // En una implementación completa, aquí manejaríamos las respuestas
    // para obtener etiquetas, mensajes, etc.
    
    qDebug() << "Gmail API response received successfully";
}

void GmailSynchronizer::onHistoryTimer()
{
    qDebug() << "History timer fired - checking for changes";
    
    // En una implementación real, aquí usaríamos los history IDs
    // obtenidos previamente para consultar solo los cambios
    // desde la última sincronización
    
    // Por ahora, simplemente iniciamos una sincronización completa
    // de todas las etiquetas (esto sería ineficiente en producción)
    QVector<Folder> folders = getFolders();
    for (const Folder& folder : folders) {
        syncFolder(Folder(folder.id(), 0, QString()));
    }
}

QString GmailSynchronizer::buildGmailUrl(const QString& endpoint) const
{
    QString normalized = endpoint;
    while (normalized.startsWith('/')) normalized.remove(0, 1);
    return QStringLiteral("https://gmail.googleapis.com/gmail/v1/users/%1/%2")
        .arg(QString::fromUtf8(QUrl::toPercentEncoding(m_account.email())))
        .arg(normalized);
}

QNetworkRequest GmailSynchronizer::createAuthRequest(const QString& url) const
{
    QNetworkRequest request{QUrl(url)};
    request.setRawHeader("Authorization", 
                        QString("Bearer %1").arg(m_accessToken).toUtf8());
    request.setHeader(QNetworkRequest::ContentTypeHeader, 
                     "application/json");
    return request;
}

bool GmailSynchronizer::refreshAccessToken()
{
    if (m_refreshToken.isEmpty()) return false;
    const QString clientId = qEnvironmentVariable("WINO_GMAIL_CLIENT_ID");
    const QString clientSecret = qEnvironmentVariable("WINO_GMAIL_CLIENT_SECRET");
    if (clientId.isEmpty() || clientSecret.isEmpty()) {
        qWarning() << "Gmail token expired and WINO_GMAIL_CLIENT_ID/SECRET are not configured";
        return false;
    }

    QNetworkAccessManager network;
    QNetworkRequest request(QUrl(QStringLiteral("https://oauth2.googleapis.com/token")));
    request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/x-www-form-urlencoded"));
    QUrlQuery form;
    form.addQueryItem(QStringLiteral("client_id"), clientId);
    form.addQueryItem(QStringLiteral("client_secret"), clientSecret);
    form.addQueryItem(QStringLiteral("refresh_token"), m_refreshToken);
    form.addQueryItem(QStringLiteral("grant_type"), QStringLiteral("refresh_token"));
    QNetworkReply *reply = network.post(request, form.toString(QUrl::FullyEncoded).toUtf8());
    QEventLoop loop;
    QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
    loop.exec();
    if (reply->error() != QNetworkReply::NoError) {
        qWarning() << "Gmail token refresh failed:" << reply->errorString();
        reply->deleteLater();
        return false;
    }
    const QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
    reply->deleteLater();
    const QString token = document.object().value(QStringLiteral("access_token")).toString();
    if (token.isEmpty()) return false;
    m_accessToken = token;
    const int expiresIn = document.object().value(QStringLiteral("expires_in")).toInt(3600);
    m_tokenExpires = QDateTime::currentDateTimeUtc().addSecs(expiresIn);
    m_account.setAccessToken(m_accessToken);
    m_account.setTokenExpires(m_tokenExpires);
    return true;
}

qint64 GmailSynchronizer::getHistoryId(const QString& folderId) const
{
    return m_historyIds.value(folderId, 0);
}

void GmailSynchronizer::setHistoryId(const QString& folderId, qint64 historyId)
{
    m_historyIds.insert(folderId, historyId);
}

MailItem GmailSynchronizer::parseMailItemFromGmail(const QJsonObject& mailJson) const
{
    MailItem item;
    
    // ID de Gmail es la clave primaria
    item.setId(mailJson["id"].toString().toLongLong()); 
    
    // El snippet sirve como cuerpo preliminar
    item.setBodyHtml(mailJson["snippet"].toString());
    
    // Parsear headers
    QJsonArray headers = mailJson["payload"].toObject()["headers"].toArray();
    
    QString from = extractHeaderValue(headers, "From").value(0);
    QString subject = extractHeaderValue(headers, "Subject").value(0);
    QString dateStr = extractHeaderValue(headers, "Date").value(0);
    
    item.setSender(from.isEmpty() ? "Unknown" : from);
    item.setSubject(subject.isEmpty() ? "(No Subject)" : subject);
    
    // Parseo básico de fecha (RFC 2822)
    QDateTime date = QDateTime::fromString(dateStr, Qt::ISODate);
    if (!date.isValid()) {
        date = QDateTime::currentDateTime();
    }
    item.setDate(date);
    
    return item;
}

Folder GmailSynchronizer::parseFolderFromGmail(const QJsonObject& labelJson) const
{
    // Esta función convertiría un objeto JSON de Gmail API (label)
    // a nuestro objeto Folder
    
    // Por ahora, devolvemos un objeto vacío como stub
    return Folder();
}

QStringList GmailSynchronizer::extractHeaderValue(const QJsonArray& headers, const QString& name) const
{
    QStringList values;
    for (const QJsonValue& headerVal : headers) {
        if (headerVal.isObject()) {
            QJsonObject header = headerVal.toObject();
            if (header.contains("name") && header["name"].toString().compare(name, Qt::CaseInsensitive) == 0) {
                values.append(header["value"].toString());
            }
        }
    }
    return values;
}
