#include "InteractiveCAN.h"
#include "InteractiveData.h"
#include "ReturnKeyLockManager.h"
#include "../usr/config.h"
#include "../httpclient/WorkNodeFormModel.h"
#include <QDebug>

// ============ 临时禁用CAN通讯日志 (可撤回) ============
//#define CAN_LOG_DISABLED
//#ifdef CAN_LOG_DISABLED
//#undef qDebug
//#undef qInfo
//#undef qWarning
//#undef qCritical
//#define qDebug() QNoDebug()
//#define qInfo() QNoDebug()
//#define qWarning() QNoDebug()
//#define qCritical() QNoDebug()
//#endif
// ====================================================

InteractiveCAN* InteractiveCAN::pInstance = nullptr;

QList<int> stringToIntList(const QString &str)
{
    QList<int> resultList;

    // 1. 清理字符串：去掉首尾的中括号 []
    QString cleanStr = str.trimmed(); // 先去除首尾空格（兼容" [105,104] "的情况）
    if (cleanStr.isEmpty() || !cleanStr.startsWith('[') || !cleanStr.endsWith(']')) {
        qWarning() << "字符串格式错误，非[num1,num2]格式：" << str;
        return resultList; // 格式错误返回空列表
    }
    // 截取中括号内的内容（去掉第0位的[和最后1位的]）
    cleanStr = cleanStr.mid(1, cleanStr.length() - 2);

    // 2. 按逗号分割字符串，得到单个数字的字符串列表
    QStringList numStrList = cleanStr.split(',');

    // 3. 遍历分割后的字符串，转换为int并添加到列表
    for (const QString &numStr : numStrList) {
        QString trimmedNumStr = numStr.trimmed(); // 去除数字前后的空格（兼容"105, 104"）
        bool ok = false;
        int num = trimmedNumStr.toInt(&ok); // 转换并检查是否成功
        if (ok) {
            resultList.append(num);
        } else {
            qWarning() << "数字转换失败，非整数：" << trimmedNumStr;
        }
    }

    return resultList;
}

InteractiveCAN::InteractiveCAN()
{
    m_canClient = new CANClient(this);

    m_canClient->start();

    connect(m_canClient, &CANClient::keyBaseChargeControlWritten, this, &InteractiveCAN::slotUnlockEKey);
    connect(m_canClient, &CANClient::lockBuckleWritten, this, &InteractiveCAN::slotUnlockLocks);
    connect(m_canClient, &CANClient::signalReturnKey, this, &InteractiveCAN::slotGetReturnCANDevices);
    connect(m_canClient, &CANClient::signalLockNfcDeviceDetected, this, &InteractiveCAN::slotDeviceDetected, Qt::QueuedConnection);
    connect(m_canClient, &CANClient::signalPopDevices, this, &InteractiveCAN::slotGetPopedDevices);

    m_bleClient = new BLEClient(this);
//    m_bleClient->start();

    m_httpClient = new HttpClient(this);
    connect(this, &InteractiveCAN::signalGetRequestData, m_httpClient, &HttpClient::slotGetRequestData, Qt::QueuedConnection);
    connect(m_httpClient, &HttpClient::signalResponseGetKeyMAC, this, &InteractiveCAN::slotHttpResponseGetKeyMAC);

    connect(this, &InteractiveCAN::signalGetIsolationPointInfo, m_httpClient, &HttpClient::slotGetRequestData);
    connect(m_httpClient, &HttpClient::signalResponseGetIsolationPointInfo, this, &InteractiveCAN::slotHttpResponseGetIsolationPointInfo);

    connect(this, &InteractiveCAN::signalPostRequestData, m_httpClient, &HttpClient::slotPostRequestData, Qt::QueuedConnection);
    connect(m_httpClient, &HttpClient::signalResponseUploadJobTicket, this, &InteractiveCAN::slotHttpResponseUploadJobTicket);
    connect(m_httpClient, &HttpClient::signalResponseUpdateWorkTicket, this, &InteractiveCAN::slotHttpResponseUpdateWorkTicket);
    connect(m_httpClient, &HttpClient::signalResponseUploadPositionInfo, this, &InteractiveCAN::slotHttpResponseUploadPositionInfo);
    connect(m_httpClient, &HttpClient::signalResponseUpdateBackLock, this, &InteractiveCAN::slotHttpResponseUpdateBackLock);
    connect(m_httpClient, &HttpClient::signalResponseGetWorkTicketByNodeId, this, &InteractiveCAN::slotHttpResponseGetWorkTicketByNodeId);
    connect(m_httpClient, &HttpClient::signalResponseUpdateKeyBack, this, &InteractiveCAN::slotHttpResponseUpdateKeyBack);

    m_bleConnectTimer = new QTimer(this);
    connect(m_bleConnectTimer, &QTimer::timeout, this, &InteractiveCAN::slotSearchBLE);
    connect(this, &InteractiveCAN::signalOkSearchedBLE, this, &InteractiveCAN::slotOkSearchedBLE);

    connect(m_bleClient, &BLEClient::tokenReceived, this, [this](const BLEToken& token) {
        if (token.success && m_bleConnectTimer) {
            if (m_startGetWorkTicketResultFlag) {
                m_bleClient->startGetWorkTicketResult();
            }
            m_okOpenKey = true;
            m_finishedFlags = false;    // 表示新的一轮开始了
            m_startGetWorkTicketResultFlag = false;
            m_okSendJobTicket = false;
            m_checkedFlags = true;
            m_searchedBLE = false;
            m_bleConnectTimer->stop();
            if (WorkNodeFormModel::instance()->modelType() == QString("isolation")) {
                createJobTicket();
            }
            else if (WorkNodeFormModel::instance()->modelType() == QString("releaseIsolation")) {
                createColockJobTicket();
            }
            else {
                // 归还设备
                checkEKeyStatus();
            }
        }
    });
    connect(m_bleClient, &BLEClient::signalSendJobTicketStatus, this, &InteractiveCAN::slotSendJobTicketStatus);
    connect(m_bleClient, &BLEClient::workTicketResultReceived, this, &InteractiveCAN::slotReceivedJobTicketResult, Qt::QueuedConnection);
    connect(m_bleClient, &BLEClient::signalFailConnect, this, &InteractiveCAN::slotReConnectBLE);

    m_checkHasKeyTimer = new QTimer(this);
    connect(m_checkHasKeyTimer, &QTimer::timeout, this, &InteractiveCAN::checkEKeyStatus);
    connect(this, &InteractiveCAN::signalCheckedKey, this, &InteractiveCAN::getJobTicketInfo);

    connect(this, &InteractiveCAN::signalUpdateColockStatus, this, &InteractiveCAN::slotUpdateColockStatus);
    
    // ========== 连接CAN信号到归还钥匙和锁管理器 ==========
    // 必须使用 Qt::QueuedConnection 因为CAN回调在子线程中
    connect(m_canClient, &CANClient::nfcDeviceDetected, 
            ReturnKeyLockManager::instance(), &ReturnKeyLockManager::onNfcDetected,
            Qt::QueuedConnection);
    connect(m_canClient, &CANClient::nfcDeviceError, 
            ReturnKeyLockManager::instance(), &ReturnKeyLockManager::onDeviceError,
            Qt::QueuedConnection);
    connect(this, &InteractiveCAN::signalJobTicketInfo,
            ReturnKeyLockManager::instance(), &ReturnKeyLockManager::slotGetJobTicketResult,
            Qt::QueuedConnection);
    connect(m_bleClient, &BLEClient::workTicketResultReceived,
            ReturnKeyLockManager::instance(), &ReturnKeyLockManager::slotReceivedJobTicket,
            Qt::QueuedConnection);
    connect(ReturnKeyLockManager::instance(), &ReturnKeyLockManager::isProcessingChanged,
            this, &InteractiveCAN::slotReturnKeyAndLockProcessingChanged);
    // ====================================================
}

