#include "control.h"
#include "global.h"
#include "fmtool.h"
#include "QsLog.h"
#include "jsonfactory.h"
#include "rspfactory.h"
#include <QSemaphore>
#include <QtConcurrent>
#include <QSettings>
#include <QJsonObject>
#include <QEventLoop>
#include <QApplication>
#include <QSqlError>
#include <QSqlQuery>
#include <QSslSocket>
#include <QSslConfiguration>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QJsonDocument>
#include <Windows.h>
#include <QFontDatabase>
#include <version.h>
#include "DataProcess/rollback.h"
#include "DataProcess/tools.h"
#include "DataProcess/cretopt.h"
#include "DataProcess/fmnetwork.h"
#include <QUuid>

static QString globalDllVersion = QString("%1.%2.%3.%4").arg(VER_MAJOR).arg(VER_MINOR).arg(VER_REVISION).arg(VER_BUILD);

Control::Control(QObject *parent) : QObject(parent), _widget(NULL)
{
    QString path;
    ToolS::GetPath(path);
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", QString("creat"));
    db.setDatabaseName(path + DB_ORDER);
    db.open();

    if(db.isOpen()) {
        QSqlQuery query(db);
        QString sqlTableExist = QString("select count(*) from sqlite_master where type='table' and name = '") + DB_TABLE_NAME + QString("'");
        if ( query.exec(sqlTableExist) ) {
            if( query.first() && !(query.value(0).toInt()) ) {
                QString sql = QString("create table ") + DB_TABLE_NAME + " (orderid varchar(60) primary key, content text)";
                if( query.exec(sql) ) {
                    QLOG_INFO() << QString(" DataBase Create Table orderlist Success");
                } else {
                    QLOG_ERROR() << query.lastError();
                }
            } else {
                QLOG_INFO() << QString(" DataBase Had Exist Table orderlist ");
            }
        } else {
            QLOG_ERROR() << query.lastError();
        }
        db.close();
    } else {
        QLOG_ERROR() << db.lastError().type() << db.lastError().text();
    }
    // 构造时检查环境变量是否设置成功;
    CheckDllEnvironmentNormal();

    _isinterrupt = false;
    _posReqJsonObj = QJsonObject();
    _responseJsonObj = QJsonObject();
}


Control::~Control()
{
}

void Control::Start(const char *indata, char *outdata)
{
    bool refundflag = false;
    QLOG_INFO() << "Control::Start: Get Data From Pos:" << QString::fromLocal8Bit(indata);
    int clearLastError =GetLastError();
    QLOG_INFO() << "Control::Start: Before QSslSocket::supportsSsl() lastError: "<<clearLastError;
    QLOG_INFO() << "Openssl support:" << QSslSocket::supportsSsl() << QApplication::libraryPaths();
    //如果加载SSL库失败,再次尝试加载;
    if( !QSslSocket::supportsSsl() ) {
        CheckQsslSupportsSslError();
        CheckDllEnvironmentNormal();
        QLOG_INFO() << "Control::Start : LastError: "<<GetLastError() <<"  EnvPath:" << qgetenv("PATH");
        QLOG_ERROR() << "Load SupportsSsl False;Then Check SupportsSsl Again After Check Env: " << QSslSocket::supportsSsl();
    }
    //输出ssl版本信息;
    CheckQsslSupportsSslVersion();

    QEventLoop loop;

    if(_widget == NULL)
        _widget = new HostWidget();

    connect(_widget, &HostWidget::Interrupt, this, &Control::OnInterrupt);
    connect(_widget, &HostWidget::RequestWithType, this, &Control::OnRequestWithType);
    connect(_widget, &HostWidget::Exits, this, [&loop, &refundflag] ()
    {
        QLOG_INFO() << "Quit With Normal";
        refundflag = true;
        loop.exit();
    });

    connect(_widget, &HostWidget::ExitWithMSG, this, [this, &loop] (QString code, QString message)
    {
        QLOG_INFO() << "Quit With : " << message;
        SetResponseWithMessage(code, message);
        loop.exit();
    });

    if(InitPOSReqJsonObj(indata)) {
        _widget->ShowUiWithRequest(_posReqJsonObj);

        ReqType reqType = (ReqType)FMTool::GetJsonValue(_posReqJsonObj, JSON_KEY_REQTYPE).toInt();

        if(reqType != REFUND && reqType != PAY) {
            loop.exec();
        } else {
            if(refundflag == false)
                loop.exec();
        }
    }

    _lock.lock();
    delete _widget;
    _widget = NULL;
    _lock.unlock();

    QByteArray responseArray = QJsonDocument(_responseJsonObj).toJson(QJsonDocument::Compact);
    char *rspData = responseArray.data();
    char tmpbuf[MAX_BUF_LEN] = { 0 };
    int len = FMTool::UTF8ToGBK((unsigned char *)rspData, (unsigned char *)tmpbuf, MAX_PRINT_LEN);

    memcpy((char *)outdata, tmpbuf, len);

    QLOG_INFO() << "Return Data To Pos : " << QString::fromLocal8Bit(outdata);
}

