#include "accountservice.h"
#include <QDebug>
#include <QSslSocket>
#include "db/dao/accountdao.h"
#include "db/dao/folderdao.h"
#include "core/authenticator.h"
#include "core/gmailauthenticator.h"
#include "core/outlookauthenticator.h"
#include "core/imapauthenticator.h"
#include "core/synchronizerprovider.h"
#include "core/eventbus.h"
#include "core/events.h"
#include "db/databasemanager.h"
#include <QSqlQuery>

AccountService::AccountService(QObject *parent)
    : QObject(parent)
{
}

QVector<Account> AccountService::getAllAccounts()
{
    return AccountDao::findAll();
}

Account* AccountService::findAccountById(int id)
{
    return AccountDao::findById(id);
}

Account* AccountService::findAccountByEmail(const QString &email)
{
    return AccountDao::findByEmail(email);
}

void AccountService::addAccount(const QString &email, const QString &providerType,
                                 const QString &accessToken, const QString &refreshToken)
{
    Account account;
    account.setEmail(email);
    account.setDisplayName(email);
    
    if (providerType == "gmail") account.setType(AccountType::Gmail);
    else if (providerType == "outlook") account.setType(AccountType::Outlook);
    else account.setType(AccountType::IMAP);
    
    account.setAccessToken(accessToken);
    account.setRefreshToken(refreshToken);
    
    qint64 id = AccountDao::insert(account);
    if (id != -1) {
        account.setId(id);
        qDebug() << "[AccountService] Account added:" << email;
        publishAccountEvent(account, true);
        emit accountAdded(account);
        emit accountListChanged();
    } else {
        qWarning() << "[AccountService] Failed to add account:" << email;
    }
}

void AccountService::addAccount(const Account &account)
{
    qint64 id = AccountDao::insert(account);
    if (id != -1) {
        Account accToAdd = account; // make a copy we can modify
        accToAdd.setId(id);
        qDebug() << "[AccountService] Account added:" << accToAdd.email();
        qDebug() << "[AccountService] Generated ID for new account:" << id;

        // Create default folders for the new account (fallback)
        QStringList defaultFolderNames = {"Inbox", "Sent", "Drafts", "Trash", "Spam"};
        for (const QString &folderName : defaultFolderNames) {
            Folder folder;
            folder.setName(folderName);
            folder.setAccountId(accToAdd.id());
            // Set special folder flags
            if (folderName == "Inbox") folder.setInbox(true);
            else if (folderName == "Sent") folder.setSent(true);
            else if (folderName == "Drafts") folder.setDrafts(true);
            else if (folderName == "Trash") folder.setTrash(true);
            if (!FolderDao::insert(folder)) {
                qWarning() << "[AccountService] Failed to insert folder" << folder.name()
                           << "for account ID" << accToAdd.id();
            }
        }

        // Attempt to sync real folders from the server; if successful, replace defaults
        syncFoldersForAccount(accToAdd);

        publishAccountEvent(accToAdd, true);
        emit accountAdded(accToAdd);
        emit accountListChanged();
    } else {
        qWarning() << "[AccountService] Failed to add account:" << account.email();
    }
}

void AccountService::removeAccount(int accountId)
{
    Account *account = AccountDao::findById(accountId);
    if (account) {
        SynchronizerProvider::instance().unregisterSynchronizer(QString::number(accountId));
        AccountDao::remove(accountId);
        publishAccountEvent(*account, false);
        emit accountRemoved(accountId);
        emit accountListChanged();
        delete account;
    }
}

void AccountService::updateAccount(const Account &account)
{
    if (AccountDao::update(account)) {
        qDebug() << "[AccountService] Account updated:" << account.email();
        // Ensure synchronizer is re‑initialized with new connection settings
        syncFoldersForAccount(account);
        emit accountListChanged();
    }
}

void AccountService::startAuthentication(const QString &email, const QString &providerType)
{
    Authenticator *auth = createAuthenticator(providerType);
    if (auth) {
        connect(auth, &Authenticator::authenticationCompleted, this, [this](const Account &account) {
            addAccount(account.email(),
                       account.type() == AccountType::Gmail ? "gmail"
                       : account.type() == AccountType::Outlook ? "outlook" : "imap",
                       account.accessToken(), account.refreshToken());
        });
        connect(auth, &Authenticator::authenticationFailed, this, [](const QString &error) {
            qWarning() << "[AccountService] Authentication failed:" << error;
        });
        connect(auth, &QObject::destroyed, auth, [auth]() { /* cleanup handled by Qt */ });
        auth->authenticate(email);
    }
}