void InteractiveCAN::createJobTicket()
{
    QString isolationPoints = WorkNodeFormModel::instance()->isolationPoints();
    if (isolationPoints.isEmpty()) return;

    m_isolationPoints = stringToIntList(isolationPoints);

    // 获取隔离点信息
    for (int i = 0; i < m_isolationPoints.length(); i++) {
        QJsonObject jsonRoot;
        QDateTime currentDateTime = QDateTime::currentDateTime();
        qint64 timestampSeconds = currentDateTime.toMSecsSinceEpoch();

        QString url = Config()->isolationPointById;
        jsonRoot.insert("id", m_isolationPoints[i]);

        QJsonDocument jsonDoc(jsonRoot);
        QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Compact);

        emit signalGetRequestData(timestampSeconds, url, jsonData, GetInteractiveData()->m_token);
    }

}

void InteractiveCAN::createColockJobTicket()
{
    QJsonArray pointsList = WorkNodeFormModel::instance()->pointsList();
    qDebug() << "[createColockJobTicket]: " << pointsList;
    if (pointsList.isEmpty()) return;

    m_pointAndLockRfids.clear();
    m_lockRFIDs.clear();
    m_okOpenKey = true;

    for (int i = 0; i < pointsList.count(); i++) {
        QJsonObject pointItem = pointsList[i].toObject();

        if (pointItem.contains("lockNfc") && pointItem.contains("pointNfc")) {
            m_pointAndLockRfids.insert(pointItem.value("pointNfc").toString(), pointItem.value("lockNfc").toString());
            m_lockRFIDs.insert(i, pointItem.value("lockNfc").toString());
        }
    }
    qDebug() << "[createColockJobTicket]: " << m_pointAndLockRfids;
    createJobTicket();
}

void InteractiveCAN::httpRequestPostUploadJobTicket()
{
    QJsonObject jsonRoot;
    QDateTime currentDateTime = QDateTime::currentDateTime();
    qint64 timestampSeconds = currentDateTime.toMSecsSinceEpoch();

    QString url = Config()->uploadJobTicketUrl;

    QJsonArray keyNfcListArray;
    QJsonObject keyNfcItem;

    keyNfcItem.insert("keyNfc", m_keyNFC);
    keyNfcItem.insert("hardwareCode", Config()->m_systemMACAddr);

    keyNfcListArray.append(keyNfcItem);

    QJsonArray lockNfcListArray;
    for (auto iter = m_lockRFIDs.begin(); iter != m_lockRFIDs.end(); ++iter) {
        QJsonObject lockNfcItem;
        lockNfcItem.insert("lockNfc", iter.value());
        lockNfcItem.insert("hardwareCode", Config()->m_systemMACAddr);
        lockNfcListArray.append(lockNfcItem);
    }

    jsonRoot.insert("nodeId", m_taskCode);
    jsonRoot.insert("ticketContent", QString::fromUtf8(m_ticketDoc.toJson(QJsonDocument::Compact)));
    jsonRoot.insert("keyNfcList", keyNfcListArray);
    jsonRoot.insert("lockNfcList", lockNfcListArray);

    qDebug() << "[httpRequestPostUploadJobTicket]: " << url << jsonRoot;

    QJsonDocument jsonDoc(jsonRoot);
    QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Compact);

    emit signalPostRequestData(timestampSeconds, url, jsonData, NULL, GetInteractiveData()->m_token);
}