bool Control::InitPOSReqJsonObj(const char *indata)
{
    QJsonParseError parseError;
    QJsonDocument inDataDoc = QJsonDocument::fromJson(QString::fromLocal8Bit(indata).toUtf8(), &parseError);

    if(parseError.error == QJsonParseError::NoError) {
        _posReqJsonObj = inDataDoc.object();
    } else {
        QString error = QString::fromLocal8Bit("请求解析失败：%1").arg(parseError.errorString());
        QLOG_ERROR() << error;
        SetResponseWithMessage("23", error);
        return false;
    }

    // 检查请求类型是否合法
    int type = FMTool::GetJsonValue(_posReqJsonObj, JSON_KEY_REQTYPE).toInt();

    // 获取POS请求时的 PartnerId, 进行 华东区 / 非华东区  进行不同加签操作;
    _posPartnerRegion = FMTool::GetJsonValue(_posReqJsonObj, JSON_KEY_PARTNERID).toInt();

    if(type < EC_SIGN || type > EC_FINDS) {
        QString error = QString::fromLocal8Bit("未知的请求类型：%1").arg(type);
        QLOG_ERROR() << error;
        SetResponseWithMessage("23", error);
        return false;
    } else {
        _posReqJsonObj[JSON_KEY_REQTYPE] = type - EC_SIGN;
    }

    // 检查是否缺少必要的字段
    int reqType = _posReqJsonObj[JSON_KEY_REQTYPE].toInt();
    if(reqType == REFUND) {
        if(_posReqJsonObj[JSON_KEY_FMID].toString().isEmpty()) {
            QString error = QString::fromLocal8Bit("fmId不能为空");
            QLOG_ERROR() << error;
            SetResponseWithMessage("23", error);
            return false;
        }
    }

    return true;
}

void Control::SetResponseWithMessage(QString code, const QString &message)
{
    _responseJsonObj[JSON_KEY_STATUSCODE] = code.toInt();
    _responseJsonObj[JSON_KEY_MESSAGE] = message;
}


void Control::CheckDllEnvironmentNormal() {
    QString appLibPath ="",configLibPath = "" ;
    char pathStr[MAX_PATH] = { 0 };
    ToolS::GetProcPath(pathStr);
    appLibPath = QString::fromLocal8Bit(pathStr);
    appLibPath = appLibPath.replace("\\", "/");
    //addLibraryPath 允许重复添加,其自身会过滤重复;
    QApplication::addLibraryPath(appLibPath);

    FMPSettings setting;
    configLibPath = setting.GetValue(CONFIG_ALL_PATH, pathStr).toString();
    configLibPath = configLibPath.replace("\\", "/");
    QLOG_INFO()<< "CheckDllEnvironmentNormal:: configLibPath: " << configLibPath <<" appLibPath:" <<appLibPath;
    QApplication::addLibraryPath(configLibPath);

    QByteArray val = qgetenv("PATH");
    QByteArray valNew ="";
    if( val.contains(pathStr) ) {
        QLOG_INFO()<< "CheckDllEnvironmentNormal:: Env Had Include Path: " << pathStr;
    } else {
        valNew += val;
        valNew += ";";
        valNew += pathStr;
        bool setEnvRet = qputenv("PATH", valNew);
        QLOG_INFO()<< "CheckDllEnvironmentNormal:: setEnvRet: " << setEnvRet<<" ErrorNum: "<<GetLastError();
        if(setEnvRet) {
            QFontDatabase::addApplicationFont(appLibPath + "msyh.ttf");
            QFont ft("Microsoft YaHei UI Light");
            qApp->setFont(ft);
        }
    }
}

