#include "templatedao.h"
#include <QSqlQuery>
#include <QSqlError>
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QDateTime>

bool TemplateDao::insert(Template& tmpl)
{
    QSqlDatabase db = DatabaseManager::instance().database();
    QSqlQuery query(db);
    query.prepare(
        "INSERT INTO Template (accountId, name, subject, bodyHtml, bodyText, variables, isDefault, createdAt, updatedAt) "
        "VALUES (:accountId, :name, :subject, :bodyHtml, :bodyText, :variables, :isDefault, :createdAt, :updatedAt)"
    );
    query.bindValue(":accountId", tmpl.accountId >= 0 ? tmpl.accountId : QVariant());
    query.bindValue(":name", tmpl.name);
    query.bindValue(":subject", tmpl.subject);
    query.bindValue(":bodyHtml", tmpl.bodyHtml);
    query.bindValue(":bodyText", tmpl.bodyText);
    
    QJsonArray varsArray;
    for (const TemplateVariable& var : tmpl.variables) {
        QJsonObject varObj;
        varObj["name"] = var.name;
        varObj["label"] = var.label;
        varObj["defaultValue"] = var.defaultValue;
        varObj["type"] = var.type;
        varObj["options"] = QJsonArray::fromStringList(var.options);
        varsArray.append(varObj);
    }
    QJsonDocument varsDoc(varsArray);
    query.bindValue(":variables", varsDoc.toJson(QJsonDocument::Compact));
    
    query.bindValue(":isDefault", tmpl.isDefault ? 1 : 0);
    query.bindValue(":createdAt", QDateTime::currentDateTime());
    query.bindValue(":updatedAt", QDateTime::currentDateTime());

    if (!query.exec()) {
        qWarning() << "Failed to insert template:" << query.lastError().text();
        return false;
    }
    tmpl.id = query.lastInsertId().toLongLong();
    return true;
}

bool TemplateDao::update(const Template& tmpl)
{
    QSqlDatabase db = DatabaseManager::instance().database();
    QSqlQuery query(db);
    query.prepare(
        "UPDATE Template SET "
        "accountId = :accountId, "
        "name = :name, "
        "subject = :subject, "
        "bodyHtml = :bodyHtml, "
        "bodyText = :bodyText, "
        "variables = :variables, "
        "isDefault = :isDefault, "
        "updatedAt = :updatedAt "
        "WHERE id = :id"
    );
    query.bindValue(":id", tmpl.id);
    query.bindValue(":accountId", tmpl.accountId >= 0 ? tmpl.accountId : QVariant());
    query.bindValue(":name", tmpl.name);
    query.bindValue(":subject", tmpl.subject);
    query.bindValue(":bodyHtml", tmpl.bodyHtml);
    query.bindValue(":bodyText", tmpl.bodyText);
    
    QJsonArray varsArray;
    for (const TemplateVariable& var : tmpl.variables) {
        QJsonObject varObj;
        varObj["name"] = var.name;
        varObj["label"] = var.label;
        varObj["defaultValue"] = var.defaultValue;
        varObj["type"] = var.type;
        varObj["options"] = QJsonArray::fromStringList(var.options);
        varsArray.append(varObj);
    }
    QJsonDocument varsDoc(varsArray);
    query.bindValue(":variables", varsDoc.toJson(QJsonDocument::Compact));
    
    query.bindValue(":isDefault", tmpl.isDefault ? 1 : 0);
    query.bindValue(":updatedAt", QDateTime::currentDateTime());

    if (!query.exec()) {
        qWarning() << "Failed to update template:" << query.lastError().text();
        return false;
    }
    return query.numRowsAffected() > 0;
}

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

    if (!query.exec()) {
        qWarning() << "Failed to delete template:" << query.lastError().text();
        return false;
    }
    return query.numRowsAffected() > 0;
}