void InteractiveCAN::httpRequestPostUpdateWorkTicket()
{
    QJsonObject jsonRoot;
    QDateTime currentDateTime = QDateTime::currentDateTime();
    qint64 timestampSeconds = currentDateTime.toMSecsSinceEpoch();

    QString url = Config()->updateWorkTicketUrl;

    QJsonArray keyNfcListArray;
    QJsonObject keyNfcItem;

    keyNfcItem.insert("keyNfc", m_keyNFC);
    keyNfcItem.insert("hardwareCode", Config()->m_systemMACAddr);

    keyNfcListArray.append(keyNfcItem);

    QJsonArray lockNfcListArray;
    for (auto iter = m_lockRFIDs.begin(); iter != m_lockRFIDs.end(); ++iter) {
        QJsonObject lockNfcItem;
        lockNfcItem.insert("lockNfc", iter.value());
        lockNfcItem.insert("hardwareCode", Config()->m_systemMACAddr);
        lockNfcListArray.append(lockNfcItem);
    }

    jsonRoot.insert("nodeId", m_taskCode);
    jsonRoot.insert("ticketContent", QString::fromUtf8(m_ticketDoc.toJson(QJsonDocument::Compact)));
    jsonRoot.insert("keyNfcList", keyNfcListArray);
    jsonRoot.insert("lockNfcList", lockNfcListArray);

    qDebug() << "[httpRequestPostUpdateWorkTicket]: " << url << jsonRoot;

    QJsonDocument jsonDoc(jsonRoot);
    QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Compact);

    emit signalPostRequestData(timestampSeconds, url, jsonData, NULL, GetInteractiveData()->m_token);
}

void InteractiveCAN::httpRequestPostUploadPositionInfo(const QJsonDocument &doc)
{
    if (doc.isNull() || doc.isEmpty()) return;
    QJsonObject rootObj = doc.object();
    if (!rootObj.contains("data"))  return;

    QJsonArray pointsInfo;
    int nodeId = WorkNodeFormModel::instance()->workId();

    QJsonArray dataObj = rootObj.value("data").toArray();
    for (int i = 0; i < dataObj.count(); i++) {
        QJsonObject dataItem = dataObj[i].toObject();
        if (dataItem.isEmpty()) continue;
        if (!dataItem.contains("dataList")) continue;

        QJsonArray dataListArr = dataItem.value("dataList").toArray();
        if (dataListArr.isEmpty()) continue;

        for (int j = 0; j < dataListArr.count(); j++) {
            QJsonObject taskItem = dataListArr[j].toObject();

            QString pointNfc, lockNfc;
            if (taskItem.contains("equipRfidNo")) {
                pointNfc = taskItem.value("equipRfidNo").toString();
            }
            if (taskItem.contains("infoRfidNo")) {
                lockNfc = taskItem.value("infoRfidNo").toString();
            }

            QJsonObject pointInfo;
            pointInfo.insert("nodeId", nodeId);
            pointInfo.insert("pointNfc", pointNfc);
            pointInfo.insert("lockNfc", lockNfc);

            pointsInfo.append(pointInfo);
        }
    }

    QJsonObject jsonRoot;
    QDateTime currentDateTime = QDateTime::currentDateTime();
    qint64 timestampSeconds = currentDateTime.toMSecsSinceEpoch();

    QString url = Config()->uploadPositionInfoUrl;
    qDebug() << "[httpRequestPostUploadPositionInfo]" << pointsInfo << url;

    jsonRoot.insert("keyNfc", m_keyNFC);
    jsonRoot.insert("hardwareCode", Config()->m_systemMACAddr);
    jsonRoot.insert("list", pointsInfo);

    QJsonDocument jsonDoc(jsonRoot);
    QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Compact);

    emit signalPostRequestData(timestampSeconds, url, jsonData, NULL, GetInteractiveData()->m_token);
}

void InteractiveCAN::httpRequestPostUpdatePointUnlock(const QJsonDocument &doc)
{
    if (doc.isNull() || doc.isEmpty()) return;
    QJsonObject rootObj = doc.object();
    if (!rootObj.contains("data"))  return;

    QJsonObject jsonRoot;
    QJsonArray pointsInfo;

    int nodeId = WorkNodeFormModel::instance()->workId();

    QJsonArray dataObj = rootObj.value("data").toArray();
    for (int i = 0; i < dataObj.count(); i++) {
        QJsonObject dataItem = dataObj[i].toObject();
        if (dataItem.isEmpty()) continue;
        if (!dataItem.contains("dataList")) continue;

        QJsonArray dataListArr = dataItem.value("dataList").toArray();
        if (dataListArr.isEmpty()) continue;

        for (int j = 0; j < dataListArr.count(); j++) {
            QJsonObject taskItem = dataListArr[j].toObject();

            QString pointNfc, lockNfc;
            if (taskItem.contains("equipRfidNo")) {
                pointNfc = taskItem.value("equipRfidNo").toString();
            }
            if (taskItem.contains("infoRfidNo")) {
                lockNfc = taskItem.value("infoRfidNo").toString();
            }

            QJsonObject pointInfo;
            pointInfo.insert("nodeId", nodeId);
            pointInfo.insert("pointNfc", pointNfc);
            pointInfo.insert("lockNfc", lockNfc);

            pointsInfo.append(pointInfo);
        }
    }

    QDateTime currentDateTime = QDateTime::currentDateTime();
    qint64 timestampSeconds = currentDateTime.toMSecsSinceEpoch();

    QString url = Config()->updatePointUnlock;

    jsonRoot.insert("keyNfc", m_keyNFC);
    jsonRoot.insert("hardwareCode", Config()->m_systemMACAddr);
    jsonRoot.insert("list", pointsInfo);

    QJsonDocument jsonDoc(jsonRoot);
    QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Compact);
    qDebug() << jsonDoc;

    emit signalPostRequestData(timestampSeconds, url, jsonData, NULL, GetInteractiveData()->m_token);
}

void InteractiveCAN::httpRequestPostUpdateBackLock(const QList<QString> &lockNfcs)
{
    QJsonArray locksInfo;

    int nodeId = WorkNodeFormModel::instance()->workId();

    for (int i = 0; i < lockNfcs.count(); i++) {
        QJsonObject infoItem;
        infoItem.insert("nodeId", nodeId);
        infoItem.insert("lockNfc", lockNfcs[i]);
        infoItem.insert("hardwareCode", Config()->m_systemMACAddr);

        locksInfo.append(infoItem);
    }

    QDateTime currentDateTime = QDateTime::currentDateTime();
    qint64 timestampSeconds = currentDateTime.toMSecsSinceEpoch();

    QString url = Config()->updateBackLock;

    QJsonDocument jsonDoc(locksInfo);
    QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Compact);
    qDebug() << "[httpRequestPostUpdateBackLock] " << jsonDoc;

    emit signalPostRequestData(timestampSeconds, url, jsonData, NULL, GetInteractiveData()->m_token);
}

