#ifndef CONFIG_H
#define CONFIG_H

#include "core_global.h"
#include "config_builder.h"
#include "letos.h"
#include <QObject>
#include <QVariant>
#include <QHash>
#include <QStringList>
#include <QSharedPointer>
#include <QDateTime>

namespace Cfg
{
    enum ExplainQueryMode
    {
        EXPLAIN = 0,
        QUERY_PLAN = 1
    };
};

CFG_CATEGORIES(Core,
    CFG_CATEGORY(General,
        CFG_ENTRY(int,          SqlHistorySize,           10000)
        CFG_ENTRY(int,          DdlHistorySize,           1000)
        CFG_ENTRY(int,          BindParamsCacheSize,      1000)
        CFG_ENTRY(int,          PopulateHistorySize,      100)
        CFG_ENTRY(QString,      LoadedPlugins,            "")
        CFG_ENTRY(QVariantHash, ActiveCodeFormatter,      QVariantHash())
        CFG_ENTRY(bool,         CheckUpdatesOnStartup,    true)
        CFG_ENTRY(QString,      Language,                 "en")
        CFG_ENTRY(QVariantHash, PostRestoreConfigUpdates, QVariantHash())
        CFG_ENTRY(bool,         PreferMasterOverSchema,   false)
    )
    CFG_CATEGORY(Console,
        CFG_ENTRY(int,          HistorySize,              100)
    )
    CFG_CATEGORY(Internal,
        CFG_ENTRY(QVariantList, Collations,               QVariantList())
        CFG_ENTRY(QVariantList, Extensions,               QVariantList())
        CFG_ENTRY(QVariantList, CodeSnippets,             QVariantList())
        CFG_ENTRY(QString,      BugReportUser,            QString())
        CFG_ENTRY(QString,      BugReportPassword,        QString())
        CFG_ENTRY(QString,      BugReportRecentTitle,     QString())
        CFG_ENTRY(QString,      BugReportRecentContents,  QString())
        CFG_ENTRY(bool,         BugReportRecentError,     false)
        CFG_ENTRY(bool,         DefaultSnippetsCreated,   false)
    )
    CFG_CATEGORY(CodeAssistant,
        CFG_ENTRY(bool,         AutoTrigger,              true)
    )
    CFG_CATEGORY(SupportReminder,
        CFG_ENTRY(bool,         ShowOccasional,           true)
#ifdef SUPPORT_REMINDERS
        CFG_ENTRY(int,          LaunchCount,              0)
        CFG_ENTRY(QDate,        FirstLaunchDate,          QDate())
        CFG_ENTRY(QDate,        LastShown,                QDate())
        CFG_ENTRY(QDate,        LastClicked,              QDate())
#endif
    )
)

#define CFG_CORE CFG_INSTANCE(Core)

class QAbstractItemModel;
class DdlHistoryModel;
class QSettings;

class API_EXPORT Config : public QObject
{
    Q_OBJECT

    public:
        virtual ~Config();

        struct CfgDb
        {
            QString name;
            QString path;
            QHash<QString,QVariant> options;
        };

        typedef QSharedPointer<CfgDb> CfgDbPtr;

        struct DbGroup;
        typedef QSharedPointer<DbGroup> DbGroupPtr;

        struct API_EXPORT DbGroup
        {
            qint64 id;
            QString referencedDbName;
            QString name;
            QList<DbGroupPtr> childs;
            int order;
            bool open = false;
            bool dbExpanded = false;
        };

        struct API_EXPORT SqlHistoryEntry
        {
            QString query;
            QString dbName;
            int rowsAffected;
            int unixtime;
        };

        typedef QSharedPointer<SqlHistoryEntry> SqlHistoryEntryPtr;

        struct API_EXPORT DdlHistoryEntry
        {
            QString dbName;
            QString dbFile;
            QDateTime timestamp;
            QString queries;
        };

        typedef QSharedPointer<DdlHistoryEntry> DdlHistoryEntryPtr;

        struct API_EXPORT ReportHistoryEntry
        {
            int id = 0;
            bool isFeatureRequest = false;
            int timestamp = 0;
            QString title;
            QString url;
        };

        struct API_EXPORT ExportImportParams
        {
            bool functions = true;
            bool collations = true;
            bool snippets = true;
            bool extensions = true;
            bool overwriteOnImport = false;
        };

        typedef QSharedPointer<ReportHistoryEntry> ReportHistoryEntryPtr;

        typedef std::function<QString()> AskUserForConfigDirFunc;

        static void setMasterConfigFile(const QString& path);
        static QString getMasterConfigFile();
        static void setAskUserForConfigDirFunc(const AskUserForConfigDirFunc& value);
        static QString getPortableConfigPath();
        static QSettings* getSettings();

