// ============================================================
//  À AJOUTER DANS function.h
// ============================================================

#include <QString>
#include <QJsonObject>
#include <QJsonArray>

// ----- 1. Créer un nouveau ticket -----
/**
 * Crée un ticket sur le serveur avec les infos du formulaire.
 * Les 2 fichiers (ORG + MOD) sont zippés ensemble et envoyés.
 *
 * @param licensedTo  nom du client (ex: "Zizi Aksel")
 * @param brand       Audi, Peugeot...
 * @param system      DCM 6.2V...
 * @param otherInfo   texte libre
 * @param module      STAGE 1, STAGE 2, E85...
 * @param carModel    caddy, 308...
 * @param carYear     2017
 * @param gear        Manual / Auto
 * @param orgFilePath chemin du fichier ORG
 * @param modFilePath chemin du fichier MOD (peut être vide)
 * @param message     premier message du ticket
 * @param ticketId    [out] ID du ticket créé (ex: "T20260419_ABC123")
 * @param response    [out] JSON brut du serveur
 * @return true si succès
 */
bool createTicket(const QString &licensedTo,
                  const QString &brand,
                  const QString &system,
                  const QString &otherInfo,
                  const QString &module,
                  const QString &carModel,
                  const QString &carYear,
                  const QString &gear,
                  const QString &orgFilePath,
                  const QString &modFilePath,
                  const QString &message,
                  QString &ticketId,
                  QString &response);

// ----- 2. Envoyer un nouveau message dans un ticket existant -----
bool sendTicketMessage(const QString &ticketId,
                       const QString &message,
                       QString &response);

// ----- 3. Récupérer les messages d'un ticket (chat) -----
/**
 * @param messages [out] liste des messages (from, text, file_url, timestamp)
 * @param status   [out] OffLine / Online / Closed / Solved
 */
bool fetchTicketMessages(const QString &ticketId,
                         QJsonArray &messages,
                         QString &status,
                         QString &response);

// ----- 4. Télécharger un fichier solution envoyé par l'admin -----
bool downloadSolutionFile(const QString &fileUrl,
                          const QString &savePath,
                          QString &response);

// ----- 5. Lister tous mes tickets -----
bool listMyTickets(QJsonArray &tickets, QString &response);


// ============================================================
//  À AJOUTER DANS function.cpp
// ============================================================

#include <QFile>
#include <QFileInfo>
#include <QUuid>
#include <QDateTime>
#include <QHttpMultiPart>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QEventLoop>
#include <QDataStream>
#include <QUrl>
#include <QUrlQuery>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>

// --- Config globale (change selon ton serveur) ---
static const QString TICKET_SERVER_URL = "https://monsite.com/ticket.php";
static const QString API_TOKEN = "CHANGE_MOI_abc123xyz789";
static const QString USER_AGENT = "OneClickTune";


// --- Helper : CRC32 pour ZIP ---
static quint32 computeCrc32(const QByteArray &data)
{
    static quint32 crcTable[256];
    static bool ready = false;
    if (!ready) {
        for (quint32 i = 0; i < 256; ++i) {
            quint32 c = i;
            for (int k = 0; k < 8; ++k)
                c = (c & 1) ? (0xEDB88320 ^ (c >> 1)) : (c >> 1);
            crcTable[i] = c;
        }
        ready = true;
    }
    quint32 crc = 0xFFFFFFFF;
    const char *p = data.constData();
    const int len = data.size();
    for (int i = 0; i < len; ++i)
        crc = crcTable[(crc ^ (quint8)p[i]) & 0xFF] ^ (crc >> 8);
    return crc ^ 0xFFFFFFFF;
}

