#include <QSqlQuery>
#include <QSqlError>
#include <QVariant>
#include <QSettings>
#include "ApplicationDAO.h"
#include "DatabaseManager.h"
#include "db/ApplicationInfo.h"

bool ApplicationDAO::upsertApplication(const ApplicationInfo &info) {
    QSqlQuery query(DatabaseManager::instance().database());

    query.prepare(R"(
        INSERT INTO applications (id, device_id, pin_code, name, code, package, status, comment, install, pin_code_checked)
        VALUES (:id, :device_id, :pin_code, :name, :code, :package, :status, :comment, :install, :pin_code_checked)
        ON CONFLICT(device_id, code)
        DO UPDATE SET
            device_id = excluded.device_id,
            pin_code = excluded.pin_code,
            name = excluded.name,
            code = excluded.code,
            package = excluded.package,
            status = excluded.status,
            comment = excluded.comment,
            install = excluded.install,
            pin_code_checked = excluded.pin_code_checked,
    )");

    query.bindValue(":id", info.id);
    query.bindValue(":device_id", info.deviceId);
    query.bindValue(":pin_code", info.pinCode);
    query.bindValue(":name", info.name);
    query.bindValue(":code", info.code);
    query.bindValue(":package", info.package);
    query.bindValue(":status", info.status);
    query.bindValue(":comment", info.comment);
    query.bindValue(":install", info.install ? "yes" : "no");
    query.bindValue(":pin_code_checked", info.pinCodeChecked ? "yes" : "no");

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

    return true;
}

bool ApplicationDAO::updatePinCode(const QString &appId, const QString &pinCode) {
    QSqlQuery query(DatabaseManager::instance().database());

    query.prepare(R"(
        UPDATE applications
        SET pin_code = :pin_code
        WHERE id = :id
    )");

    query.bindValue(":pin_code", pinCode);
    query.bindValue(":id", appId);

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

bool ApplicationDAO::update(
    const int &appId,
    const QString &pinCode,
    const QString &comment,
    const QString &status
) {
    QSqlQuery query(DatabaseManager::instance().database());

    query.prepare(R"(
        UPDATE applications
        SET pin_code = :pin_code, comment = :comment, status = :status
        WHERE id = :id
    )");

    query.bindValue(":id", appId);
    query.bindValue(":comment", comment);
    query.bindValue(":pin_code", pinCode);
    query.bindValue(":status", status);

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

bool ApplicationDAO::updatePinCodeStatus(
    const int &appId,
    const QString &pinCodeStatus,
    const QString &comment
) {
    QSqlQuery query(DatabaseManager::instance().database());

    query.prepare(R"(
        UPDATE applications
        SET pin_code_checked = :pin_code_checked, comment = :comment, status = :status
        WHERE id = :id
    )");

    query.bindValue(":id", appId);
    query.bindValue(":comment", comment);
    query.bindValue(":pin_code_checked", pinCodeStatus);
    query.bindValue(":status", pinCodeStatus == "yes" ? "active" : "off");

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


ApplicationInfo parseApplication(const QSqlQuery &query) {
    ApplicationInfo app;
    app.id = query.value("id").toInt();
    app.deviceId = query.value("device_id").toString();
    app.pinCode = query.value("pin_code").toString();
    app.code = query.value("code").toString();
    app.name = query.value("name").toString();
    app.package = query.value("package").toString();
    app.status = query.value("status").toString();
    app.comment = query.value("comment").toString();
    app.install = query.value("install").toString() == "yes";
    app.pinCodeChecked = query.value("pin_code_checked").toString() == "yes";
    return app;
}

ApplicationInfo ApplicationDAO::getApplication(const QString &deviceId, const QString &appCode) {
    QSqlQuery query(DatabaseManager::instance().database());

    query.prepare(R"(
        SELECT id, device_id, pin_code, code, name, package, status, comment, install, pin_code_checked
        FROM applications
        WHERE device_id = :device_id AND code = :code
        LIMIT 1
    )");

    query.bindValue(":device_id", deviceId);
    query.bindValue(":code", appCode);

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

    while (query.next()) {
        return parseApplication(query);
    }
    return {};
}

QList<ApplicationInfo> ApplicationDAO::getApplicationsByDeviceId(const QString &deviceId) {
    QSqlQuery query(DatabaseManager::instance().database());
    QList<ApplicationInfo> list;
    query.prepare(R"(
        SELECT id, device_id, pin_code, code, name, package, status, comment, install, pin_code_checked
        FROM applications
        WHERE device_id = :device_id
    )");

    query.bindValue(":device_id", deviceId);

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

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

    return list;
}

bool ApplicationDAO::checkInstalledApps(const QString &deviceId, const QSet<QString> &apps) {
    QSqlQuery query(DatabaseManager::instance().database());
    const QSettings settings("config.ini", QSettings::IniFormat);
    QStringList banks = settings.value("apps/list", QStringList()).toStringList();

    for (const QString &bank: banks) {
        QString package = settings.value(bank + "/android_package_name").toString();
        QString name = settings.value(bank + "/name").toString();

        query.prepare(R"(
        INSERT INTO applications (device_id, name, code, package, status, install, pin_code_checked)
        VALUES (:device_id, :name, :code, :package, :status, :install, :pin_code_checked)
        ON CONFLICT(device_id, code)
        DO UPDATE SET
            install = excluded.install
    )");

        query.bindValue(":device_id", deviceId);
        query.bindValue(":code", bank);
        query.bindValue(":name", name);
        query.bindValue(":package", package);
        query.bindValue(":status", "off");
        query.bindValue(":pin_code_checked", "no");
        query.bindValue(":install", apps.contains(package) ? "yes" : "no");

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


    return true;
}