        virtual void init() = 0;
        virtual const QString& getConfigDir() const = 0;
        virtual QString getConfigFilePath() const = 0;
        virtual bool isInMemory() const = 0;

        virtual void beginMassSave() = 0;
        virtual void commitMassSave() = 0;
        virtual void rollbackMassSave() = 0;
        virtual bool isMassSaving() const = 0;
        virtual void set(const QString& group, const QString& key, const QVariant& value) = 0;
        virtual QVariant get(const QString& group, const QString& key) = 0;
        virtual QVariant get(const QString& group, const QString& key, const QVariant& defaultValue) = 0;
        virtual QHash<QString,QVariant> getAll() = 0;

        virtual bool addDb(const QString& name, const QString& path, const QVariantHash& options) = 0;
        virtual bool updateDb(const QString& name, const QString &newName, const QString& path, const QVariantHash& options) = 0;
        virtual bool removeDb(const QString& name) = 0;
        virtual bool isDbInConfig(const QString& name) = 0;
        virtual QString getLastErrorString() const = 0;

        /**
         * @brief Provides list of all registered databases.
         * @return List of database entries.
         *
         * Registered databases are those that user added to the application. They are not necessary valid or supported.
         * They can be inexisting or unsupported, but they are kept in registry in case user fixes file path,
         * or loads plugin to support it.
         */
        virtual QList<CfgDbPtr> dbList() = 0;
        virtual CfgDbPtr getDb(const QString& dbName) = 0;

        virtual void storeGroups(const QList<DbGroupPtr>& groups) = 0;
        virtual QList<DbGroupPtr> getGroups() = 0;
        virtual DbGroupPtr getDbGroup(const QString& dbName) = 0;

        virtual qint64 addSqlHistory(const QString& sql, const QString& dbName, int timeSpentMillis, int rowsAffected) = 0;
        virtual void updateSqlHistory(qint64 id, const QString& sql, const QString& dbName, int timeSpentMillis, int rowsAffected) = 0;
        virtual void clearSqlHistory() = 0;
        virtual void deleteSqlHistory(const QList<qint64>& ids) = 0;
        virtual QAbstractItemModel* getSqlHistoryModel() = 0;

        virtual void addCliHistory(const QString& text) = 0;
        virtual void applyCliHistoryLimit() = 0;
        virtual void clearCliHistory() = 0;
        virtual QStringList getCliHistory() const = 0;

        virtual void addBindParamHistory(const QVector<QPair<QString, QVariant>>& params) = 0;
        virtual void applyBindParamHistoryLimit() = 0;
        virtual QVector<QPair<QString, QVariant>> getBindParamHistory(const QStringList& paramNames) const = 0;

        virtual void addPopulateHistory(const QString& database, const QString& table, int rows, const QHash<QString, QPair<QString, QVariant>>& columnsPluginsConfig) = 0;
        virtual void applyPopulateHistoryLimit() = 0;
        virtual QHash<QString, QPair<QString, QVariant>> getPopulateHistory(const QString& database, const QString& table, int& rows) const = 0;
        virtual QVariant getPopulateHistory(const QString& pluginName) const = 0;

        virtual void addDdlHistory(const QString& queries, const QString& dbName, const QString& dbFile) = 0;
        virtual QList<DdlHistoryEntryPtr> getDdlHistoryFor(const QString& dbName, const QString& dbFile, const QDate& date) = 0;
        virtual DdlHistoryModel* getDdlHistoryModel() = 0;
        virtual void clearDdlHistory() = 0;

        virtual QList<QVariantHash> getScriptFunctions() = 0;
        virtual void setScriptFunctions(const QList<QVariantHash>& newFunctions) = 0;

        virtual void begin() = 0;
        virtual void commit() = 0;
        virtual void rollback() = 0;

        virtual QString getSqlite3Version() const = 0;

        virtual void exportConfig(const QString& filePath, const ExportImportParams& params) = 0;
        virtual void importConfig(const QString& filePath, const ExportImportParams& params) = 0;
        virtual ExportImportParams getParamsForConfigImport(const QString& filePath, QString* errorMsg) = 0;

        virtual bool getConfigMigrated() const = 0;

    signals:
        void massSaveBegins();
        void massSaveCommitted();
        void sqlHistoryRefreshNeeded();
        void ddlHistoryRefreshNeeded();
        void reportsHistoryRefreshNeeded();

    public slots:
        virtual void refreshSqlHistory() = 0;
        virtual void refreshDdlHistory() = 0;

    protected:
        static AskUserForConfigDirFunc askUserForConfigDirFunc;

};

#define CFG LETOS->getConfig()

#endif // CONFIG_H
