/*
 * Copyright (C) 2014-2026 CZ.NIC
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of portions of this program with the
 * OpenSSL library under certain conditions as described in each
 * individual source file, and distribute linked combinations including
 * the two.
 */

#pragma once

#include <QDateTime>
#include <QHash>
#include <QList>
#include <QObject>
#include <QString>

#include "src/datovka_shared/identifiers/account_id.h"
#include "src/datovka_shared/io/sqlite/db_single.h"
#include "src/datovka_shared/json/draft_interface.h"

namespace Json {
	class DbInfo; /* Forward declaration. */
	class DmDraft; /* Forward declaration. */
	class Int64StringList; /* Forward declaration. */
}

class DraftEntry {
public:
	/*
	 * Don't forget to declare types in order to make them work with
	 * the meta-object system.
	 */
	static
	void declareTypes(void);

	DraftEntry(void)
	    : id(-1), acntId(), creationTime(), modificationTime(), dmDraft()
	{ }

	DraftEntry(qint64 i, const AcntId &a,
	    const QDateTime &ct, const QDateTime &mt, const Json::DmDraft &dc)
	    : id(i), acntId(a), creationTime(ct), modificationTime(mt),
	    dmDraft(dc)
	{ }
#ifdef Q_COMPILER_RVALUE_REFS
	DraftEntry(qint64 i, AcntId &&a,
	    QDateTime &&ct, QDateTime &&mt, Json::DmDraft &&dc)
	    : id(i), acntId(::std::move(a)), creationTime(::std::move(ct)),
	    modificationTime(::std::move(mt)), dmDraft(::std::move(dc))
	{ }
#endif /* Q_COMPILER_RVALUE_REFS */

	qint64 id;
	AcntId acntId;
	/* Location key missing. */
	QDateTime creationTime;
	QDateTime modificationTime;
	Json::DmDraft dmDraft;
};

Q_DECLARE_METATYPE(DraftEntry)
Q_DECLARE_METATYPE(QList<DraftEntry>)

class DraftEntryList : public QList<DraftEntry> {
public:
	/*
	 * Don't forget to declare types in order to make them work with
	 * the meta-object system.
	 */
	static
	void declareTypes(void);

	/* Expose list constructors. */
	using QList<DraftEntry>::QList;

	/* Some older compilers complain about missing constructor. */
	DraftEntryList(void);

	DraftEntryList(const QList<DraftEntry> &other);
#ifdef Q_COMPILER_RVALUE_REFS
	DraftEntryList(QList<DraftEntry> &&other);
#endif /* Q_COMPILER_RVALUE_REFS */
};

Q_DECLARE_METATYPE(DraftEntryList)

class DraftCounts : public QHash<AcntId, qint64> {
public:
	/*
	 * Don't forget to declare types in order to make them work with
	 * the meta-object system.
	 */
	static
	void declareTypes(void);
};

Q_DECLARE_METATYPE(DraftCounts)

/*!
 * @brief Encapsulates draft database.
 */
class DraftDb : public QObject, public SQLiteDbSingle {
	Q_OBJECT

public:
	/* Use parent class constructor. */
	using SQLiteDbSingle::SQLiteDbSingle;

	/* Make some inherited methods public. */
	using SQLiteDbSingle::closeDb;

	/*!
	 * @brief Return path to directory where associated files can be stored.
	 *
	 * @note The path may actually not exist if no files are stored there.
	 *
	 * @return String containing a directory path.
	 */
	QString assocFileDirPath(void) const;

	/*!
	 * @brief Get information about the database.
	 *
	 * @param[out] info Database information.
	 * @return True on success, false on any error.
	 */
	bool getDbInfo(Json::DbInfo &info) const;

	/*!
	 * @brief Update database information.
	 *
	 * @param[in] info Database information.
	 * @return True on success, false on any error.
	 */
	bool updateDbInfo(const Json::DbInfo &info);

	/*!
	 * @brief Insert draft.
	 *
	 * @note Emits draftsInserted() and draftCountChanged() when new drafts inserted.
	 *
	 * @param[in] acntId Account identifier.
	 * @param[in] dmDraft Draft data.
	 * @return True on success, false on any error.
	 */
	bool insertDraft(const AcntId &acntId,
	    const Json::DmDraft &dmDraft);