void InteractiveCAN::httpRequestPostUpdateKeyBack(const QString &keyNfc, int target, const QJsonArray& lockList)
{
    QJsonObject jsonRoot;

    QDateTime currentDateTime = QDateTime::currentDateTime();
    qint64 timestampSeconds = currentDateTime.toMSecsSinceEpoch();

    QString url = Config()->updateKeyBack;

    jsonRoot.insert("target", target);
    jsonRoot.insert("keyNfc", keyNfc);
    jsonRoot.insert("hardwareCode", Config()->m_systemMACAddr);
    jsonRoot.insert("list", lockList);

    QJsonDocument jsonDoc(jsonRoot);
    QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Compact);
    qDebug() << jsonDoc;

    emit signalPostRequestData(timestampSeconds, url, jsonData, NULL, GetInteractiveData()->m_token);
}

void InteractiveCAN::httpRequestGetWorkTicketByNodeId()
{
    QJsonObject jsonRoot;
    QDateTime currentDateTime = QDateTime::currentDateTime();
    qint64 timestampSeconds = currentDateTime.toMSecsSinceEpoch();

    QString url = Config()->workTicketByNodeId;
    jsonRoot.insert("nodeId", m_taskCode);

    QJsonDocument jsonDoc(jsonRoot);
    QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Compact);
    qDebug() << "[httpRequestGetWorkTicketByNodeId] " << url << m_taskCode;

    emit signalGetRequestData(timestampSeconds, url, jsonData, GetInteractiveData()->m_token);
}

void InteractiveCAN::checkEKeyStatus()
{
    // TODO: 暂时考虑只有两把钥匙的情况
    QMap<quint8, CANKeyRFIDStatus> keyRFIDs = m_canClient->keyRFIDStatus();
    quint8 nodeId = keyRFIDs.keys().first();
    CANKeyRFIDStatus keyRFID = keyRFIDs[nodeId];

    if (keyRFID.leftKeyRFID == m_keyNFC) {
        QMap<quint8, CANKeyBaseStatus> keyBaseStatus = m_canClient->keyBaseStatus();
        if (keyBaseStatus.find(nodeId) == keyBaseStatus.end()) {
            return;
        }
        bool hasLeftKey = keyBaseStatus[nodeId].leftHasKey;

        // 钥匙被放回，开始读取作业票详情， 后续可能是从后台获取
        if (hasLeftKey) {
            qDebug() << "[checkEKeyStatus]: start checking key";
            m_checkHasKeyTimer->stop();
            emit signalCheckedKey(nodeId, true);
        }
    }
    else if (keyRFID.rightKeyRFID == m_keyNFC) {
        QMap<quint8, CANKeyBaseStatus> keyBaseStatus = m_canClient->keyBaseStatus();
        if (keyBaseStatus.find(nodeId) == keyBaseStatus.end()) {
            return;
        }
        bool hasRightKey = keyBaseStatus[nodeId].rightHasKey;
        if (hasRightKey) {
            m_checkHasKeyTimer->stop();
            emit signalCheckedKey(nodeId, false);
        }
    }
}

void InteractiveCAN::getJobTicketInfo(quint8 nodeId, bool isLeftKey)
{
    qDebug() << "[getJobTicketInfo]: " << m_checkedFlags;
    if (m_checkedFlags) {
        m_checkedFlags = false;
        m_canClient->lockingEKey(nodeId, isLeftKey, !isLeftKey);

//        m_bleClient->startGetWorkTicketResult();
        if (m_bleClient) {
            qDebug() << "[getJobTicketInfo]: " << m_bleClient->isConnected();
            if (m_bleClient->isConnected()) {
                m_bleClient->startGetWorkTicketResult();
            }
            else {
                m_startGetWorkTicketResultFlag = true;
                m_bleClient->apiConnectDevice(m_currentKeyMAC);
            }
        }
    }
}

void InteractiveCAN::slotHttpResponseGetKeyMAC(QByteArray data)
{
    QJsonParseError error;
    QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
    qDebug() << "[slotHttpResponseGetKeyMAC]: " << jsonDoc;
    if (error.error != QJsonParseError::NoError) {
        return;
    }
    if (jsonDoc.isNull() || jsonDoc.isEmpty()) {
        return;
    }

    QJsonObject rootObj = jsonDoc.object();
    if(rootObj.contains("code")) {
        int codeValue = rootObj.value("code").toInt();
        if (codeValue == 408) {
            GetInteractiveData()->notifyRequestTimedOut();
            return;
        }
        if(codeValue == 200 || codeValue == 0) {
            if(rootObj.contains("data")) {
                QJsonObject vDataObj = rootObj.value("data").toObject();
                if (vDataObj.contains("macAddress")) {
                    m_currentKeyMAC = vDataObj.value("macAddress").toString();
                    m_bleClient->m_selectedMAC = m_currentKeyMAC;
                    m_searchedBLE = false;
                    m_bleConnectTimer->start(1000);
                }
            }
        }
    }
}

