#include "alarmEventDataService.h"
#include "logger.h"
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QUrlQuery>
#include <QNetworkReply>
#include <QTimer>
#include <QFile>
#include <QSettings>
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonParseError>
#include <QCoreApplication>

AlarmEventDataService* AlarmEventDataService::instance()
{
    static AlarmEventDataService instance;
    return &instance;
}

AlarmEventDataService::AlarmEventDataService()
    : m_serviceStatus(ServiceStatus::Uninitialized)
    //, m_realTimeConnectionStatus(ConnectionStatus::Disconnected)
    //, m_historicalConnectionStatus(ConnectionStatus::Disconnected)
    , m_amqpClient(nullptr)
    , m_amqpQueue(nullptr)
    , m_amqpExchange(nullptr)
    , m_reconnectAttempts(0)
    , m_maxReconnectAttempts(10)
    , m_currentRequestId(1)
{
    m_reconnectTimer = new QTimer(this);
    m_reconnectTimer->setSingleShot(true);
    connect(m_reconnectTimer, &QTimer::timeout, this, &AlarmEventDataService::onReconnectTimeout);

    m_networkManager = new QNetworkAccessManager(this);

    initialize();
}

AlarmEventDataService::~AlarmEventDataService()
{
    stop();
}

void AlarmEventDataService::initialize()
{
    //读取配置文件
    QString configDir = QCoreApplication::applicationDirPath();
    m_settingsFile = configDir + "/config/alarmEventService_config.ini";
    QFile file(m_settingsFile);
    if(file.open(QIODevice::ReadWrite))
    {
        m_isVaildSettingsFile = true;
        m_settings = new QSettings(m_settingsFile, QSettings::IniFormat);

        //RabbitMQConfig
        m_config.rabbitMQConfig.host = m_settings->value("RabbitMQConfig/host").toString();
        m_config.rabbitMQConfig.port = m_settings->value("RabbitMQConfig/port").toInt();
        m_config.rabbitMQConfig.username = m_settings->value("RabbitMQConfig/username").toString();
        m_config.rabbitMQConfig.password = m_settings->value("RabbitMQConfig/password").toString();
        m_config.rabbitMQConfig.virtualHost = m_settings->value("RabbitMQConfig/virtualHost").toString();
        m_config.rabbitMQConfig.exchangeName = m_settings->value("RabbitMQConfig/exchangeName").toString();
        m_config.rabbitMQConfig.queueName = m_settings->value("RabbitMQConfig/queueName").toString();
        m_config.rabbitMQConfig.routingKey = m_settings->value("RabbitMQConfig/routingKey").toString();
        m_config.rabbitMQConfig.reconnectInterval = m_settings->value("RabbitMQConfig/reconnectInterval").toInt();
        m_config.rabbitMQConfig.heartbeat = m_settings->value("RabbitMQConfig/heartbeat").toInt();
        m_config.rabbitMQConfig.autoAck = m_settings->value("RabbitMQConfig/autoAck").toBool();
        //HistoricalDataConfig
        m_config.historicalConfig.baseUrl = m_settings->value("HistoricalDataConfig/baseUrl").toUrl();
        m_config.historicalConfig.timeout = m_settings->value("HistoricalDataConfig/timeout").toInt();
        m_config.historicalConfig.maxRetries = m_settings->value("HistoricalDataConfig/maxRetries").toInt();
        m_config.historicalConfig.retryInterval = m_settings->value("HistoricalDataConfig/retryInterval").toInt();

        m_serviceStatus = ServiceStatus::Disconnected;
    }
    else
        m_isVaildSettingsFile = false;
}

void AlarmEventDataService::start()
{
    if(m_serviceStatus != ServiceStatus::Disconnected)
        return;

    m_serviceStatus = ServiceStatus::Connecting;
    //启动实时数据服务
    startRealTimeDataService();
}

void AlarmEventDataService::stop()
{
    cancelReconnect();
    cleanupRabbitMQConnection();

    m_serviceStatus = ServiceStatus::Disconnected;
}

void AlarmEventDataService::queryHistoricalEvents(const QDateTime& startTime, const QDateTime& endTime, int confirmStatus)
{
    QUrl url = buildHistoricalQueryUrl(startTime, endTime, confirmStatus);
    //QUrl url = m_config.historicalConfig.baseUrl;
    if(!url.isValid())
        return;

    //创建网络请求
    QNetworkRequest request(url);
    request.setTransferTimeout(m_config.historicalConfig.timeout);
    //发送请求
    QNetworkReply* reply = m_networkManager->get(request);
    connect(reply, &QNetworkReply::finished, this, [this, reply](){
        onHistoricalRequestFinished(reply);
    });
}

