// SPDX-FileCopyrightText: 2017 Linus Jahn <lnj@kaidan.im>
// SPDX-FileCopyrightText: 2019 Xavier <xavi@delape.net>
// SPDX-FileCopyrightText: 2020 Yury Gubich <blue@macaw.me>
// SPDX-FileCopyrightText: 2022 Melvin Keskin <melvo@olomono.de>
// SPDX-FileCopyrightText: 2022 Bhavy Airi <airiragahv@gmail.com>
// SPDX-FileCopyrightText: 2023 Filipe Azevedo <pasnox@gmail.com>
// SPDX-FileCopyrightText: 2023 Tibor Csötönyi <work@taibsu.de>
//
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

// std
#include <memory>
// Qt
#include <QObject>

class QSqlQuery;
class QSqlDatabase;
struct DatabasePrivate;

/**
 * The Database class manages the SQL database. It opens the database and converts old
 * formats.
 */
class Database : public QObject
{
    Q_OBJECT

public:
    static Database *instance();

    explicit Database(QObject *parent = nullptr);
    ~Database();

    /**
     * Converts the database to the latest version and guarantees that all tables have
     * been created.
     */
    void createTables();

    // used in unit tests
    void createV3Database();

    /// Transaction on random thread from the thread pool (should be replaced in the
    /// future).
    void startTransaction();
    void commitTransaction();

private:
    QObject *dbWorker() const;
    QSqlDatabase currentDatabase();
    QSqlQuery createQuery();

    /// Returns the number of active transactions on the current thread.
    int &activeTransactions();
    /// Begins a transaction if none has been started.
    void transaction();
    /// Commits the transaction if every transaction has been finished.
    void commit();

    /**
     * @return true if the database has to be converted using @c convertDatabase()
     * because the database is not up-to-date.
     */
    bool needToConvert();

    /**
     * Converts the database to latest model.
     */
    void convertDatabase();

    /**
     * Loads the database information and detects the database version.
     */
    void loadDatabaseInfo();

    /**
     * Saves the database information.
     */
    void saveDatabaseInfo();

    /**
     * Creates a new database without content.
     */
    void createNewDatabase();

    /*
     * Upgrades the database to the next version.
     */
    void convertDatabaseToV2();
    void convertDatabaseToV3();
    void convertDatabaseToV4();
    void convertDatabaseToV5();
    void convertDatabaseToV6();
    void convertDatabaseToV7();
    void convertDatabaseToV8();
    void convertDatabaseToV9();
    void convertDatabaseToV10();
    void convertDatabaseToV11();
    void convertDatabaseToV12();
    void convertDatabaseToV13();
    void convertDatabaseToV14();
    void convertDatabaseToV15();
    void convertDatabaseToV16();
    void convertDatabaseToV17();
    void convertDatabaseToV18();
    void convertDatabaseToV19();
    void convertDatabaseToV20();
    void convertDatabaseToV21();
    void convertDatabaseToV22();
    void convertDatabaseToV23();
    void convertDatabaseToV24();
    void convertDatabaseToV25();
    void convertDatabaseToV26();
    void convertDatabaseToV27();
    void convertDatabaseToV28();
    void convertDatabaseToV29();
    void convertDatabaseToV30();
    void convertDatabaseToV31();
    void convertDatabaseToV32();
    void convertDatabaseToV33();
    void convertDatabaseToV34();
    void convertDatabaseToV35();
    void convertDatabaseToV36();
    void convertDatabaseToV37();
    void convertDatabaseToV38();
    void convertDatabaseToV39();
    void convertDatabaseToV40();
    void convertDatabaseToV41();
    void convertDatabaseToV42();
    void convertDatabaseToV43();
    void convertDatabaseToV44();
    void convertDatabaseToV45();
    void convertDatabaseToV46();
    void convertDatabaseToV47();
    void convertDatabaseToV48();
    void convertDatabaseToV49();
    void convertDatabaseToV50();
    void convertDatabaseToV51();
    void convertDatabaseToV52();
    void convertDatabaseToV53();
    void convertDatabaseToV54();
    void convertDatabaseToV55();
    void convertDatabaseToV56();
    void convertDatabaseToV57();

    std::unique_ptr<DatabasePrivate> d;

    static Database *s_instance;

    friend class DatabaseComponent;
    friend class DatabaseTest;
    // TODO: Remove TrustDb as friend class once MessageDb is no singleton anymore
    friend class TrustDb;
};