// --- Helper : ajouter un fichier dans un zip en mémoire ---
static void appendFileToZip(QByteArray &zipData,
                            QList<QByteArray> &cdEntries,
                            const QByteArray &fileName,
                            const QByteArray &fileContent)
{
    quint32 crc = computeCrc32(fileContent);
    quint32 size = (quint32)fileContent.size();
    quint32 localHeaderOffset = (quint32)zipData.size();

    // Local File Header
    QByteArray lfh;
    QDataStream lhs(&lfh, QIODevice::WriteOnly);
    lhs.setByteOrder(QDataStream::LittleEndian);
    lhs << (quint32)0x04034b50;
    lhs << (quint16)20 << (quint16)0 << (quint16)0;
    lhs << (quint16)0 << (quint16)0;
    lhs << crc << size << size;
    lhs << (quint16)fileName.size() << (quint16)0;
    zipData.append(lfh);
    zipData.append(fileName);
    zipData.append(fileContent);

    // Central Directory Entry
    QByteArray cd;
    QDataStream cs(&cd, QIODevice::WriteOnly);
    cs.setByteOrder(QDataStream::LittleEndian);
    cs << (quint32)0x02014b50;
    cs << (quint16)20 << (quint16)20;
    cs << (quint16)0 << (quint16)0;
    cs << (quint16)0 << (quint16)0;
    cs << crc << size << size;
    cs << (quint16)fileName.size();
    cs << (quint16)0 << (quint16)0;
    cs << (quint16)0 << (quint16)0;
    cs << (quint32)0 << localHeaderOffset;
    cd.append(fileName);
    cdEntries.append(cd);
}

// --- Helper : finaliser un zip ---
static void finalizeZip(QByteArray &zipData, const QList<QByteArray> &cdEntries)
{
    quint32 cdOffset = (quint32)zipData.size();

    for (const QByteArray &cd : cdEntries)
        zipData.append(cd);

    quint32 cdSize = (quint32)zipData.size() - cdOffset;

    QByteArray eocd;
    QDataStream es(&eocd, QIODevice::WriteOnly);
    es.setByteOrder(QDataStream::LittleEndian);
    es << (quint32)0x06054b50;
    es << (quint16)0 << (quint16)0;
    es << (quint16)cdEntries.size() << (quint16)cdEntries.size();
    es << cdSize << cdOffset;
    es << (quint16)0;
    zipData.append(eocd);
}

// --- Helper : POST multipart et récupérer la réponse ---
static bool doPost(const QUrl &url,
                   QHttpMultiPart *multiPart,
                   QString &responseOut)
{
    QNetworkAccessManager manager;
    QNetworkRequest request(url);
    request.setRawHeader("User-Agent", USER_AGENT.toUtf8());
    request.setRawHeader("X-API-Token", API_TOKEN.toUtf8());

    QNetworkReply *reply = manager.post(request, multiPart);
    multiPart->setParent(reply);

    QEventLoop loop;
    QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
    loop.exec();

    bool ok = (reply->error() == QNetworkReply::NoError);
    if (ok) {
        responseOut = QString::fromUtf8(reply->readAll());
    } else {
        responseOut = "Erreur réseau : " + reply->errorString();
    }

    reply->deleteLater();
    return ok;
}

// --- Helper : GET simple et récupérer la réponse ---
static bool doGet(const QUrl &url, QString &responseOut)
{
    QNetworkAccessManager manager;
    QNetworkRequest request(url);
    request.setRawHeader("User-Agent", USER_AGENT.toUtf8());
    request.setRawHeader("X-API-Token", API_TOKEN.toUtf8());

    QNetworkReply *reply = manager.get(request);

    QEventLoop loop;
    QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
    loop.exec();

    bool ok = (reply->error() == QNetworkReply::NoError);
    if (ok) {
        responseOut = QString::fromUtf8(reply->readAll());
    } else {
        responseOut = "Erreur réseau : " + reply->errorString();
    }

    reply->deleteLater();
    return ok;
}