void Control::CheckQsslSupportsSslError() {
    long sslLibVerNum = QSslSocket::sslLibraryVersionNumber();
    QString sslLibVerString =QSslSocket::sslLibraryVersionString();
    QLOG_ERROR()<< "CheckQsslSupportsSslError:: sslLibVerNum: " << sslLibVerNum<<" sslLibVerString: "<<sslLibVerString;

    long sslLibBuiVerNum =  QSslSocket::sslLibraryBuildVersionNumber();
    QString sslLibBuiVerString = QSslSocket::sslLibraryBuildVersionString();
    QLOG_ERROR()<< "CheckQsslSupportsSslError:: sslLibBuiVerNum: " << sslLibBuiVerNum<<" sslLibBuiVerString: "<<sslLibBuiVerString;
}

void Control::CheckQsslSupportsSslVersion() {
    long sslLibVerNum = QSslSocket::sslLibraryVersionNumber();
    QString sslLibVerString =QSslSocket::sslLibraryVersionString();
    QLOG_INFO()<< "CheckQsslSupportsSslVersion:: sslLibVerNum: " << sslLibVerNum<<" sslLibVerString: "<<sslLibVerString;

    long sslLibBuiVerNum =  QSslSocket::sslLibraryBuildVersionNumber();
    QString sslLibBuiVerString = QSslSocket::sslLibraryBuildVersionString();
    QLOG_INFO()<< "CheckQsslSupportsSslVersion:: sslLibBuiVerNum: " << sslLibBuiVerNum<<" sslLibBuiVerString: "<<sslLibBuiVerString;
}


bool Control::SendMessageBefore(const QJsonObject &json, QByteArray &outdata, QString &error, ReqType reqType)
{
    QString iv = QString::number(json[JSON_KEY_PARTNERID].toInt()) + json[JSON_KEY_STOREID].toString() + json[JSON_KEY_STATIONID].toString();
    QLOG_INFO() << "SendMessageBefore:: Get  iv (partnerId+storeId+stationId): " << iv;
    QJsonObject tmpjson = json;
    tmpjson[JSON_KEY_VER] = DEFAULT_JSON_VER_VALUE;
    // EC-1446 华东区 必须将此值设定为 1;  CCO-非华东区 对此值无要求: 0 / 1 都兼容;
    tmpjson.insert(JSON_KEY_POSTYPE, 1);

    // 添加  dllVersion + serverUrl  辅助信息;
    if ( !tmpjson.contains(JSON_KEY_DLLVER) )
        tmpjson.insert(JSON_KEY_DLLVER, globalDllVersion);
    if ( !tmpjson.contains(JSON_KEY_SERVER_URL) )
         tmpjson.insert(JSON_KEY_SERVER_URL, _setting.GetValue(VALUE_URL, "").toString() );

    CretOperate::GetMAC(tmpjson);       // add json Key-Value:  "mac": "**-**-**-**-**-**-**-**"
    CretOperate::GetSign(tmpjson, iv);    // RSASign  And add json Key-Value:  "sign": "*"

    QLOG_INFO() << "Send Json Before: " << tmpjson;

    bool isOk = false;
    isOk = SendMessageReady(tmpjson, outdata, error, reqType);

    if(!_isinterrupt && !isOk) {
    	//如果没有点击取消支付按钮直接写数据库异步冲正
        if(tmpjson.contains(JSON_KEY_REQTYPE) && (tmpjson[JSON_KEY_REQTYPE].toInt() == 72 ||
          (tmpjson[JSON_KEY_REQTYPE].toInt() >= 300 && tmpjson[JSON_KEY_REQTYPE].toInt() <= 400)))
        {
            QByteArray data = QJsonDocument(tmpjson).toJson(QJsonDocument::Compact);
            RollBack::SetRollOrder(json[JSON_KEY_PARTORDERID].toString(), QString(data));
        }
        return false;
    }

    return isOk;
}


