#include "mainwindow.h"
#include "ui_mainwindow.h"


/**
 * @brief   切换到充值页面
 *  点击工具栏的“充值”触发
 * @param   void
 * @return  void
 * @author  柯劲帆
 * @date    2024-07-30
 */
void MainWindow::on_depositAction_triggered()
{
    ui->depositUserIdBox->clear();
    depositUserIdFilled = false;
    ui->stackedWidget->setCurrentWidget(ui->depositPage);
}


/**
 * @brief   读卡器扫描卡片
 * 点击充值页面的“查询”触发。
 * 如果读卡器未连接，显示警告信息并跳转到设置页面。
 * 显示Inventory的查询结果，最多显示10张卡。
 * @param   void
 * @return  void
 * @author  柯劲帆
 * @date    2024-07-30
 */
void MainWindow::on_depositInventoryButton_clicked()
{
    if (!reader.is_connected())
    {
        QMessageBox::warning(this, QString("提示"), QString("读卡器未连接，请设置。"));
        if (ui->stackedWidget->currentWidget() != ui->settingPage)
        {
            ui->stackedWidget->setCurrentWidget(ui->settingPage);
        }
        return;
    }

    QStringList cardIdList = reader.inventory(10);  // 最多显示10张卡
    ui->depositCardIdBox->clear();
    if (cardIdList.empty())
    {
        QMessageBox::warning(this, "提示", "未发现卡片，请将卡片放置于读卡器上方。");
    }
    else
    {
        ui->depositCardIdBox->addItems(cardIdList);
    }
}


/**
 * @brief   按卡号充值
 * 该函数在用户点击“按卡号充值”按钮时触发，用于对指定卡号进行充值操作。
 * @param   void
 * @return  void
 * @details
 * 函数首先检查软件和设备的准备状态。如果数据库未连接或设备不支持充值，函数将显示警告信息并返回。
 * 接着，函数检查是否选择了卡号和充值金额。如果未选择卡号或充值金额无效，函数将显示警告信息并返回。
 * 然后，函数调用 `topUpCard` 函数进行充值操作，并在操作成功后显示充值结果。
 * @author  柯劲帆
 * @date    2024-07-30
 */
void MainWindow::on_depositByCardIdButton_clicked()
{
    if (!softwareReady())
    {
        QMessageBox::warning(this, QString("提示"), QString("数据库未连接，请设置。"));
        if (ui->stackedWidget->currentWidget() != ui->settingPage)
        {
            ui->stackedWidget->setCurrentWidget(ui->settingPage);
        }
        return;
    }

    if (!reader.is_connected())
    {
        QMessageBox::warning(this, QString("提示"), QString("读卡器未连接，请设置。"));
        if (ui->stackedWidget->currentWidget() != ui->settingPage)
        {
            ui->stackedWidget->setCurrentWidget(ui->settingPage);
        }
        return;
    }

    if (!device.is_depositAllowed())
    {
        QMessageBox::warning(this, QString("提示"), QString("本设备不支持充值。"));
        return;
    }

    if (ui->depositCardIdBox->currentIndex() == -1)
    {
        QMessageBox::warning(this, "提示", "请放置卡片并点击查询按钮。");
        return;
    }
    QString cardId = ui->depositCardIdBox->currentText();

    double topUpValue = ui->depositValueBox->value();
    if (!(topUpValue > 0.00))
    {
        QMessageBox::warning(this, "提示", "请输入大于0.00的充值金额。");
        return;
    }

    bool success = false;
    QString info;
    double originalBalance, finalBalance;
    QString recordId;
    success = topUpCard(cardId, topUpValue, originalBalance, finalBalance, recordId, info);
    if (!success)
    {
        QMessageBox::warning(this, "提示", info + QString("\n充值失败。"));
        return;
    }

    success = reader.insertRecord(recordId, cardId);
    if (!success)
    {
        QMessageBox::warning(this, "提示", "充值成功。写卡失败。本交易不记录在卡上。");
    }

    QString depositResultMessage = QString("充值成功：") + QString::number(topUpValue) + QString("元\n");
    depositResultMessage += QString("原余额：") + QString::number(originalBalance) + QString("元\n");
    depositResultMessage += QString("充值后余额：") + QString::number(finalBalance) + QString("元\n");
    depositResultMessage += QString("充值设备：") + device.getName() + QString("\n");
    depositResultMessage += QString("交易记录号：") + recordId + QString("\n");
    QMessageBox::information(this, "充值成功", depositResultMessage);
    ui->depositValueBox->setValue(0.00);
    return;
}


