#include "templateengine.h"
#include <QFile>
#include <QDir>
#include <QTextStream>
#include <QProcess>
#include <QRegularExpression>
#include <QStandardPaths>

QString TemplateEngine::exportDir()
{
    QString dir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/BudgetPro_Export";
    if (!QDir(dir).exists())
        QDir().mkpath(dir);
    return dir;
}

QString TemplateEngine::resolveValue(const QString &text, const QJsonObject &data)
{
    QString result = text;
    static QRegularExpression re("\\{\\{(.+?)\\}\\}");
    QRegularExpressionMatchIterator it = re.globalMatch(text);
    while (it.hasNext()) {
        QRegularExpressionMatch match = it.next();
        QString path = match.captured(1).trimmed();
        QStringList parts = path.split(".");
        QJsonValue val = data;
        for (const QString &part : parts) {
            if (val.isObject())
                val = val.toObject()[part];
            else if (val.isArray()) {
                bool ok;
                int idx = part.toInt(&ok);
                if (ok)
                    val = val.toArray()[idx];
                else
                    val = QJsonValue();
            } else {
                val = QJsonValue();
                break;
            }
        }
        QString replacement;
        if (val.isDouble())
            replacement = QString::number(val.toDouble(), 'f', 2);
        else
            replacement = val.toString();
        result.replace(match.captured(0), replacement);
    }
    return result;
}

void TemplateEngine::drawLabel(QPainter *painter, const QJsonObject &elem, const QJsonObject &data)
{
    QString text = resolveValue(elem["text"].toString(), data);
    int x = elem["x"].toInt();
    int y = elem["y"].toInt();
    int w = elem.value("w").toInt(150);
    int h = elem.value("h").toInt(20);

    QString font = elem.value("font").toString("Arial");
    int size = elem.value("size").toInt(12);
    bool bold = elem.value("bold").toBool(false);

    QFont f(font, size);
    f.setBold(bold);
    painter->setFont(f);

    painter->drawText(QRect(x, y, w, h), Qt::AlignLeft | Qt::AlignVCenter, text);
}

void TemplateEngine::drawTable(QPainter *painter, const QJsonObject &elem, const QJsonObject &data)
{
    int x = elem["x"].toInt();
    int y = elem["y"].toInt();
    int w = elem["w"].toInt(200);
    int rowHeight = elem.value("rowHeight").toInt(20);
    QJsonArray columns = elem["columns"].toArray();
    int colCount = columns.size();
    int colWidth = w / colCount;

    // Header
    painter->setBrush(QColor("#CCCCCC"));
    painter->setPen(Qt::black);
    for (int c = 0; c < colCount; c++) {
        painter->drawRect(x + c * colWidth, y, colWidth, rowHeight);
        painter->drawText(QRect(x + c * colWidth, y, colWidth, rowHeight),
                          Qt::AlignCenter, columns[c].toString());
    }

    // Data
    QString dsPath = elem["dataSource"].toString();
    QStringList parts = dsPath.split(".");
    QJsonValue dsValue = data;
    for (const QString &part : parts) {
        if (dsValue.isObject())
            dsValue = dsValue.toObject()[part];
        else
            break;
    }

    if (!dsValue.isArray())
        return;

    QJsonArray rows = dsValue.toArray();
    for (int r = 0; r < rows.size(); r++) {
        QJsonObject row = rows[r].toObject();
        int rowY = y + rowHeight + r * rowHeight;
        painter->setBrush((r % 2 == 0) ? Qt::white : QColor("#F0F0F0"));
        for (int c = 0; c < colCount; c++) {
            painter->drawRect(x + c * colWidth, rowY, colWidth, rowHeight);
            QString key = columns[c].toString().toLower();
            QJsonValue cell = row[key];
            QString cellText;
            if (cell.isDouble())
                cellText = QString::number(cell.toDouble(), 'f', 2);
            else
                cellText = cell.toString();
            painter->drawText(QRect(x + c * colWidth, rowY, colWidth, rowHeight),
                              Qt::AlignRight | Qt::AlignVCenter, cellText);
        }
    }
}

void TemplateEngine::drawQRCode(QPainter *painter, const QJsonObject &elem, const QJsonObject &data)
{
    QString qrData = resolveValue(elem["data"].toString(), data);
    int x = elem["x"].toInt();
    int y = elem["y"].toInt();
    int w = elem["w"].toInt(40);
    int h = elem["w"].toInt(40);

    // Simplified QR-like drawing placeholder
    painter->setPen(Qt::black);
    painter->setBrush(Qt::white);
    painter->drawRect(x, y, w, h);
    painter->setBrush(Qt::black);
    int cellSize = 4;
    int hash = qHash(qrData);
    for (int i = 0; i < w / cellSize; i++) {
        for (int j = 0; j < h / cellSize; j++) {
            if ((hash + i * 31 + j * 37) % 3 == 0) {
                painter->drawRect(x + i * cellSize, y + j * cellSize, cellSize, cellSize);
            }
        }
    }
    painter->drawText(QRect(x, y + h + 2, w, 12), Qt::AlignCenter, qrData.left(20));
}