bool Control::SendMessageReady(const QJsonObject &json, QByteArray &outdata, QString &error, ReqType reqType)
{
    QByteArray array;
    QString url = _setting.GetValue(VALUE_URL, "").toString();
    QByteArray data = QJsonDocument(json).toJson(QJsonDocument::Compact);

#ifdef MOCK_SIMPHONY
    QEventLoop loop;
    QFuture<void> future = QtConcurrent::run( [ this, &loop, json, &array]()
    {
        QLOG_DEBUG() << "=========================================== Read debug data. ========================================";
        int type = FMTool::GetJsonValue(json, JSON_KEY_REQTYPE).toInt(sign);
        QLOG_DEBUG() << "Test req type: " << type;
        QFile f(qApp->applicationDirPath()+QString("/test_simphony/%1.txt").arg(type));
        f.open(QIODevice::ReadOnly);
        array = f.readAll();
        Sleep(1000*10);
        QLOG_DEBUG() << "=========================================== End read debug data. ====================================";
    });
    QFutureWatcher<void> watcher;
    watcher.setFuture(future);
    connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit()));
    loop.exec();
#else
    if(!Control::HttpPost(url, array, data, "application/json;charset=utf-8", "application/json", error, 60, reqType))
    {
        return false;
    }
#endif

    outdata = array;
    return true;
}

bool Control::HttpPost(QString url, QByteArray &outdata, const QByteArray &indata, QString content, QString accept, QString  &error, int timeout, ReqType reqType)
{
    QString host = _setting.GetValue(VALUE_HOST, "").toString();
    QEventLoop loop;
    QTimer timer;

    QSslConfiguration config = QSslConfiguration::defaultConfiguration();
    config.setPeerVerifyMode(QSslSocket::VerifyNone);
    QNetworkAccessManager manger;
    QNetworkRequest request(url);

    request.setSslConfiguration(config);
    request.setRawHeader("Content-Type", content.toUtf8());
    request.setRawHeader("Accept", accept.toUtf8());
    request.setRawHeader("Host", host.toUtf8());
    request.setRawHeader("Authorization", "Basic dXBzLWNsaWVudDo2VGk4TjBXNzRyb1A=");

    QLOG_INFO() << "HttpPost Url: " << url << " Content: " << content << " Accept: " << accept << " Timeout: " << timeout;

    QSharedPointer<QNetworkReply> reply = QSharedPointer<QNetworkReply>(manger.post(request, indata));

    connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
    connect(&manger, &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
    connect(_widget, &HostWidget::Interrupt, &loop, &QEventLoop::quit);
    connect(reply.data(), static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error), &loop, &QEventLoop::quit);
    timer.start(timeout*1000);
    loop.exec();

#ifdef FM_TEST
    QEventLoop test_loop;
    QTimer test_timer;
    connect(&test_timer, &QTimer::timeout, &test_loop, &QEventLoop::quit);
    test_timer.start(3*1000);
    test_loop.exec();
#endif

    if(_isinterrupt) {
        error = QString::fromLocal8Bit("收款被取消");
        return false;
    }

    if(reply->error() != QNetworkReply::NoError) {
        if( reqType == PAY || reqType == REFUND ) {
            error = QString::fromLocal8Bit("网络异常.交易失败.如已扣款将会自动返还");
        } else {
            error = QString::fromLocal8Bit("网络异常,获取服务端返回数据失败");
        }

        QLOG_INFO() << reply->errorString() << "Contents: " << reply->readAll();
        return false;
    }

    outdata = reply->readAll();

    if(outdata.isEmpty()) {
        if( reqType == PAY || reqType == REFUND ) {
            error = QString::fromLocal8Bit("网络异常.交易失败.如已扣款将会自动返还");
        } else {
            error = QString::fromLocal8Bit("网络异常,获取服务端返回数据为空");
        }

        QLOG_INFO() << reply->errorString() << "Contents: " << reply->readAll();
        return false;
    }

    QLOG_INFO() << "HttpPost Response: " << outdata.data();

    return true;
}