void AlarmEventDataService::confirmEvents(const QStringList& uuids, QVariant context)
{
    m_currentRequestId = context.toULongLong();

    QUrl url = buildConfirmEventsUrl();
    if(!url.isValid())
        return;

    //创建网络请求
    QNetworkRequest request(url);
    request.setTransferTimeout(m_config.historicalConfig.timeout);
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
    //将uuis转换为json数组
    QJsonArray jsonArray;
    for(const QString& uuid : uuids)
        jsonArray.append(uuid);
    QJsonDocument doc(jsonArray);
    QByteArray jsonData = doc.toJson();
    //发送请求
    QNetworkReply* reply = m_networkManager->post(request, jsonData);
    connect(reply, &QNetworkReply::finished, this, [this, reply](){
        onConfirmEventsRequestFinished(reply);
    });
}

void AlarmEventDataService::startRealTimeDataService()
{
    cleanupRabbitMQConnection();

    m_amqpClient = new QAmqpClient(this);
    /*connect(m_amqpClient, &QAmqpClient::connected, this, &AlarmEventDataService::onRabbitMQConnected);
    connect(m_amqpClient, &QAmqpClient::disconnected, this, &AlarmEventDataService::onRabbitMQDisconnected);
    connect(m_amqpClient, QOverload<QAMQP::Error>::of(&QAmqpClient::error), this, &AlarmEventDataService::onRabbitMQError);*/
    //采用QT4的连接方式，新的连接方式无法识别
    connect(m_amqpClient, SIGNAL(connected()), this, SLOT(onRabbitMQConnected()));
    connect(m_amqpClient, SIGNAL(disconnected()), this, SLOT(onRabbitMQDisconnected()));
    connect(m_amqpClient, SIGNAL(error(QAMQP::Error)), this, SLOT(onRabbitMQError(QAMQP::Error)));
    connect(m_amqpClient, SIGNAL(socketErrorOccurred(QAbstractSocket::SocketError)), this, SLOT(onRabbitMQSocketError(QAbstractSocket::SocketError)));

    m_amqpClient->setHost(m_config.rabbitMQConfig.host);
    m_amqpClient->setPort(m_config.rabbitMQConfig.port);
    m_amqpClient->setUsername(m_config.rabbitMQConfig.username);
    m_amqpClient->setPassword(m_config.rabbitMQConfig.password);
    m_amqpClient->setVirtualHost(m_config.rabbitMQConfig.virtualHost);
    m_amqpClient->connectToHost();
}

void AlarmEventDataService::cleanupRabbitMQConnection()
{
    if(m_amqpQueue)
    {
        m_amqpQueue->deleteLater();
        m_amqpQueue = nullptr;
    }

    if(m_amqpExchange)
    {
        m_amqpExchange->deleteLater();
        m_amqpExchange = nullptr;
    }

    if(m_amqpClient)
    {
        m_amqpClient->disconnectFromHost();
        m_amqpClient->deleteLater();
        m_amqpClient = nullptr;
    }
}

void AlarmEventDataService::scheduleReconnect()
{
    if(m_serviceStatus == ServiceStatus::Connected || m_serviceStatus == ServiceStatus::Reconnecting)
        return;

    if (m_reconnectAttempts < m_maxReconnectAttempts)
    {
        int delay = m_config.rabbitMQConfig.reconnectInterval * (1 /*<< m_reconnectAttempts*/); // 指数退避,<<n相当于乘以2的n次方
        m_reconnectTimer->start(delay);
        m_reconnectAttempts++;
        m_serviceStatus = ServiceStatus::Reconnecting;
    }
    else
        qInfo() << "Maximum reconnection attempts reached";
}

void AlarmEventDataService::cancelReconnect()
{
    if(m_reconnectTimer->isActive())
        m_reconnectTimer->stop();
    m_reconnectAttempts = 0;
}

