#include <QSqlQuery>
#include <QSqlError>
#include <QVariant>

#include "DatabaseManager.h"
#include "TransactionDAO.h"

#include "time/DateUtils.h"

bool TransactionDAO::insertTransactionIfNotExists(const TransactionInfo &info) {
    QSqlQuery query(DatabaseManager::instance().database());

    if (info.amount < 0) {
        // Если исходящая, то мы знаем время timestamp
        query.prepare(R"(
            SELECT COUNT(*) FROM transactions
            WHERE account_id = :account_id AND amount = :amount AND timestamp = :timestamp AND type = :type
        )");
    } else {
        query.prepare(R"(
            SELECT COUNT(*) FROM transactions
            WHERE account_id = :account_id AND amount = :amount AND bankTime = :bankTime AND type = :type
        )");
    }

    query.bindValue(":account_id", info.accountId);
    query.bindValue(":amount", info.amount);
    query.bindValue(":type", transactionTypeToString(info.type));
    query.bindValue(":timestamp", DateUtils::toUtcString(info.timestamp));

    if (!query.exec() || !query.next()) {
        qWarning() << "Ошибка при проверке существования transaction:" << query.lastError().text();
        return false;
    }

    if (query.value(0).toInt() > 0) {
        return false; // Уже существует
    }

    query.prepare(R"(
        INSERT INTO transactions (
            account_id, amount, fee, status, phone, type,
            description, updated_description, short_description, updated_short_description,
            bank_name, bank_time, name, bank_tr_external_id,
            update_time, complete_time, timestamp
        ) VALUES (
            :account_id, :amount, :fee, :status, :phone, :type,
            :description, :updated_description, :short_description, :updated_short_description,
            :bank_name, :bank_time, :name, :bank_tr_external_id,
            :update_time, :complete_time, :timestamp
        )
    )");

    query.bindValue(":account_id", info.accountId);
    query.bindValue(":amount", info.amount);
    query.bindValue(":fee", info.fee);
    query.bindValue(":status", transactionStatusToString(info.status));
    query.bindValue(":type", transactionTypeToString(info.type));
    query.bindValue(":phone", info.phone);
    query.bindValue(":name", info.name);
    query.bindValue(":description", info.description);
    query.bindValue(":updated_description", info.updatedDescription);
    query.bindValue(":short_description", info.shortDescription);
    query.bindValue(":updated_short_description", info.updatedShortDescription);
    query.bindValue(":bank_name", info.bankName);
    query.bindValue(":bank_time", info.bankTime);
    query.bindValue(":bank_tr_external_id", info.bankTrExternalId);
    query.bindValue(":update_time", DateUtils::toUtcString(info.updateTime));
    query.bindValue(":complete_time", DateUtils::toUtcString(info.completeTime));

    if (!query.exec()) {
        qWarning() << "Ошибка при вставке transaction:" << query.lastError().text();
        return false;
    }

    return true;
}

int TransactionDAO::insertTransaction(const TransactionInfo &info) {
    QSqlQuery query(DatabaseManager::instance().database());

    query.prepare(R"(
        INSERT INTO transactions (
            account_id, amount, fee, status, phone, type,
            description, updated_description, short_description, updated_short_description,
            bank_name, bank_time, name, bank_tr_external_id,
            update_time, complete_time
        ) VALUES (
            :account_id, :amount, :fee, :status, :phone, :type,
            :description, :updated_description, :short_description, :updated_short_description,
            :bank_name, :bank_time, :name, :bank_tr_external_id,
            :update_time, :complete_time
        )
    )");

    query.bindValue(":account_id", info.accountId);
    query.bindValue(":amount", info.amount);
    query.bindValue(":fee", info.fee);
    query.bindValue(":status", transactionStatusToString(info.status));
    query.bindValue(":type", transactionTypeToString(info.type));
    query.bindValue(":phone", info.phone);
    query.bindValue(":name", info.name);
    query.bindValue(":description", info.description);
    query.bindValue(":updated_description", info.updatedDescription);
    query.bindValue(":short_description", info.shortDescription);
    query.bindValue(":updated_short_description", info.updatedShortDescription);
    query.bindValue(":bank_name", info.bankName);
    query.bindValue(":bank_time", info.bankTime);
    query.bindValue(":bank_tr_external_id", info.bankTrExternalId);
    query.bindValue(":update_time", DateUtils::toUtcString(info.updateTime));
    query.bindValue(":complete_time", DateUtils::toUtcString(info.completeTime));

    if (!query.exec()) {
        qWarning() << "Ошибка при вставке transaction:" << query.lastError().text();
        return -1;
    }

    return query.lastInsertId().toInt();
}