bool Control::GetRSA(QString &error)
{
    QJsonObject json;
    QString iv;
    QString url = _setting.GetValue(VALUE_URL_CRET, "").toString();

    json.insert(JSON_KEY_VER, DEFAULT_JSON_VER_VALUE);
    json.insert(JSON_KEY_REQTYPE, SIGN);
    json.insert(JSON_KEY_STOREID, FMTool::GetJsonValue(_posReqJsonObj, JSON_KEY_STOREID).toString());
    json.insert(JSON_KEY_STATIONID, FMTool::GetJsonValue(_posReqJsonObj, JSON_KEY_STATIONID).toString());
    json.insert(JSON_KEY_PARTNERID, FMTool::GetJsonValue(_posReqJsonObj, JSON_KEY_PARTNERID).toInt());
    json.insert(JSON_KEY_DLLVER, globalDllVersion);
    json.insert(JSON_KEY_SERVER_URL, url);

    iv = QString::number(FMTool::GetJsonValue(_posReqJsonObj, JSON_KEY_PARTNERID).toInt())
            + FMTool::GetJsonValue(_posReqJsonObj, JSON_KEY_STOREID).toString()
            + FMTool::GetJsonValue(_posReqJsonObj, JSON_KEY_STATIONID).toString();

    QLOG_INFO() << "Get  iv (partnerId+storeId+stationId): " << iv;

    CretOperate::GetDES3MAC(json, _posPartnerRegion, iv);
    QByteArray array;

    QLOG_INFO() << "Get Mac Sign Request: " << json;

    QByteArray data = QJsonDocument(json).toJson(QJsonDocument::Compact);

    if(!Control::HttpPost(url, array, data,"application/json;charset=utf-8", "application/json", error, 60, SIGN))
    {
        QLOG_ERROR() << "HttpPost Error :" << error;
        return false;
    }

    if(CretOperate::SetRSACret(array, _posPartnerRegion, iv) == 0)
    {
        error = QString::fromLocal8Bit("签名失败，请重新获取签名");
        return false;
    }

    return true;
}

void Control::RequestSign()
{
    QString error;
    bool rlt = GetRSA(error);

    if(rlt)
        SetResponseWithMessage("100", QString::fromLocal8Bit("签名成功"));
    else
        SetResponseWithMessage("22", QString::fromLocal8Bit("签名失败"));

    _lock.lock();
    if(_widget != NULL)
        _widget->ShowResponseMGS(SIGN, rlt, error);

    _lock.unlock();
}