MessageHandleResult AlarmEventDataService::processMessage(const QAmqpMessage& message)
{
    QJsonParseError parseError;
    QJsonDocument doc = QJsonDocument::fromJson(message.payload(), &parseError);

    if(parseError.error != QJsonParseError::NoError)
    {
        // QString errorString = QString("JSON parse error: %1 at offset %2")
        //                   .arg(parseError.errorString())
        //                   .arg(parseError.offset);

        return MessageHandleResult::ParseError;
    }

    if(!doc.isObject())
    {
        //QString errorString = "JSON document is not an object";
        return MessageHandleResult::ValidationError;
    }

    QJsonObject obj = doc.object();
    //加一层判断，当前实时报警信息和确认同步信息都走该接口，并且没有做消息层面的标识，因此先从消息体结构来判断
    if(obj.contains("event_uuid") && obj.contains("event") && obj.contains("type"))
    {
        EventData event = EventData::fromJson(obj);
        if(!validateEvent(event))
            return MessageHandleResult::ValidationError;

        emit realTimeEventReceived(event);
    }

    return MessageHandleResult::Success;
}

bool AlarmEventDataService::validateEvent(const EventData& event)
{
    if(event.id.isEmpty())
        return false;
    QDateTime eventTime = QDateTime::fromMSecsSinceEpoch(event.timestamp);
    if(!eventTime.isValid())
        return false;

    return true;
}

QUrl AlarmEventDataService::buildHistoricalQueryUrl(const QDateTime& startTime, const QDateTime& endTime, int confirmStatus)
{
    QUrl url = m_config.historicalConfig.baseUrl;
    QUrlQuery query;
    query.addQueryItem("begin", QString::number(startTime.toMSecsSinceEpoch()));
    query.addQueryItem("end", QString::number(endTime.toMSecsSinceEpoch()));
    if(confirmStatus == 0) //未确认
        query.addQueryItem("status", "0,1,2");
    if(confirmStatus == 1) //已确认
        query.addQueryItem("status", "3,4,5");
    url.setQuery(query);
    return url;
}

void AlarmEventDataService::processHistoricalResponse(const QByteArray& data)
{
    QJsonParseError parseError;
    QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);

    if(parseError.error != QJsonParseError::NoError)
    {
        QString errorMsg = QString("Historical Events JSON parse error: %1 at offset %2")
                          .arg(parseError.errorString())
                          .arg(parseError.offset);
        emit historicalQueryError(errorMsg);
        return;
    }

    if(!doc.isObject())
    {
        QString errorMsg = "Historical Events JSON document is not an object";
        emit historicalQueryError(errorMsg);
        return ;
    }

    QList<EventData> historicalEvents;
    QJsonObject obj = doc.object();
    QJsonValue dataValue = obj.value("data");
    if(dataValue.isArray())
    {
        QJsonArray eventArray = dataValue.toArray();
        int size = eventArray.size();
        for(int i = 0; i < size; ++i)
        {
            QJsonValue eventValue = eventArray.at(i);
            if(!eventValue.isObject())
                continue;

            QJsonObject eventObj = eventValue.toObject();
            EventData event = EventData::fromJson(eventObj);
            if(validateEvent(event))
                historicalEvents.append(event);
        }
    }
    emit historicalQueryData(historicalEvents);
}

QUrl AlarmEventDataService::buildConfirmEventsUrl()
{
    QUrl url = m_config.historicalConfig.baseUrl;
    QUrlQuery query;
    query.addQueryItem("status", "3");
    url.setQuery(query);
    return url;
}

// ==================== 槽函数 ====================
void AlarmEventDataService::onRabbitMQConnected()
{
    //qInfo() << "RabbitMQ connectecd";
    m_serviceStatus = ServiceStatus::Connected;
    //Exchange
    m_amqpExchange = m_amqpClient->createExchange(m_config.rabbitMQConfig.exchangeName);
    m_amqpExchange->declare(QAmqpExchange::FanOut, QAmqpExchange::Durable);
    //Queue
    m_amqpQueue = m_amqpClient->createQueue(m_config.rabbitMQConfig.queueName);
    connect(m_amqpQueue, SIGNAL(opened()), this, SLOT(onAmqpQueueOpened()));
}

void AlarmEventDataService::onRabbitMQDisconnected()
{
    //qWarning() << "Disconnected to RabbitMQ";
    LOG_WARN("RabbitMQ", "Disconnected to RabbitMQ");
    m_serviceStatus = ServiceStatus::Disconnected;
    scheduleReconnect();
}

void AlarmEventDataService::onRabbitMQError(QAMQP::Error error)
{
    //qWarning() << m_amqpClient->errorString();
    LOG_WARN("RabbitMQ", m_amqpClient->errorString());
    m_serviceStatus = ServiceStatus::Error;
    scheduleReconnect();
}

void AlarmEventDataService::onRabbitMQSocketError(QAbstractSocket::SocketError error)
{
    //qWarning() << "RabbitMQ connection socket error: " << error;
    LOG_WARN("RabbitMQ", QString("RabbitMQ connection socket error: %1").arg(error));
    m_serviceStatus = ServiceStatus::Error;
    scheduleReconnect();
}