	/*!
	 * @brief Update draft data and/or account identification.
	 *
	 * @note Emits draftsUpdated() when drafts updated and draftCountChanged() when account changed.
	 *
	 * @param[in] id Draft id.
	 * @param[in] newAcntId New account identifier.
	 * @param[in] dmDraft Draft data.
	 * @return True on success, false on any error.
	 */
	bool updateDraft(qint64 id, const AcntId &newAcntId,
	    const Json::DmDraft &dmDraft);

	/*!
	 * @brief Delete drafts from database file.
	 *
	 * @note Emits draftsDeleted() and draftCountChanged() when drafts deleted.
	 *
	 * @param[in] ids List of draft identifiers.
	 * @return True on success, false on any error.
	 */
	bool deleteDrafts(Json::Int64StringList ids);

	/*!
	 * @brief Delete drafts from database file.
	 *
	 * @note Emits draftsDeleted() and draftCountChanged() when drafts deleted.
	 *
	 * @param[in] acntIds Account identifiers.
	 * @return True on success, false on any error.
	 */
	bool deleteDrafts(QList<AcntId> acntIds);

	/*!
	 * @brief Get draft entries.
	 *
	 * @param[in] ids Draft ids.
	 * @param[out] draftEntries Draft data entries.
	 * @return True on success, false on any error.
	 */
	bool getDraftListing(const Json::Int64StringList &ids,
	    QList<DraftEntry> &draftEntries) const;

	/*!
	 * @brief Get draft entries.
	 *
	 * @note Returns all drafts if \a acntIds is empty.
	 *
	 * @param[in] acntIds Account identifiers.
	 * @param[out] draftEntries Draft data entries.
	 * @return True on success, false on any error.
	 */
	bool getDraftListing(const QList<AcntId> &acntIds,
	    QList<DraftEntry> &draftEntries) const;

	/*!
	 * @brief Get draft counts.
	 *
	 * @param[in] acntId Account identifier.
	 * @param[out] draftCount Draft count or respective account.
	 * @return True on success, false on any error.
	 */
	bool getDraftCount(const AcntId &acntId, qint64 &draftCount) const;

	/*!
	 * @brief Get draft counts.
	 *
	 * @param[in] acntIds Account identifiers.
	 * @param[out] draftCounts Draft counts or respective accounts.
	 * @return True on success, false on any error.
	 */
	bool getDraftCounts(const QList<AcntId> &acntIds,
	    DraftCounts &draftCounts) const;

Q_SIGNALS:
	/*!
	 * @brief Emitted when new tags have been inserted into the database.
	 *
	 * @param[in] entries Newly created draft entries.
	 */
	void draftsInserted(const DraftEntryList &entries);

	/*!
	 * @brief Emitted when drafts have been modified in the database.
	 *
	 * @param[in] entries Modified draft entries.
	 */
	void draftsUpdated(const DraftEntryList &entries);

	/*!
	 * @brief Emitted when drafts have been deleted from the database.
	 *
	 * @param[in] ids Deleted draft identifiers.
	 */
	void draftsDeleted(const Json::Int64StringList &ids);

	/*!
	 * @brief Emitted when draft count for account has changed.
	 *
	 * @param[in] draftCounts Draft counts for accounts where it has changed.
	 */
	void draftCountChanged(const DraftCounts &draftCounts);

protected:
	/*!
	 * @brief Return path to directory where associated files can be stored.
	 *
	 * @note The path may actually not exist if no files are stored there.
	 *
	 * @param[in] fileName Database file name.
	 * @return String containing a directory path.
	 */
	static
	QString _assocFileDirPath(QString fileName);

	/*!
	 * @brief Returns list of tables.
	 *
	 * @return List of pointers to tables.
	 */
	virtual
	QList<class SQLiteTbl *> listOfTables(void) const Q_DECL_OVERRIDE;

	/*!
	 * @brief Fixes some database states.
	 *
	 * @brief Adds missing entries or converts database content between
	 *     versions.
	 *
	 * @return True on success.
	 */
	virtual
	bool assureConsistency(void) Q_DECL_OVERRIDE;

	/*!
	 * @brief Enables foreign key support.
	 *
	 * @note Foreign key functionality is not enabled by default at runtime.
	 *     https://sqlite.org/foreignkeys.html
	 *
	 * @return True on success.
	 */
	virtual
	bool enableFunctionality(void) Q_DECL_OVERRIDE;
};