// ============================================================
//  1. CRÉER UN TICKET
// ============================================================
bool createTicket(const QString &licensedTo,
                  const QString &brand,
                  const QString &system,
                  const QString &otherInfo,
                  const QString &module,
                  const QString &carModel,
                  const QString &carYear,
                  const QString &gear,
                  const QString &orgFilePath,
                  const QString &modFilePath,
                  const QString &message,
                  QString &ticketId,
                  QString &response)
{
    // 1. Construire le ZIP contenant ORG + MOD (si fourni)
    QByteArray zipData;
    QList<QByteArray> cdEntries;

    // ORG file (obligatoire)
    if (!QFile::exists(orgFilePath)) {
        response = "Fichier ORG introuvable : " + orgFilePath;
        return false;
    }
    QFile orgFile(orgFilePath);
    if (!orgFile.open(QIODevice::ReadOnly)) {
        response = "Impossible d'ouvrir le fichier ORG.";
        return false;
    }
    QByteArray orgContent = orgFile.readAll();
    orgFile.close();

    QString orgName = "ORG_" + QFileInfo(orgFilePath).fileName();
    appendFileToZip(zipData, cdEntries, orgName.toUtf8(), orgContent);

    // MOD file (optionnel)
    if (!modFilePath.isEmpty() && QFile::exists(modFilePath)) {
        QFile modFile(modFilePath);
        if (modFile.open(QIODevice::ReadOnly)) {
            QByteArray modContent = modFile.readAll();
            modFile.close();
            QString modName = "MOD_" + QFileInfo(modFilePath).fileName();
            appendFileToZip(zipData, cdEntries, modName.toUtf8(), modContent);
        }
    }

    finalizeZip(zipData, cdEntries);

    // 2. Préparer le multipart
    QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);

    auto addField = [&](const QString &name, const QString &value) {
        QHttpPart part;
        part.setHeader(QNetworkRequest::ContentDispositionHeader,
                       QVariant(QString("form-data; name=\"%1\"").arg(name)));
        part.setBody(value.toUtf8());
        multiPart->append(part);
    };

    addField("action",      "create");
    addField("licensed_to", licensedTo);
    addField("brand",       brand);
    addField("system",      system);
    addField("other_info",  otherInfo);
    addField("module",      module);
    addField("car_model",   carModel);
    addField("car_year",    carYear);
    addField("gear",        gear);
    addField("message",     message);
    addField("timestamp",   QDateTime::currentDateTime().toString(Qt::ISODate));

    // ZIP
    QHttpPart zipPart;
    zipPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/zip"));
    zipPart.setHeader(QNetworkRequest::ContentDispositionHeader,
                      QVariant("form-data; name=\"archive\"; filename=\"ticket_files.zip\""));
    zipPart.setBody(zipData);
    multiPart->append(zipPart);

    // 3. Envoi
    if (!doPost(QUrl(TICKET_SERVER_URL), multiPart, response))
        return false;

    // 4. Parser la réponse pour extraire le ticket_id
    QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
    if (!doc.isObject()) {
        response = "Réponse serveur invalide.";
        return false;
    }

    QJsonObject obj = doc.object();
    if (obj.value("status").toString() != "ok") {
        response = obj.value("message").toString("Erreur inconnue.");
        return false;
    }

    ticketId = obj.value("ticket_id").toString();
    return true;
}


// ============================================================
//  2. ENVOYER UN MESSAGE DANS UN TICKET
// ============================================================
bool sendTicketMessage(const QString &ticketId,
                       const QString &message,
                       QString &response)
{
    QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);

    auto addField = [&](const QString &name, const QString &value) {
        QHttpPart part;
        part.setHeader(QNetworkRequest::ContentDispositionHeader,
                       QVariant(QString("form-data; name=\"%1\"").arg(name)));
        part.setBody(value.toUtf8());
        multiPart->append(part);
    };

    addField("action",     "message");
    addField("ticket_id",  ticketId);
    addField("message",    message);
    addField("timestamp",  QDateTime::currentDateTime().toString(Qt::ISODate));

    if (!doPost(QUrl(TICKET_SERVER_URL), multiPart, response))
        return false;

    QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
    return doc.isObject() && doc.object().value("status").toString() == "ok";
}


// ============================================================
//  3. RÉCUPÉRER LES MESSAGES D'UN TICKET
// ============================================================
bool fetchTicketMessages(const QString &ticketId,
                         QJsonArray &messages,
                         QString &status,
                         QString &response)
{
    QUrl url(TICKET_SERVER_URL);
    QUrlQuery query;
    query.addQueryItem("action",    "fetch");
    query.addQueryItem("ticket_id", ticketId);
    url.setQuery(query);

    if (!doGet(url, response))
        return false;

    QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
    if (!doc.isObject()) return false;

    QJsonObject obj = doc.object();
    if (obj.value("status").toString() != "ok") return false;

    messages = obj.value("messages").toArray();
    status   = obj.value("ticket_status").toString("OffLine");
    return true;
}