void AlarmEventDataService::onAmqpQueueOpened()
{
    LOG_INFO("RabbitMQ", "AmqpQueue opend");
    m_amqpQueue->declare(QAmqpQueue::AutoDelete);
    m_amqpQueue->bind(m_amqpExchange, m_config.rabbitMQConfig.routingKey); //对于广播类型的交换机，消息会发送给所有与之绑定的队列，所以此时routingKey无所谓
    connect(m_amqpQueue, SIGNAL(messageReceived()), this, SLOT(onMessageReceived()));
    if(m_config.rabbitMQConfig.autoAck)
        m_amqpQueue->consume(QAmqpQueue::coNoAck);
    else
        m_amqpQueue->consume(0); //0表示不适用任何特殊选项，即手动确认
}

void AlarmEventDataService::onMessageReceived()
{
    QAmqpMessage message = m_amqpQueue->dequeue();
    //qDebug() << message.payload();
    LOG_INFO("RabbitMQ", QString("messageReceived: %1").arg(message.payload()));
    MessageHandleResult result = processMessage(message);

    //手动确认消息
    if(!m_config.rabbitMQConfig.autoAck && result == MessageHandleResult::Success)
        m_amqpQueue->ack(message);
}

void AlarmEventDataService::onReconnectTimeout()
{
    startRealTimeDataService();
}

void AlarmEventDataService::onHistoricalRequestFinished(QNetworkReply* reply)
{
    if(reply->error() == QNetworkReply::NoError)
    {
        QByteArray data = reply->readAll();
        processHistoricalResponse(data);
        LOG_INFO("Http-QueryHistoricalEvents", QString(data));
    }
    else
    {
        QString errorMsg = QString("Request HistoricalData error: %1").arg(/*reply->request().url().toDisplayString(),*/ reply->errorString());
        emit historicalQueryError(errorMsg);
        LOG_ERROR("Http-QueryHistoricalEvents", errorMsg);
    }

    reply->deleteLater();
}

void AlarmEventDataService::onConfirmEventsRequestFinished(QNetworkReply* reply)
{
    QStringList successUuids;
    if(reply->error() == QNetworkReply::NoError)
    {
        QByteArray data = reply->readAll();
        QJsonParseError parseError;
        QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);

        if(parseError.error == QJsonParseError::NoError && doc.isObject())
        {
            QJsonObject docObj = doc.object();
            //int code = docObj.value("code").toInt();
            QString msg = docObj.value("msg").toString();
            QJsonValue dataValue = docObj.value("data");
            if(dataValue.isObject())
            {
                QJsonObject dataObj = dataValue.toObject();
                QJsonValue uuidValue = dataObj.value("success_uuids");
                if(uuidValue.isArray())
                {
                    QJsonArray uuidArray = uuidValue.toArray();
                    for(const QJsonValue& value : std::as_const(uuidArray))
                    {
                        if(value.isString())
                            successUuids.append(value.toString());
                    }

                    emit confirmEventsResult(true, "", successUuids, m_currentRequestId);
                }
                else
                {
                    QString errorMsg = QString("ConfirmEvents reply error, msg: %1").arg(msg);
                    LOG_ERROR("Http-ConfirmEvents", errorMsg);
                    emit confirmEventsResult(false, QString("确认事件失败(%1)").arg(msg), successUuids, m_currentRequestId);
                }
            }
            else
            {
                QString errorMsg = QString("ConfirmEvents reply Json is not object: %1").arg(QString(data));
                LOG_ERROR("Http-ConfirmEvents", errorMsg);
                emit confirmEventsResult(false, QString("确认事件失败(Json is not object)"), successUuids, m_currentRequestId);
            }
        }
        else
        {
            QString errorMsg = QString("ConfirmEvents reply Json parse error: %1").arg(parseError.errorString());
            LOG_ERROR("Http-ConfirmEvents", errorMsg);
            emit confirmEventsResult(false, QString("确认事件失败(JsonParseError)"), successUuids, m_currentRequestId);
        }
    }
    else
    {
        QString errorMsg = QString("ConfirmEvents reply Network error: %1").arg(reply->errorString());
        LOG_ERROR("Http-ConfirmEvents", errorMsg);
        emit confirmEventsResult(false, QString("确认事件失败(Reply Network error)"), successUuids, m_currentRequestId);
    }

    reply->deleteLater();
}