bool Control::GetJson(ReqType type,QJsonObject &json, const QByteArray array, QString &error)
{
    QJsonParseError parseError;
    QJsonDocument jsonDocument = QJsonDocument::fromJson(array.data(), &parseError);

    if( jsonDocument.isNull() ) {
        QLOG_ERROR() << "server return not json" << parseError.errorString();
        error = QString::fromLocal8Bit("服务端返回数据异常");
        return false;
    }

    json = jsonDocument.object();

    if( json.contains(JSON_KEY_MESSAGE) && json.contains(JSON_KEY_STATUSCODE) &&
            ( json[JSON_KEY_STATUSCODE].toInt() == 307 || json[JSON_KEY_STATUSCODE].toInt() == 303 ) )
    {
        error = QString::fromLocal8Bit("签名过期，请重新获取签名");
        json.insert(JSON_KEY_MESSAGE, QString::fromLocal8Bit("签名过期，请重新获取签名"));
    }

    if(type == AGAIN_PRINT)
        json.insert(JSON_KEY_FMID, _fmId);

    if(type == REFUND && json.contains(JSON_KEY_STATUSCODE) && json[JSON_KEY_STATUSCODE].toInt() == 100)
        json.insert(JSON_KET_CLIENTREF, QString("0") );
        // 未发现 成员变量 _request 被赋值; 故使用 0 字符替代;
        //json.insert(JSON_KET_CLIENTREF, FMTool::GetString(_request.Amount, 12));

    RspFactory::GetResponseJson(type, _responseJsonObj, json);

    if(json.contains(JSON_KEY_MESSAGE))
        error = json[JSON_KEY_MESSAGE].toString();

    if(json.contains(JSON_KEY_STATUSCODE) && json[JSON_KEY_STATUSCODE].toInt() == 100)
        return true;

    return false;
}

void Control::RequestBusinessAction(ReqType type, QStringList list)
{
    QJsonObject json, rtjson;
    QString error;
    QByteArray outdata; 
    bool isOk = true;

    if(type == AGAIN_PRINT)
        _fmId = list[0];

    JsonFactory::GetJsonWithType(type, json, _posReqJsonObj, list);
    QJsonDocument(json).toJson(QJsonDocument::Compact);

    isOk = SendMessageBefore(json, outdata, error, type);

    if(isOk)
        isOk = Control::GetJson(type, rtjson, outdata, error);
    else
        SetResponseWithMessage("23", error);

    //如果没有点击取消按钮走正常的退出或显示流程
    if(!_isinterrupt) {
        _lock.lock();

        if(_widget != NULL && type == FINDS) {
            _widget->ShowQueryRespJson(isOk, rtjson, error);
        } else {
            _widget->ShowResponseMGS(type, isOk, error);
        }

        _lock.unlock();

    }
    // 中断支付请求被检测到, 写数据库 通过 RollBack 进行异步冲正;
    else {

        QString iv = QString::number(json[JSON_KEY_PARTNERID].toInt()) +
                json[JSON_KEY_STOREID].toString() + json[JSON_KEY_STATIONID].toString();

        QLOG_INFO() << "iv : " << iv;

        QJsonObject tmpjson = json;
        tmpjson[JSON_KEY_VER] = DEFAULT_JSON_VER_VALUE;
        tmpjson[JSON_KEY_REQTYPE] = 3;
        CretOperate::GetSign(tmpjson, iv);

        QLOG_INFO() << "Send Reverse Json (rollback/wash): " << tmpjson;
        QByteArray tmpdata = QJsonDocument(tmpjson).toJson(QJsonDocument::Compact);
        error = QString::fromLocal8Bit("支付取消成功.如已扣款将会自动返还");

        RollBack::SetRollOrder(json[JSON_KEY_PARTORDERID].toString(), QString(tmpdata));
        SetResponseWithMessage("23", error);

        _lock.lock();

        if(_widget != NULL && type == FINDS) {
            _widget->ShowQueryRespJson(isOk, rtjson, error);
        } else {
            _widget->ShowResponseMGS(type, isOk, error);
        }

        _lock.unlock();

    }
}

void Control::OnRequestWithType(ReqType type, QStringList list)
{
    if(type == SIGN) {
        // 签名请求;
        RequestSign();
        return ;
    }

    // 其他非签名请求: 支付+退款+查找 [ 打印日结单 + 打印交班单 + 流水查询重打小票]
    RequestBusinessAction(type, list);
}

void Control::setIsinterrupt(bool isinterrupt)
{
    _isinterrupt = isinterrupt;
}

void Control::OnInterrupt()
{
    _isinterrupt = true;
}

bool Control::IsValidCode(const QString &code)
{
    int beginNum = code.mid(0, 2).toInt();

    if ( beginNum>=25 && beginNum<=30 ) {
        return true;
    }

    if ( beginNum == 13 && code.length() == 18 ) {
        return true;
    }

    return false;
}