void TemplateEngine::drawImage(QPainter *painter, const QJsonObject &elem)
{
    QString source = elem["source"].toString();
    int x = elem["x"].toInt();
    int y = elem["y"].toInt();
    int w = elem["w"].toInt(60);
    int h = elem["w"].toInt(40);

    QPixmap pixmap(source);
    if (!pixmap.isNull()) {
        painter->drawPixmap(x, y, w, h, pixmap.scaled(w, h, Qt::KeepAspectRatio));
    } else {
        painter->drawRect(x, y, w, h);
        painter->drawText(QRect(x, y, w, h), Qt::AlignCenter, "[IMG]");
    }
}

void TemplateEngine::drawLine(QPainter *painter, const QJsonObject &elem)
{
    painter->setPen(QPen(Qt::black, 1));
    int x = elem["x"].toInt();
    int y = elem["y"].toInt();
    int w = elem["w"].toInt(200);
    painter->drawLine(x, y, x + w, y);
}

void TemplateEngine::drawJsonElements(QPainter *painter, const QJsonArray &elements, const QJsonObject &data)
{
    for (const QJsonValue &val : elements) {
        QJsonObject elem = val.toObject();
        QString type = elem["type"].toString();
        if (type == "label")
            drawLabel(painter, elem, data);
        else if (type == "table")
            drawTable(painter, elem, data);
        else if (type == "qr")
            drawQRCode(painter, elem, data);
        else if (type == "image")
            drawImage(painter, elem);
        else if (type == "line")
            drawLine(painter, elem);
    }
}

QString TemplateEngine::exportToPDF(const Template &tpl, const QJsonObject &data)
{
    QString filePath = exportDir() + "/" + tpl.name + ".pdf";

    QPrinter printer(QPrinter::ScreenResolution);
    printer.setPageSize(QPrinter::A4);
    printer.setOutputFormat(QPrinter::Pdf);
    printer.setOutputFileName(filePath);

    QPainter painter(&printer);
    QJsonDocument doc = QJsonDocument::fromJson(tpl.content.toUtf8());
    QJsonArray elements = doc.object()["elements"].toArray();
    drawJsonElements(&painter, elements, data);
    painter.end();

    return filePath;
}

QString TemplateEngine::exportToXLSX(const Template &tpl, const QJsonObject &data)
{
    QString filePath = exportDir() + "/" + tpl.name + ".csv";

    // Fallback to CSV since QXlsx may not be installed
    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
        return QString();
    QTextStream out(&file);

    QJsonDocument doc = QJsonDocument::fromJson(tpl.content.toUtf8());
    QJsonArray elements = doc.object()["elements"].toArray();
    for (const QJsonValue &val : elements) {
        QJsonObject elem = val.toObject();
        if (elem["type"].toString() == "label") {
            QString text = resolveValue(elem["text"].toString(), data);
            out << text << "\n";
        }
    }
    file.close();
    return filePath;
}

QString TemplateEngine::exportToDOCX(const Template &tpl, const QJsonObject &data)
{
    QString filePath = exportDir() + "/" + tpl.name + ".docx";
    QString htmlPath = exportDir() + "/" + tpl.name + "_temp.html";

    // Generate HTML
    QFile htmlFile(htmlPath);
    if (!htmlFile.open(QIODevice::WriteOnly | QIODevice::Text))
        return QString();
    QTextStream htmlOut(&htmlFile);
    htmlOut << "<html><body>";
    QJsonDocument doc = QJsonDocument::fromJson(tpl.content.toUtf8());
    QJsonArray elements = doc.object()["elements"].toArray();
    for (const QJsonValue &val : elements) {
        QJsonObject elem = val.toObject();
        if (elem["type"].toString() == "label") {
            QString text = resolveValue(elem["text"].toString(), data);
            htmlOut << "<p>" << text << "</p>";
        }
    }
    htmlOut << "</body></html>";
    htmlFile.close();

    // Try pandoc
    QString cmd = "pandoc \"" + htmlPath + "\" -o \"" + filePath + "\"";
    QProcess::execute(cmd);

    // Clean up temp HTML
    QFile::remove(htmlPath);

    return filePath;
}