void InteractiveCAN::slotHttpResponseGetIsolationPointInfo(QByteArray data)
{
    QJsonParseError error;
    QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);

    if (error.error != QJsonParseError::NoError) {
        return;
    }
    if (jsonDoc.isNull() || jsonDoc.isEmpty()) {
        return;
    }
    else {
        qDebug() << "[InteractiveCAN::slotHttpResponseGetIsolationPointInfo]: " << jsonDoc;
    }

    QJsonObject rootObj = jsonDoc.object();
    if(rootObj.contains("code")) {
        int codeValue = rootObj.value("code").toInt();
        if (codeValue == 408) {
            GetInteractiveData()->notifyRequestTimedOut();
            return;
        }
        if(codeValue == 200 || codeValue == 0) {
            if(rootObj.contains("data")) {
                IsolationPointInfo info;
                QJsonObject vDataObj = rootObj.value("data").toObject();
                if (vDataObj.contains("id") && vDataObj.value("id").isDouble()) {
                    info.dataId = vDataObj.value("id").toInt();
                }
                if (vDataObj.contains("pointName")) {
                    info.equipName = vDataObj.value("pointName").toString();
                }
                if (vDataObj.contains("pointNfc")) {
                    info.equipRfidNo = vDataObj.value("pointNfc").toString();
                }

                if (WorkNodeFormModel::instance()->modelType() == QString("isolation")) {
                    m_pointInfo.append(info);
                    if (m_pointInfo.length() == m_isolationPoints.length()) {
                        getLockRFIDs(m_lockRFIDs);      // 获取锁的信息
                    }
                }
                else {
                    info.target = 1;
                    if (m_pointAndLockRfids.find(info.equipRfidNo) != m_pointAndLockRfids.end()) {
                        info.infoRfidNo = m_pointAndLockRfids[info.equipRfidNo];
                    }
                     m_pointInfo.append(info);
                     qDebug() << "[slotHttpResponseGetIsolationPointInfo] " << m_pointInfo.length() << m_isolationPoints.length();
                     if (m_pointInfo.length() == m_isolationPoints.length()) {
                         if (m_okOpenKey) {
                             sendJobTicket();
                         }
                     }
                }
            }
        }
    }
}

void InteractiveCAN::slotHttpResponseUploadJobTicket(QByteArray data)
{
    QJsonParseError error;
    QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
    qDebug() << "[slotHttpResponseUploadJobTicket]" << jsonDoc;
    if (error.error != QJsonParseError::NoError) {
        return;
    }

    if (jsonDoc.isNull() || jsonDoc.isEmpty()) {
        return;
    }
    ;
    QJsonObject rootObj = jsonDoc.object();
    if(rootObj.contains("code")) {
        int codeValue = rootObj.value("code").toInt();
        if (codeValue == 408) {
            GetInteractiveData()->notifyRequestTimedOut();
            return;
        }
        if(codeValue == 200 || codeValue == 0) {
//            m_okUnlockKey = true;
//            qDebug() << "[slotHttpResponseUploadJobTicket]" << jsonDoc;
            if (m_bleClient) {
//                m_bleClient->disconnectDevice();
            }
        }
    }
}

void InteractiveCAN::slotHttpResponseUpdateWorkTicket(QByteArray data)
{
    QJsonParseError error;
    QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
    qDebug() << "[slotHttpResponseUpdateWorkTicket]" << jsonDoc;
    if (error.error != QJsonParseError::NoError) {
        return;
    }

    if (jsonDoc.isNull() || jsonDoc.isEmpty()) {
        return;
    }
    ;
    QJsonObject rootObj = jsonDoc.object();
    if(rootObj.contains("code")) {
        int codeValue = rootObj.value("code").toInt();
        if(codeValue == 200 || codeValue == 0) {

        }
    }
}

void InteractiveCAN::slotHttpResponseUploadPositionInfo(QByteArray data)
{
    QJsonParseError error;
    QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
    if (error.error != QJsonParseError::NoError) {
        return;
    }
    qDebug() << "[slotHttpResponseUploadPositionInfo]" << jsonDoc;
    if (jsonDoc.isNull() || jsonDoc.isEmpty()) {
        return;
    }
    ;
    QJsonObject rootObj = jsonDoc.object();
    if(rootObj.contains("code")) {
        int codeValue = rootObj.value("code").toInt();
        if (codeValue == 408) {
            GetInteractiveData()->notifyRequestTimedOut();
            return;
        }
        if(codeValue == 200 || codeValue == 0) {
//            qDebug() << "[slotHttpResponseUploadPositionInfo]" << jsonDoc;
        }
    }
}

void InteractiveCAN::slotHttpResponseUpdatePointUnlock(QByteArray data)
{
    QJsonParseError error;
    QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
    if (error.error != QJsonParseError::NoError) {
        return;
    }
    qDebug() << "[slotHttpResponseUpdatePointUnlock]" << jsonDoc;
    if (jsonDoc.isNull() || jsonDoc.isEmpty()) {
        return;
    }
    ;
    QJsonObject rootObj = jsonDoc.object();
    if(rootObj.contains("code")) {
        int codeValue = rootObj.value("code").toInt();
        if (codeValue == 408) {
            GetInteractiveData()->notifyRequestTimedOut();
            return;
        }
        if(codeValue == 200 || codeValue == 0) {
//            qDebug() << "[slotHttpResponseUpdatePointUnlock]" << jsonDoc;
        }
    }
}

void InteractiveCAN::slotHttpResponseUpdateBackLock(QByteArray data)
{
    QJsonParseError error;
    QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
    if (error.error != QJsonParseError::NoError) {
        return;
    }
    qDebug() << "[slotHttpResponseUpdateBackLock]" << jsonDoc;
    if (jsonDoc.isNull() || jsonDoc.isEmpty()) {
        return;
    }
    ;
    QJsonObject rootObj = jsonDoc.object();
    if(rootObj.contains("code")) {
        int codeValue = rootObj.value("code").toInt();
        if (codeValue == 408) {
            GetInteractiveData()->notifyRequestTimedOut();
            return;
        }
        if(codeValue == 200 || codeValue == 0) {
            m_getLockedRfidFlag = false;
        }
    }
}