/**
 * @brief   按学/工号充值
 * 该函数在用户点击“按学/工号充值”按钮时触发，用于对指定学/工号的用户进行充值操作。
 * @param   void
 * @return  void
 * @details
 * 函数首先检查软件和设备的准备状态。如果数据库未连接或设备不支持充值，函数将显示警告信息并返回。
 * 接着，函数检查是否填写了学/工号和充值金额。如果未填写或充值金额无效，函数将显示警告信息并返回。
 * 然后，函数在数据库中查询指定学/工号绑定的卡片。如果卡片不存在，函数将显示警告信息并返回。
 * 如果卡片存在，函数调用 `topUpCard` 函数进行充值操作，并在操作成功后显示充值结果。
 * @author  柯劲帆
 * @date    2024-07-30
 */
void MainWindow::on_depositByUserIdButton_clicked()
{
    if (!softwareReady())
    {
        QMessageBox::warning(this, QString("提示"), QString("数据库未连接，请设置。"));
        if (ui->stackedWidget->currentWidget() != ui->settingPage)
        {
            ui->stackedWidget->setCurrentWidget(ui->settingPage);
        }
        return;
    }

    if (!device.is_depositAllowed())
    {
        QMessageBox::warning(this, QString("提示"), QString("本设备不支持充值。"));
        return;
    }

    if (!depositUserIdFilled)
    {
        QMessageBox::warning(this, "提示", "请填写学/工号。");
        return;
    }
    int userId = ui->depositUserIdBox->value();

    double topUpValue = ui->depositValueBox->value();
    if (!(topUpValue > 0.00))
    {
        QMessageBox::warning(this, "提示", "请输入大于0.00的充值金额。");
        return;
    }

    QSqlQuery query(db->getDatabase());
    bool success = false;
    query.prepare(QString("select id from card "
                          "where userId = :userId;"));
    query.bindValue(":userId", userId);
    success = query.exec();
    if (!success)
    {
        QMessageBox::warning(this, "提示", "数据库异常。充值失败。");
        return;
    }
    if (!query.next())
    {
        QMessageBox::warning(this, "提示", "学/工号未绑定卡。充值失败。");
        return;
    }
    QString cardId = query.value("id").toString();

    QString info;
    double originalBalance = 0, finalBalance = 0;
    QString recordId;
    success = topUpCard(cardId, topUpValue, originalBalance, finalBalance, recordId, info);
    if (!success)
    {
        QMessageBox::warning(this, "提示", info + QString("\n充值失败。"));
        return;
    }

    QString depositResultMessage = QString("充值成功：") + QString::number(topUpValue) + QString("元\n");
    depositResultMessage += QString("原余额：") + QString::number(originalBalance) + QString("元\n");
    depositResultMessage += QString("充值后余额：") + QString::number(finalBalance) + QString("元\n");
    depositResultMessage += QString("充值设备：") + device.getName() + QString("\n");
    depositResultMessage += QString("交易记录号：") + recordId + QString("\n");
    QMessageBox::information(this, "充值成功", depositResultMessage);
    ui->depositValueBox->setValue(0.00);
    return;
}