// ============================================================
//  4. TÉLÉCHARGER UN FICHIER SOLUTION
// ============================================================
bool downloadSolutionFile(const QString &fileUrl,
                          const QString &savePath,
                          QString &response)
{
    QNetworkAccessManager manager;
    QNetworkRequest request((QUrl(fileUrl)));
    request.setRawHeader("User-Agent", USER_AGENT.toUtf8());
    request.setRawHeader("X-API-Token", API_TOKEN.toUtf8());

    QNetworkReply *reply = manager.get(request);

    QEventLoop loop;
    QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
    loop.exec();

    if (reply->error() != QNetworkReply::NoError) {
        response = "Erreur téléchargement : " + reply->errorString();
        reply->deleteLater();
        return false;
    }

    QByteArray data = reply->readAll();
    reply->deleteLater();

    QFile out(savePath);
    if (!out.open(QIODevice::WriteOnly)) {
        response = "Impossible d'écrire le fichier : " + savePath;
        return false;
    }
    out.write(data);
    out.close();

    response = QString("Fichier enregistré : %1 (%2 octets)").arg(savePath).arg(data.size());
    return true;
}


// ============================================================
//  5. LISTER MES TICKETS
// ============================================================
bool listMyTickets(QJsonArray &tickets, QString &response)
{
    QUrl url(TICKET_SERVER_URL);
    QUrlQuery query;
    query.addQueryItem("action", "list");
    url.setQuery(query);

    if (!doGet(url, response))
        return false;

    QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
    if (!doc.isObject()) return false;

    QJsonObject obj = doc.object();
    if (obj.value("status").toString() != "ok") return false;

    tickets = obj.value("tickets").toArray();
    return true;
}


// ============================================================
//  EXEMPLE D'UTILISATION DANS TON UI
// ============================================================
//
// --- Création du ticket (bouton CREAT) ---
//
// void MainWindow::onCreateTicketClicked()
// {
//     QString ticketId, resp;
//     bool ok = createTicket(
//         "Zizi Aksel",             // licensed to
//         ui->brandCombo->currentText(),
//         ui->systemCombo->currentText(),
//         ui->otherEdit->text(),
//         ui->moduleCombo->currentText(),
//         ui->carModelEdit->text(),
//         ui->carYearCombo->currentText(),
//         ui->gearCombo->currentText(),
//         m_orgFilePath,   // sélectionné via le bouton ORG file
//         m_modFilePath,   // sélectionné via le bouton Mod file
//         ui->messageEdit->toPlainText(),
//         ticketId,
//         resp
//     );
//
//     if (ok) {
//         m_currentTicketId = ticketId;
//         ui->ticketNumberCombo->addItem(ticketId);
//         QMessageBox::information(this, "Ticket créé",
//             "Ticket " + ticketId + " créé avec succès.");
//
//         // Démarrer le refresh auto du chat
//         m_chatTimer->start(5000);   // toutes les 5s
//     } else {
//         QMessageBox::critical(this, "Erreur", resp);
//     }
// }
//
// --- Timer qui rafraîchit le chat ---
//
// void MainWindow::onChatTimerTick()
// {
//     if (m_currentTicketId.isEmpty()) return;
//
//     QJsonArray messages;
//     QString status, resp;
//     if (fetchTicketMessages(m_currentTicketId, messages, status, resp)) {
//         // Mettre à jour le label "Support Status"
//         ui->statusLabel->setText(status);
//         ui->statusLabel->setStyleSheet(
//             status == "Online" ? "color: green;" : "color: red;");
//
//         // Afficher les messages dans le chat
//         ui->chatArea->clear();
//         for (const QJsonValue &v : messages) {
//             QJsonObject msg = v.toObject();
//             QString from = msg.value("from").toString();
//             QString text = msg.value("text").toString();
//             QString ts   = msg.value("timestamp").toString();
//             QString fileUrl = msg.value("file_url").toString();
//
//             QString line = QString("[%1] %2 : %3")
//                 .arg(ts).arg(from).arg(text);
//             ui->chatArea->append(line);
//
//             // Si un fichier solution est joint
//             if (!fileUrl.isEmpty()) {
//                 ui->chatArea->append(
//                     "  → Fichier solution disponible : " + fileUrl);
//             }
//         }
//     }
// }