void InteractiveCAN::slotHttpResponseGetWorkTicketByNodeId(QByteArray data)
{
    QJsonParseError error;
    QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
    qDebug() << "[slotHttpResponseGetWorkTicketByNodeId]" << jsonDoc;
    if (error.error != QJsonParseError::NoError) {
        emit signalJobTicketInfo(-3, QJsonObject());
        return;
    }
    if (jsonDoc.isNull() || jsonDoc.isEmpty()) {
        emit signalJobTicketInfo(-3, QJsonObject());
        return;
    }

    QJsonObject rootObj = jsonDoc.object();
    if(rootObj.contains("code")) {
        int codeValue = rootObj.value("code").toInt();
        if (codeValue == 408) {
            GetInteractiveData()->notifyRequestTimedOut();
            return;
        }
        if(codeValue == 200 || codeValue == 0) {
            if(rootObj.contains("data")) {
                QJsonObject vDataObj = rootObj.value("data").toObject();
                if (vDataObj.isEmpty()) {
                    emit signalJobTicketInfo(-3, QJsonObject());
                    return;
                }
                emit signalJobTicketInfo(0, vDataObj);
                if (vDataObj.contains("ticketContent")) {
                    QVariantMap pointInfo;

                    for (auto iter = m_pointInfo.begin(); iter != m_pointInfo.end(); ++iter) {
                        pointInfo[QString::number(iter->dataId)] = iter->equipName;
                    }
                    emit signalJobTicketResult(vDataObj.value("ticketContent").toString(), pointInfo);
                }
                return;
            }
        }
    }
    emit signalJobTicketInfo(-3, QJsonObject());
}

void InteractiveCAN::slotHttpResponseUpdateKeyBack(QByteArray data)
{
    QJsonParseError error;
    QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
    qDebug() << "[slotHttpResponseUpdateKeyBack]" << jsonDoc;
    if (error.error != QJsonParseError::NoError) {
        emit signalJobTicketInfo(-3, QJsonObject());
        return;
    }
    if (jsonDoc.isNull() || jsonDoc.isEmpty()) {
        emit signalJobTicketInfo(-3, QJsonObject());
        return;
    }

    QJsonObject rootObj = jsonDoc.object();
    if(rootObj.contains("code")) {
        int codeValue = rootObj.value("code").toInt();
        if(codeValue == 200 || codeValue == 0) {
            InteractiveCAN::instance()->httpRequestPostUpdateWorkTicket();
        }
    }
}

void InteractiveCAN::slotUpdateColockStatus(const QString& cardNfc)
{
    // TODO: 更新共锁人状态
    QJsonObject jsonRoot;
    QDateTime currentDateTime = QDateTime::currentDateTime();
    qint64 timestampSeconds = currentDateTime.toMSecsSinceEpoch();

    QString url;

    QString modelType = WorkNodeFormModel::instance()->modelType();
    if (modelType == QString("isolation")) {
        url = Config()->updateColockUrl;
    }
    else if (modelType == QString("releaseIsolation")) {
        url = Config()->updateUncolockUrl;
    }
    qDebug() << "[slotUpdateColockStatus]: " << url << modelType;

    jsonRoot.insert("nodeId", m_taskCode);
    jsonRoot.insert("cardNfc", cardNfc);

    QJsonDocument jsonDoc(jsonRoot);
    QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Compact);
    if (!url.isEmpty()) {
        emit signalPostRequestData(timestampSeconds, url, jsonData, NULL, GetInteractiveData()->m_token);
    }
}

void InteractiveCAN::slotGetReturnCANDevices()
{
    // 传入qml端的消息JSON结构:
    /*
     * {
     *      "tasks": [
     *          {
     *              "key": {"status": "", "nfc": ""},
     *              "workName": "",
     *              "workId": "",
     *              "username": "",
     *              "locks": [{"status": "", "nfc": ""}]
     *          },
     *      ],
     *      "errorDevices": [{"status": "", "nfc": ""}],
     *      "noTaskDevices": [{"status": "", "nfc": ""}]
     * }
    */
    QMap<QString, quint8> keyInfo = CANClient::returnDevicesInfo.keysMap;
    QMap<QString, quint8> lockInfo = CANClient::returnDevicesInfo.locksMap;

    QJsonArray taskArray;

    for (auto keyIter = keyInfo.begin(); keyIter != keyInfo.end(); ++keyIter) {
        // TODO: 根据钥匙NFC获取当前作业票
    }

    QJsonArray errDevicesArr;
    QJsonArray noTaskDevicesArr;
    for (auto lockIter = lockInfo.begin(); lockIter != lockInfo.end(); ++lockIter) {
        // TODO: 如果锁存在某个任务中，但此时没有钥匙放入，进入异常；否则无关联任务
    }

    QJsonObject rootObject;
    rootObject.insert("tasks", taskArray);
    rootObject.insert("errorDevices", errDevicesArr);
    rootObject.insert("noTaskDevices", noTaskDevicesArr);

    QJsonDocument doc(rootObject);
    QByteArray info = doc.toJson(QJsonDocument::Compact);

    emit signalReturnDevicesInfo(info);
}

void InteractiveCAN::slotReConnectBLE()
{
    if (m_bleClient) {
        m_bleClient->apiConnectDevice(m_currentKeyMAC);
    }
}

void InteractiveCAN::slotGetPopedDevices(const QString &nfcId, const QString &deviceType, int slotIndex)
{
    if (deviceType == "lock") {

    }
    else if (deviceType == "key") {
        m_okUnlockKey = true;
    }
    qDebug() << "[slotGetPopedDevices] " << m_popedDevices.locksNfc;
    qDebug() << "[slotGetPopedDevices] " << m_popedDevices.keyNfc;
}

void InteractiveCAN::slotReturnKeyAndLockProcessingChanged()
{
    if (ReturnKeyLockManager::instance()->isProcessing()) {
        connect(m_bleClient, &BLEClient::workTicketResultReceived,
                ReturnKeyLockManager::instance(), &ReturnKeyLockManager::slotReceivedJobTicket,
                Qt::QueuedConnection);
    }
    else {
        disconnect(m_bleClient, &BLEClient::workTicketResultReceived,
                   ReturnKeyLockManager::instance(), &ReturnKeyLockManager::slotReceivedJobTicket);
    }
}

InteractiveCAN *InteractiveCAN::instance()
{
    if (!pInstance) {
        pInstance = new InteractiveCAN;
    }
    return pInstance;
}