bool TransactionDAO::deleteTransaction(const int id) {
    QSqlQuery query(DatabaseManager::instance().database());
    query.prepare("DELETE FROM transactions WHERE id = :id");
    query.bindValue(":id", id);
    return query.exec();
}


TransactionInfo parseTransaction(const QSqlQuery &query) {
    TransactionInfo tx;
    tx.id = query.value("id").toInt();
    tx.accountId = query.value("account_id").toInt();
    tx.amount = query.value("amount").toDouble();
    tx.fee = query.value("fee").toDouble();
    tx.status = transactionStatusFromString(query.value("status").toString());
    tx.type = transactionTypeFromString(query.value("type").toString());
    tx.phone = query.value("phone").toString();
    tx.name = query.value("name").toString();
    tx.description = query.value("description").toString();
    tx.updatedDescription = query.value("updated_description").toString();
    tx.shortDescription = query.value("short_description").toString();
    tx.updatedShortDescription = query.value("updated_short_description").toString();
    tx.bankName = query.value("bank_name").toString();
    tx.bankTime = query.value("bank_time").toString();
    tx.bankTrExternalId = query.value("bank_tr_external_id").toString();
    tx.updateTime = DateUtils::fromUtcString(query.value("update_time").toString());
    tx.completeTime = DateUtils::fromUtcString(query.value("complete_time").toString());
    tx.timestamp = DateUtils::fromUtcString(query.value("timestamp").toString());
    return tx;
}


QList<TransactionInfo> TransactionDAO::getTransactions(const int accountId) {
    QList<TransactionInfo> list;
    QSqlQuery query(DatabaseManager::instance().database());

    query.prepare(R"(
        SELECT * FROM transactions
        WHERE account_id = :account_id
        ORDER BY timestamp DESC
    )");

    query.bindValue(":account_id", accountId);

    if (!query.exec()) {
        qWarning() << "Ошибка при получении transactions:" << query.lastError().text();
        return list;
    }

    while (query.next()) {
        list.append(parseTransaction(query));
    }

    return list;
}

bool TransactionDAO::updateTransaction(const TransactionInfo &info) {
    QSqlQuery query(DatabaseManager::instance().database());

    qDebug().noquote().nospace() << convertTransactionToString(info);

    query.prepare(R"(
        UPDATE transactions SET
            amount = :amount,
            fee = :fee,
            status = :status,
            type = :type,
            phone = :phone,
            description = :description,
            updated_description = :updated_description,
            short_description = :short_description,
            updated_short_description = :updated_short_description,
            bank_name = :bank_name,
            bank_time = :bank_time,
            name = :name,
            bank_tr_external_id = :bank_tr_external_id,
            update_time = :update_time,
            complete_time = :complete_time,
            timestamp = :timestamp
        WHERE id = :id
    )");

    query.bindValue(":id", info.id);
    query.bindValue(":amount", info.amount);
    query.bindValue(":fee", info.fee);
    query.bindValue(":status", transactionStatusToString(info.status));
    query.bindValue(":type", transactionTypeToString(info.type));
    query.bindValue(":phone", info.phone);
    query.bindValue(":name", info.name);
    query.bindValue(":description", info.description);
    query.bindValue(":updated_description", info.updatedDescription);
    query.bindValue(":short_description", info.shortDescription);
    query.bindValue(":updated_short_description", info.updatedShortDescription);
    query.bindValue(":bank_name", info.bankName);
    query.bindValue(":bank_time", info.bankTime);
    query.bindValue(":bank_tr_external_id", info.bankTrExternalId);
    query.bindValue(":update_time", DateUtils::toUtcString(info.updateTime));
    query.bindValue(":complete_time", DateUtils::toUtcString(info.completeTime));
    query.bindValue(":timestamp", DateUtils::toUtcString(info.timestamp));

    if (!query.exec()) {
        qWarning() << "Ошибка при обновлении transaction:" << query.lastError().text();
        return false;
    }

    return true;
}

