#include "database.h"
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>

Database::Database(QObject *parent)
    : QObject{parent}
    , m_pool{}
{
    m_pool.setMaxThreadCount(1);
    m_pool.setExpiryTimeout(-1);
}

Database &Database::getInstance()
{
    static Database instance;
    return instance;
}

Database *Database::create(QQmlEngine *qmlEngine, QJSEngine *)
{
    Database &instance = getInstance();
    Q_ASSERT(qmlEngine->thread() == instance.thread());

    static QQmlEngine *engine = nullptr;
    if (engine) {
        Q_ASSERT(qmlEngine == engine);
    } else {
        engine = qmlEngine;
    }

    QJSEngine::setObjectOwnership(&instance, QJSEngine::CppOwnership);

    return &instance;
}

QFuture<void> Database::open(const QString &user,
                             const QString &password,
                             const QString &hostName,
                             bool usePort,
                             int portNumber,
                             const QString &databaseName,
                             const QString &connectOptions)
{
    return QtConcurrent::run(
        &m_pool,
        [this, user, password, hostName, usePort, portNumber, databaseName, connectOptions]() {
            QString errorMessage;
            QString connectionName;
            {
                QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");
                db.setHostName(hostName);
                if (usePort) {
                    db.setPort(portNumber);
                }
                db.setDatabaseName(databaseName);
                db.setConnectOptions(connectOptions);
                if (db.open(user, password)) {
                    QSqlQuery q(db);
                    if (q.exec("SET search_path TO camper, public")) {
                        emit opened();
                        return;
                    }
                    errorMessage = q.lastError().text();
                } else {
                    errorMessage = db.lastError().text();
                }
                connectionName = db.connectionName();
            }
            QSqlDatabase::removeDatabase(connectionName);
            emit errorOcurred(errorMessage);
        });
}

QFuture<void> Database::close()
{
    return QtConcurrent::run(&m_pool, [this]() {
        QString connectionName;
        {
            QSqlDatabase db = QSqlDatabase::database();
            if (!db.isValid()) {
                return;
            }
            connectionName = db.connectionName();
            db.close();
        }
        QSqlDatabase::removeDatabase(connectionName);
        emit closed();
    });
}

#include "moc_database.cpp"