InteractiveCAN *InteractiveCAN::create(QQmlEngine *, QJSEngine *)
{
    return instance();
}

InteractiveCAN::~InteractiveCAN()
{
    m_canClient->stopThreadAndRelease();
    delete m_canClient;
    m_canClient = nullptr;

    m_bleClient->stopDeviceDiscovery();
    delete m_bleClient;
    m_bleClient = nullptr;

    delete m_httpClient;
    m_httpClient = nullptr;
}

void InteractiveCAN::openEKey()
{
    resetAll();

    int tryCount = 3;
    for (int i = 0; i < tryCount; i++) {
        if (m_canClient->openEKey(m_keyNFC)) {
            break;
        }
    }
    qDebug() << "[InteractiveCAN] key nfc: " << m_keyNFC;
    if (!m_keyNFC.isEmpty()) {
        QJsonObject jsonRoot;
        QDateTime currentDateTime = QDateTime::currentDateTime();
        qint64 timestampSeconds = currentDateTime.toMSecsSinceEpoch();

        QString url = Config()->keyMACByNFC;
        jsonRoot.insert("nfc", this->m_keyNFC);

        QJsonDocument jsonDoc(jsonRoot);
        QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Compact);

        emit signalGetRequestData(timestampSeconds, url, jsonData, GetInteractiveData()->m_token);
    }
}

void InteractiveCAN::getEKey()
{
    qDebug() << "[getEKey] " << m_finishedFlags;
    if (m_finishedFlags) return;

    if (m_canClient) {
        m_canClient->setWorkingKey(true);
    }
    int tryCount = 3;
    for (int i = 0; i < tryCount; i++) {
        if (m_canClient->unlockEKey()) {
            break;
        }
    }
}

void InteractiveCAN::getLocks()
{
    if (m_canClient) {
        m_canClient->setWorkingLocks(true);
    }
    QList<int> lockNums = m_lockRFIDs.keys();
    int tryCount = 3;
    for (int i = 0; i < tryCount; i++) {
        if (m_canClient->unlockLocks(lockNums)) {
            break;
        }
    }
}

void InteractiveCAN::getLockRFIDs(QMap<int, QString> &rfids)
{
    if (m_isolationPoints.length() <= 0) return;

    int tryCount = 3;
    for (int i = 0; i < tryCount; i++) {
        if (m_canClient->getLockRfids(m_isolationPoints.length(), rfids)) {
            break;
        }
    }
    qDebug() << m_lockRFIDs;
    if (m_okOpenKey) {
        sendJobTicket();
    }
}

void InteractiveCAN::sendJobTicket()
{
    qDebug() << m_pointInfo.count() << m_lockRFIDs.count() << m_isolationPoints.count();
    if (m_pointInfo.count() != m_lockRFIDs.count() || m_lockRFIDs.count() != m_isolationPoints.count()) {
        return;
    }

    QJsonArray data;
    QJsonArray pointListArray;
    for (auto iter = m_pointInfo.begin(); iter != m_pointInfo.end(); ++iter) {
        QJsonObject pointItem;
        pointItem.insert("dataId", iter->dataId);
        pointItem.insert("equipName", iter->equipName);
        pointItem.insert("equipRfidNo", iter->equipRfidNo);
        pointItem.insert("target", iter->target);
        if (iter->target > 0) {
            pointItem.insert("infoRfidNo", iter->infoRfidNo);
        }
        pointListArray.append(pointItem);
    }

    QJsonObject dataList;
    dataList.insert("dataList", pointListArray);
    dataList.insert("codeId", 1);
    dataList.insert("taskCode", QString::number(m_taskCode));

    data.append(dataList);

    QJsonArray lockListArray;
    for (auto iter = m_lockRFIDs.begin(); iter != m_lockRFIDs.end(); ++iter) {
        QJsonObject rfidItem;
        rfidItem.insert("lockId", iter.key());
        rfidItem.insert("rfid", iter.value());
        lockListArray.append(rfidItem);
    }

    QString cardNfc;
    QJsonArray nodeUserList = WorkNodeFormModel::instance()->nodeUserList();
    if (!nodeUserList.isEmpty()) {
        for (auto iter = nodeUserList.begin(); iter != nodeUserList.end(); ++iter) {
            QJsonDocument nodeUserDoc = QJsonDocument::fromJson(QByteArray::fromStdString(iter->toString().toStdString().c_str()));
            QJsonObject root = nodeUserDoc.object();
            if (!root.contains("type") || !root.contains("cardNfc")) {
                continue;
            }
            QString userType = root.value("type").toString();
            if (userType == QString("jtlocker")) {
                cardNfc = root.value("cardNfc").toString();
                break;
            }
        }
    }
    QJsonObject rootObj;
    rootObj.insert("cardNo", cardNfc);
    rootObj.insert("data", data);
    rootObj.insert("lockList", lockListArray);
    rootObj.insert("password", "123456");

    QJsonDocument ticketDoc(rootObj);

    m_ticketDoc = ticketDoc;

    if (m_bleClient && m_okOpenKey) {
        qDebug() << "[sendJobTicket] " << ticketDoc.toJson(QJsonDocument::Compact);
        m_bleClient->sendJobTicket(ticketDoc);
    }
}

void InteractiveCAN::updateKeyAndLockStatus(bool status)
{
    m_canClient->setWorkingKey(status);
    m_canClient->setWorkingLocks(status);
}

void InteractiveCAN::uploadColockInfo()
{
//    QJsonArray locksInfo;

//    QJsonObject infoItem;
//    infoItem.insert("nodeId", 1473);
//    infoItem.insert("lockNfc", "C351B1E4");
//    infoItem.insert("hardwareCode", "B8:41:D9:FC:73:AF");

//    locksInfo.append(infoItem);

//    QDateTime currentDateTime = QDateTime::currentDateTime();
//    qint64 timestampSeconds = currentDateTime.toMSecsSinceEpoch();

//    QString url = Config()->updateBackLock;

//    QJsonDocument jsonDoc(locksInfo);
//    QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Compact);
//    qDebug() << "[httpRequestPostUpdateBackLock] " << jsonDoc;

    //    emit signalPostRequestData(timestampSeconds, url, jsonData, NULL, "90db3fc16f08453b8658559697357092");
}