std::optional<Template> TemplateDao::findById(qint64 id)
{
    QSqlDatabase db = DatabaseManager::instance().database();
    QSqlQuery query(db);
    query.prepare("SELECT id, accountId, name, subject, bodyHtml, bodyText, variables, isDefault, createdAt, updatedAt FROM Template WHERE id = :id");
    query.bindValue(":id", id);

    if (!query.exec() || !query.next()) {
        return std::nullopt;
    }

    Template tmpl;
    tmpl.id = query.value(0).toLongLong();
    tmpl.accountId = query.value(1).isNull() ? -1 : query.value(1).toLongLong();
    tmpl.name = query.value(2).toString();
    tmpl.subject = query.value(3).toString();
    tmpl.bodyHtml = query.value(4).toString();
    tmpl.bodyText = query.value(5).toString();
    
    QJsonDocument varsDoc = QJsonDocument::fromJson(query.value(6).toByteArray());
    QJsonArray varsArray = varsDoc.array();
    for (const auto& v : varsArray) {
        QJsonObject obj = v.toObject();
        TemplateVariable var;
        var.name = obj["name"].toString();
        var.label = obj["label"].toString();
        var.defaultValue = obj["defaultValue"].toString();
        var.type = obj["type"].toString();
        QJsonArray optsArray = obj["options"].toArray();
        for (const auto& opt : optsArray) {
            var.options.append(opt.toString());
        }
        tmpl.variables.append(var);
    }
    
    tmpl.isDefault = query.value(7).toBool();
    tmpl.createdAt = QDateTime::fromString(query.value(8).toString(), Qt::ISODate);
    tmpl.updatedAt = QDateTime::fromString(query.value(9).toString(), Qt::ISODate);
    return tmpl;
}

QVector<Template> TemplateDao::findByAccount(qint64 accountId)
{
    QSqlDatabase db = DatabaseManager::instance().database();
    QSqlQuery query(db);
    if (accountId >= 0) {
        query.prepare("SELECT id, accountId, name, subject, bodyHtml, bodyText, variables, isDefault, createdAt, updatedAt FROM Template WHERE accountId = :accountId OR accountId IS NULL ORDER BY isDefault DESC, name");
        query.bindValue(":accountId", accountId);
    } else {
        query.prepare("SELECT id, accountId, name, subject, bodyHtml, bodyText, variables, isDefault, createdAt, updatedAt FROM Template WHERE accountId IS NULL ORDER BY isDefault DESC, name");
    }

    QVector<Template> templates;
    if (query.exec()) {
        while (query.next()) {
            Template tmpl;
            tmpl.id = query.value(0).toLongLong();
            tmpl.accountId = query.value(1).isNull() ? -1 : query.value(1).toLongLong();
            tmpl.name = query.value(2).toString();
            tmpl.subject = query.value(3).toString();
            tmpl.bodyHtml = query.value(4).toString();
            tmpl.bodyText = query.value(5).toString();
            
            QJsonDocument varsDoc = QJsonDocument::fromJson(query.value(6).toByteArray());
            QJsonArray varsArray = varsDoc.array();
            for (const auto& v : varsArray) {
                QJsonObject obj = v.toObject();
                TemplateVariable var;
                var.name = obj["name"].toString();
                var.label = obj["label"].toString();
                var.defaultValue = obj["defaultValue"].toString();
                var.type = obj["type"].toString();
                QJsonArray optsArray = obj["options"].toArray();
        for (const auto& opt : optsArray) {
            var.options.append(opt.toString());
        }
                tmpl.variables.append(var);
            }
            
            tmpl.isDefault = query.value(7).toBool();
            tmpl.createdAt = QDateTime::fromString(query.value(8).toString(), Qt::ISODate);
            tmpl.updatedAt = QDateTime::fromString(query.value(9).toString(), Qt::ISODate);
            templates.append(tmpl);
        }
    }
    return templates;
}

QVector<Template> TemplateDao::findGlobal()
{
    return findByAccount(-1);
}