Authenticator* AccountService::createAuthenticator(const QString &providerType)
{
    if (providerType == "gmail") return new GmailAuthenticator(this);
    if (providerType == "outlook") return new OutlookAuthenticator(this);
    if (providerType == "imap") return new ImapAuthenticator(this);
    qWarning() << "[AccountService] Unknown provider:" << providerType;
    return nullptr;
}

bool AccountService::testConnection(const Account::ConnectionSettings &settings, QString &errorMessage)
{
    QSslSocket socket;
    if (settings.incomingSsl) {
        socket.connectToHostEncrypted(settings.incomingHost, settings.incomingPort);
        if (!socket.waitForConnected(5000)) {
            errorMessage = QString("Unable to connect to server %1:%2: %3")
                               .arg(settings.incomingHost)
                               .arg(settings.incomingPort)
                               .arg(socket.errorString());
            return false;
        }
        if (!socket.waitForEncrypted(5000)) {
            errorMessage = QString("TLS handshake failed: %1").arg(socket.errorString());
            return false;
        }
    } else {
        socket.connectToHost(settings.incomingHost, settings.incomingPort);
        if (!socket.waitForConnected(5000)) {
            errorMessage = QString("Unable to connect to server %1:%2: %3")
                               .arg(settings.incomingHost)
                               .arg(settings.incomingPort)
                               .arg(socket.errorString());
            return false;
        }
    }
    // Optionally read greeting (ignore)
    if (!socket.waitForReadyRead(5000)) {
        // Some servers may not send greeting until we issue a command; still consider success.
    }
    return true;
}

void AccountService::publishAccountEvent(const Account &account, bool added)
{
    if (added) {
        WinoMail::Events::AccountAddedEvent event;
        event.account = account;
        EVENT_BUS.publish(event);
    } else {
        WinoMail::Events::AccountRemovedEvent event;
        event.accountId = account.id();
        EVENT_BUS.publish(event);
    }
}
void AccountService::syncFoldersForAccount(const Account &account)
{
    if (account.email().isEmpty()) {
        qWarning() << "[AccountService] Invalid account for folder sync (empty email)";
        return;
    }

    // Only IMAP accounts support folder listing via SYNC
    if (account.type() != AccountType::IMAP) {
        qDebug() << QStringLiteral("[AccountService] Folder sync only supported for IMAP accounts; skipping");
        return;
    }

    QString accountIdStr = QString::number(account.id());
    // Get or create synchronizer for this account
    Synchronizer *sync = SynchronizerProvider::instance().getSynchronizer(accountIdStr);
    if (!sync) {
        sync = SynchronizerProvider::instance().createSynchronizer(accountIdStr, QStringLiteral("imap"));
        if (!sync) {
            qWarning() << QStringLiteral("[AccountService] Failed to create IMAP synchronizer for account") << accountIdStr;
            return;
        }
    }
    // Initialize the synchronizer with the account (sets m_account)
    if (!sync->initialize(account)) {
        qWarning() << QStringLiteral("[AccountService] Failed to initialize IMAP synchronizer for account") << accountIdStr;
        // Do not treat as fatal; we can still try to get folders (some impls may not need init)
    }

    // Retrieve folders from the server
    QVector<Folder> remoteFolders = sync->getFolders();
    if (remoteFolders.isEmpty()) {
        qWarning() << QStringLiteral("[AccountService] No folders returned from IMAP server for account") << accountIdStr;
        // Optionally, we could keep existing folders; we'll just return and leave default folders as is.
        return;
    }

    // Remove existing folders for this account to avoid duplicates
    if (!FolderDao::removeByAccountId(account.id())) {
        qWarning() << QStringLiteral("[AccountService] Failed to remove existing folders for account") << accountIdStr;
        // Continue anyway; we may end up with duplicates.
    }

    // Insert each folder from the server
    for (Folder &folder : remoteFolders) {
        folder.setAccountId(account.id());
        // Ensure the folder has an invalid ID (0) so DB assigns new one
        folder.setId(0);
        if (!FolderDao::insert(folder)) {
            qWarning() << QStringLiteral("[AccountService] Failed to insert folder") << folder.name() << QStringLiteral("for account") << accountIdStr;
        }
    }

    qDebug() << QStringLiteral("[AccountService] Synced %1 folders for account %2").arg(remoteFolders.size()).arg(accountIdStr);
}

    void AccountService::notifyAccountAdded(const Account &account)
    {
        emit accountAdded(account);
        emit accountListChanged();
    }
#include "accountservice.moc"