bool TransactionDAO::updateTransactionStatus(
    const int id,
    const TransactionStatus status
) {
    QSqlQuery query(DatabaseManager::instance().database());

    query.prepare(R"(
        UPDATE transactions SET
            status = :status,
            update_time = :update_time,
            complete_time = :complete_time
        WHERE id = :id
    )");

    // const QString currentTime = now.toString("yyyy-MM-dd HH:mm:ss");
    query.bindValue(":id", id);
    query.bindValue(":status", transactionStatusToString(status));
    query.bindValue(":update_time", DateUtils::toUtcString(DateUtils::getUtcNow()));

    if (status == TransactionStatus::Complete) {
        query.bindValue(":complete_time", DateUtils::toUtcString(DateUtils::getUtcNow()));
    } else {
        query.bindValue(":complete_time", QVariant(QMetaType(QMetaType::QString)));
    }

    if (!query.exec()) {
        qWarning() << "Ошибка при обновлении transaction:" << query.lastError().text();
        return false;
    }
    return true;
}

TransactionInfo TransactionDAO::getTransactionById(const int id) {
    QSqlQuery query(DatabaseManager::instance().database());
    query.prepare("SELECT * FROM transactions WHERE id = :id");
    query.bindValue(":id", id);


    if (!query.exec() || !query.next()) {
        qWarning() << "Ошибка или не найден transaction с id:" << id << query.lastError().text();
        return {};
    }
    return parseTransaction(query);
}

QList<TransactionInfo> TransactionDAO::getTransactionsWithinHours(const int accountId, const int hours) {
    QList<TransactionInfo> list;
    QSqlQuery query(DatabaseManager::instance().database());
    query.prepare(R"(
        SELECT * FROM transactions
        WHERE account_id = :accountId
        AND timestamp >= datetime('now', '-' || :hours || ' hours')
    )");
    query.bindValue(":accountId", accountId);
    query.bindValue(":hours", hours);

    if (!query.exec()) {
        qWarning() << "Ошибка получения транзакций: " << query.lastError().text();
        return {};
    }

    while (query.next()) {
        list.append(parseTransaction(query));
    }
    return list;
}

bool TransactionDAO::updateTransaction(
    const int id,
    const TransactionStatus status,
    const QString &oldDesc,
    const QString &newDesc
) {
    QSqlQuery query(DatabaseManager::instance().database());

    query.prepare(R"(
        UPDATE transactions SET
            status = :status,
            update_time = :update_time,
            complete_time = :complete_time,
            description = :description,
            updated_description = :updated_description
        WHERE id = :id
    )");

    query.bindValue(":id", id);
    query.bindValue(":status", transactionStatusToString(status));
    query.bindValue(":update_time", DateUtils::toUtcString(DateUtils::getUtcNow()));

    if (oldDesc.isEmpty() || oldDesc != newDesc) {
        query.bindValue(":description", newDesc);
        query.bindValue(":updated_description", oldDesc);
    } else {
        query.bindValue(":description", newDesc);
        query.bindValue(":updated_description", "");
    }

    if (status == TransactionStatus::Complete) {
        query.bindValue(":complete_time", DateUtils::toUtcString(DateUtils::getUtcNow()));
    } else {
        query.bindValue(":complete_time", QVariant(QMetaType(QMetaType::QString)));
    }

    if (!query.exec()) {
        qWarning() << "Ошибка при обновлении transaction:" << query.lastError().text();
        return false;
    }
    return true;
}

bool TransactionDAO::updateExternalTransactionId(const int id, const int externalId) {
    QSqlQuery query(DatabaseManager::instance().database());

    query.prepare(R"(
        UPDATE transactions SET
            external_id = :external_id
        WHERE id = :id
    )");

    query.bindValue(":id", id);
    query.bindValue(":external_id", externalId);

    if (!query.exec()) {
        qWarning() << "Ошибка при обновлении external_id:" << query.lastError().text();
        return false;
    }
    return true;
}