QVector<Template> TemplateDao::findAll()
{
    QSqlDatabase db = DatabaseManager::instance().database();
    QSqlQuery query(db);
    query.prepare("SELECT id, accountId, name, subject, bodyHtml, bodyText, variables, isDefault, createdAt, updatedAt FROM Template ORDER BY accountId, isDefault DESC, name");

    QVector<Template> templates;
    if (query.exec()) {
        while (query.next()) {
            Template tmpl;
            tmpl.id = query.value(0).toLongLong();
            tmpl.accountId = query.value(1).isNull() ? -1 : query.value(1).toLongLong();
            tmpl.name = query.value(2).toString();
            tmpl.subject = query.value(3).toString();
            tmpl.bodyHtml = query.value(4).toString();
            tmpl.bodyText = query.value(5).toString();
            
            QJsonDocument varsDoc = QJsonDocument::fromJson(query.value(6).toByteArray());
            QJsonArray varsArray = varsDoc.array();
            for (const auto& v : varsArray) {
                QJsonObject obj = v.toObject();
                TemplateVariable var;
                var.name = obj["name"].toString();
                var.label = obj["label"].toString();
                var.defaultValue = obj["defaultValue"].toString();
                var.type = obj["type"].toString();
                QJsonArray optsArray = obj["options"].toArray();
        for (const auto& opt : optsArray) {
            var.options.append(opt.toString());
        }
                tmpl.variables.append(var);
            }
            
            tmpl.isDefault = query.value(7).toBool();
            tmpl.createdAt = QDateTime::fromString(query.value(8).toString(), Qt::ISODate);
            tmpl.updatedAt = QDateTime::fromString(query.value(9).toString(), Qt::ISODate);
            templates.append(tmpl);
        }
    }
    return templates;
}

std::optional<Template> TemplateDao::findDefault(qint64 accountId)
{
    QSqlDatabase db = DatabaseManager::instance().database();
    QSqlQuery query(db);
    if (accountId >= 0) {
        query.prepare("SELECT id, accountId, name, subject, bodyHtml, bodyText, variables, isDefault, createdAt, updatedAt FROM Template WHERE (accountId = :accountId OR accountId IS NULL) AND isDefault = 1 LIMIT 1");
        query.bindValue(":accountId", accountId);
    } else {
        query.prepare("SELECT id, accountId, name, subject, bodyHtml, bodyText, variables, isDefault, createdAt, updatedAt FROM Template WHERE accountId IS NULL AND isDefault = 1 LIMIT 1");
    }

    if (!query.exec() || !query.next()) {
        return std::nullopt;
    }

    Template tmpl;
    tmpl.id = query.value(0).toLongLong();
    tmpl.accountId = query.value(1).isNull() ? -1 : query.value(1).toLongLong();
    tmpl.name = query.value(2).toString();
    tmpl.subject = query.value(3).toString();
    tmpl.bodyHtml = query.value(4).toString();
    tmpl.bodyText = query.value(5).toString();
    
    QJsonDocument varsDoc = QJsonDocument::fromJson(query.value(6).toByteArray());
    QJsonArray varsArray = varsDoc.array();
    for (const auto& v : varsArray) {
        QJsonObject obj = v.toObject();
        TemplateVariable var;
        var.name = obj["name"].toString();
        var.label = obj["label"].toString();
        var.defaultValue = obj["defaultValue"].toString();
        var.type = obj["type"].toString();
        QJsonArray optsArray = obj["options"].toArray();
        for (const auto& opt : optsArray) {
            var.options.append(opt.toString());
        }
        tmpl.variables.append(var);
    }
    
    tmpl.isDefault = query.value(7).toBool();
    tmpl.createdAt = QDateTime::fromString(query.value(8).toString(), Qt::ISODate);
    tmpl.updatedAt = QDateTime::fromString(query.value(9).toString(), Qt::ISODate);
    return tmpl;
}