/**
 * @brief   充值卡
 * 该函数用于给指定的卡片充值。充值前会进行一系列检查，包括设备是否支持充值、卡片状态、充值金额是否超过限额等。
 * 如果所有检查通过，将调用存储过程 `sp_depositCard` 进行充值操作，并返回充值后的余额。
 * @param   cardId          要充值的卡号
 * @param   topUpValue      充值金额
 * @param   originalBalance 充值前的余额，通过引用返回
 * @param   finalBalance    充值后的余额，通过引用返回
 * @param   recordId        交易编号，通过引用返回
 * @param   info            如果出现异常，填入异常信息，通过引用返回
 * @return  int            是否充值成功
 * - 0  失败
 * - 1  成功
 * - 2  充值成功但写卡失败
 * @details
 * 函数首先检查设备是否支持充值。如果设备不支持，函数返回失败并设置错误信息。
 * 接着，函数查询数据库获取卡片的当前状态和余额。如果卡片不存在、已挂失或未启用，函数返回失败并设置相应的错误信息。
 * 然后，函数检查充值后余额是否超过限额（9999.99元）。如果超过限额，函数返回失败并设置错误信息。
 * 如果所有检查通过，函数调用存储过程 `sp_depositCard` 进行充值操作，并通过存储过程返回充值后的余额。
 * @author  柯劲帆
 * @date    2024-07-30
 */
bool MainWindow::topUpCard(QString cardId, double topUpValue, double &originalBalance, double &finalBalance, QString &recordId, QString &info)
{
    if (!device.is_depositAllowed())
    {
        info = "设备不支持充值。";
        return false;
    }

    QSqlQuery query(db->getDatabase());
    bool success;
    query.prepare(QString("select userId, `status`, balance from card "
                          "where id = :cardId;"));
    query.bindValue(":cardId", cardId);
    success = query.exec();
    if (!success)
    {
        info = "数据库异常。";
        return false;
    }
    if (!query.next())
    {
        info = "卡号不存在。";
        return false;
    }
    int cardStatus = query.value("status").toInt();
    if (cardStatus == -1)
    {
        info = "此卡已被挂失。";
        return false;
    }
    if (cardStatus == 0)
    {
        info = "此卡未被启用。";
        return false;
    }
    originalBalance = query.value("balance").toDouble();
    int userId = query.value("userId").toInt();

    if (originalBalance + topUpValue > (double)9999.99)
    {
        info = "充值金额超过限额。卡余额不得超过9999.99元。";
        return false;
    }

    QDateTime currentTime = QDateTime::currentDateTime();
    recordId = getRecordId(currentTime, userId, 1);

    query.finish();
    query.prepare(QString("call sp_depositCard (:cardId, :value, :recordId, :device, :time, :type, @newBalance);"));
    query.bindValue(":cardId", cardId);
    query.bindValue(":value", topUpValue);
    query.bindValue(":recordId", recordId);
    query.bindValue(":device", device.getId());
    query.bindValue(":time", currentTime.toString("yyyy-MM-dd hh:mm:ss"));
    query.bindValue(":type", 1);
    success = query.exec();
    if (!success)
    {
        info = "数据库异常。";
        return false;
    }
    query.exec("select @newBalance;");
    query.next();
    finalBalance = query.value("@newBalance").toDouble();

    return true;
}


/**
 * @brief   生成唯一的记录号
 * 该函数用于生成一个唯一的记录号，记录号由当前时间、用户ID、记录类型和一个随机数拼接而成。
 * @param   currentTime 当前时间
 * @param   userId      用户的学/工号
 * @param   recordType  记录类型
 * - 0  消费
 * - 1  充值
 * @return  QString     生成的唯一记录号（长度为30个字符）
 * @details
 * 函数首先将当前时间转换为字符串格式，然后将用户ID转换为11位长度的字符串（右对齐，不足部分用0填充）。
 * 接着生成一个随机的4位十六进制数，最后将这些部分拼接起来形成一个唯一的记录号。
 * @author  柯劲帆
 * @date    2024-07-30
 */
QString MainWindow::getRecordId(QDateTime currentTime, int userId, int recordType)
{
    QString timeStr = currentTime.toString("yyyyMMddhhmmss");   // 第0-13位：时间
    QString userIdStr = QString::number(userId).rightJustified(11, '0');    // 第14-24位：用户学/工号
    QString typeStr = QString::number(recordType);  // 第25位：记录类型
    QString randomHex = QString::number(QRandomGenerator::global()->bounded(0x10000), 16).rightJustified(4, '0');   // 第26-29位：随机十六进制数
    QString recordId = timeStr + userIdStr + typeStr + randomHex;   // 共30位
    return recordId.toUpper();
}