void InteractiveCAN::getWorkTicketByNodeId()
{
    httpRequestGetWorkTicketByNodeId();
}

void InteractiveCAN::connectBLEDevice(const QString &keyNfc)
{
    qDebug() << keyNfc;
    if (!keyNfc.isEmpty()) {
        m_keyNFC = keyNfc;
        QJsonObject jsonRoot;
        QDateTime currentDateTime = QDateTime::currentDateTime();
        qint64 timestampSeconds = currentDateTime.toMSecsSinceEpoch();

        QString url = Config()->keyMACByNFC;
        jsonRoot.insert("nfc", keyNfc);

        QJsonDocument jsonDoc(jsonRoot);
        QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Compact);

        emit signalGetRequestData(timestampSeconds, url, jsonData, GetInteractiveData()->m_token);
    }
}

void InteractiveCAN::unlockDevices(const QString &nfc, const QString &deviceType)
{
    if (deviceType == "key") {
        m_canClient->unlockEKey(nfc);
    }
    else if (deviceType == "lock") {
        m_canClient->unlockLock(nfc);
    }
}

void InteractiveCAN::resetAll()
{
    m_searchedBLE = false;
    m_checkedFlags = true;

    m_okOpenKey = false;
    m_okSendJobTicket = false;
    m_okUnlockLocks = false;
    m_okUnlockKey = false;

    m_pointInfo.clear();
}

QString InteractiveCAN::retryReadLockRFID(int lockNum)
{
    int tryCount = 3;
    for (int i = 0; i < tryCount; i++) {
        QString lockNfc = m_canClient->getLockRFID(lockNum);
        qDebug() << "[retryReadLockRFID] " << lockNfc;
        if (!lockNfc.isEmpty()) {
            return lockNfc;
        }
    }
    return QString();
}

void InteractiveCAN::slotUnlockEKey(const CANKeyBaseChargeStatus& status)
{
    if (status.success) {
        if (m_okOpenKey) {
            QMutexLocker locker(&m_mutex);
            if (!m_okUnlockKey) {
                // 上传作业票信息
                httpRequestPostUploadJobTicket();

//                httpRequestPostInsertUnlockKey();
            }
        }
    }
}

void InteractiveCAN::slotUnlockLocks(bool success, const QList<QString>& lockRfids)
{
    if (success) {
//        QMutexLocker locker(&m_mutex);
        if (m_okOpenKey) {
            m_okUnlockLocks = true;
            if (WorkNodeFormModel::instance()->modelType() == QString("isolation")) {
                m_finishedFlags = false;
                getEKey();
                m_finishedFlags = true;
                // 等待钥匙取出
                QTimer::singleShot(10000, [this]() {
                    m_checkHasKeyTimer->start(1000);
                });
            }
        }
//        qDebug() << "[slotUnlockLocks]: " << lockRfids << WorkNodeFormModel::instance()->modelType();
//        if (WorkNodeFormModel::instance()->modelType() == QString("releaseIsolation")) {
//            if (!m_getLockedRfidFlag) {
//                m_getLockedRfidFlag = true;
//                qDebug() << "[slotUnlockLocks]: " << lockRfids;
//                httpRequestPostUpdateBackLock(lockRfids);

//                emit signalUpdateBackLockStatus(lockRfids);
//            }
//        }
    }
}

void InteractiveCAN::slotDeviceDetected(const QString &nfcId)
{
    if (!nfcId.isEmpty()) {
        if (WorkNodeFormModel::instance()->modelType() == QString("releaseIsolation")) {
            if (!m_getLockedRfidFlag) {
                m_getLockedRfidFlag = true;

                QList<QString> lockRfids;
                lockRfids << nfcId;
                qDebug() << "[slotDeviceDetected]: " << nfcId << lockRfids;
                httpRequestPostUpdateBackLock(lockRfids);

                emit signalUpdateBackLockStatus(lockRfids);
            }
        }
    }
}

void InteractiveCAN::slotSearchBLE()
{
    QMutexLocker locker(&m_mutex);
    if (m_bleClient && !m_currentKeyMAC.isEmpty() && !m_searchedBLE) {
        m_searchedBLE = true;
        emit signalOkSearchedBLE(true);
    }
}

void InteractiveCAN::slotOkSearchedBLE(bool success)
{
    if (success) {
        if (m_bleClient->isConnected()) {
            createColockJobTicket();
        }
        else {
            m_bleClient->apiConnectDevice(m_currentKeyMAC);
        }
    }
}

void InteractiveCAN::slotSendJobTicketStatus(bool success)
{
    if (success) {
        m_okSendJobTicket = true;
        if (WorkNodeFormModel::instance()->modelType() == QString("isolation")) {
            if (!m_okUnlockLocks) {
                getLocks();
            }
        }
        else {
            m_finishedFlags = false;
            getEKey();
            m_finishedFlags = true;
            // 等待钥匙取出
            QTimer::singleShot(10000, [this]() {
                m_checkHasKeyTimer->start(1000);
            });
        }
    }
}

void InteractiveCAN::slotReceivedJobTicketResult(const BLERWorkTicketResult &result)
{
    qDebug() << "[slotReceivedJobTicketResult] " << result.success;
    if (result.success) {
        QVariantMap pointInfo;
        for (auto iter = m_pointInfo.begin(); iter != m_pointInfo.end(); ++iter) {
            pointInfo[QString::number(iter->dataId)] = iter->equipName;
        }

        emit signalJobTicketResult(result.resultDoc.toJson(), pointInfo);
        if (WorkNodeFormModel::instance()->modelType() == QString("isolation")) {
            // 上传点位更新信息
            httpRequestPostUploadPositionInfo(result.resultDoc);
        }
        else {
            httpRequestPostUpdatePointUnlock(result.resultDoc);
        }
    }
}