bool TemplateDao::setDefault(qint64 id, bool isDefault)
{
    QSqlDatabase db = DatabaseManager::instance().database();
    db.transaction();
    
    QSqlQuery query(db);
    // First, unset any existing default for this account
    if (isDefault) {
        auto tmplOpt = findById(id);
        if (tmplOpt.has_value()) {
            qint64 accId = tmplOpt->accountId;
            if (accId >= 0) {
                query.prepare("UPDATE Template SET isDefault = 0, updatedAt = :updatedAt WHERE accountId = :accountId AND isDefault = 1");
                query.bindValue(":accountId", accId);
            } else {
                query.prepare("UPDATE Template SET isDefault = 0, updatedAt = :updatedAt WHERE accountId IS NULL AND isDefault = 1");
            }
            query.bindValue(":updatedAt", QDateTime::currentDateTime());
            if (!query.exec()) {
                db.rollback();
                qWarning() << "Failed to unset previous default:" << query.lastError().text();
                return false;
            }
        }
    }
    
    // Set new default
    query.prepare("UPDATE Template SET isDefault = :isDefault, updatedAt = :updatedAt WHERE id = :id");
    query.bindValue(":id", id);
    query.bindValue(":isDefault", isDefault ? 1 : 0);
    query.bindValue(":updatedAt", QDateTime::currentDateTime());

    if (!query.exec()) {
        db.rollback();
        qWarning() << "Failed to set default template:" << query.lastError().text();
        return false;
    }
    
    return db.commit();
}

QJsonObject Template::toJson() const
{
    QJsonObject obj;
    obj["id"] = static_cast<qint64>(id);
    obj["accountId"] = accountId >= 0 ? static_cast<qint64>(accountId) : QJsonValue::Null;
    obj["name"] = name;
    obj["subject"] = subject;
    obj["bodyHtml"] = bodyHtml;
    obj["bodyText"] = bodyText;
    
    QJsonArray varsArray;
    for (const TemplateVariable& var : variables) {
        QJsonObject varObj;
        varObj["name"] = var.name;
        varObj["label"] = var.label;
        varObj["defaultValue"] = var.defaultValue;
        varObj["type"] = var.type;
        varObj["options"] = QJsonArray::fromStringList(var.options);
        varsArray.append(varObj);
    }
    obj["variables"] = varsArray;
    
    obj["isDefault"] = isDefault;
    obj["createdAt"] = createdAt.toString(Qt::ISODate);
    obj["updatedAt"] = updatedAt.toString(Qt::ISODate);
    return obj;
}

Template Template::fromJson(const QJsonObject& obj)
{
    Template tmpl;
    tmpl.id = obj["id"].toVariant().toLongLong();
    tmpl.accountId = obj["accountId"].isNull() ? -1 : obj["accountId"].toVariant().toLongLong();
    tmpl.name = obj["name"].toString();
    tmpl.subject = obj["subject"].toString();
    tmpl.bodyHtml = obj["bodyHtml"].toString();
    tmpl.bodyText = obj["bodyText"].toString();
    
    QJsonArray varsArray = obj["variables"].toArray();
    for (const auto& v : varsArray) {
        QJsonObject varObj = v.toObject();
        TemplateVariable var;
        var.name = varObj["name"].toString();
        var.label = varObj["label"].toString();
        var.defaultValue = varObj["defaultValue"].toString();
        var.type = varObj["type"].toString();
        QJsonArray optsArray = varObj["options"].toArray();
        for (const auto& opt : optsArray) {
            var.options.append(opt.toString());
        }
        tmpl.variables.append(var);
    }
    
    tmpl.isDefault = obj["isDefault"].toBool();
    tmpl.createdAt = QDateTime::fromString(obj["createdAt"].toString(), Qt::ISODate);
    tmpl.updatedAt = QDateTime::fromString(obj["updatedAt"].toString(), Qt::ISODate);
    return tmpl;
}