/*
 * Copyright (C) 2014-2022 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 <QList>
#include <QObject>

#include "src/datovka_shared/io/sqlite/db_single.h"
#include "src/tags/tag_assignment.h" /* Because it is passed via a signal. */
#include "src/tags/tag_entry.h"

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

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

	/*!
	 * @brief Insert new tag into database file.
	 *
	 * @note Emits tagInserted() when new tag inserted.
	 *
	 * @param[in] tagName Text label of the tag.
	 * @param[in] tagColour Colour of the tag in HEX format.
	 * @return True on success, false on any error.
	 */
	bool insertTag(const QString &tagName, const QString &tagColour);

	/*!
	 * @brief Update tag in database file.
	 *
	 * @note Emits tagUpdated() when tag updated.
	 *
	 * @param[in] id ID of the tag to be modified.
	 * @param[in] tagName Text label of the tag.
	 * @param[in] tagColour Colour of the tag in HEX format.
	 * @return True on success, false on any error.
	 */
	bool updateTag(int id, const QString &tagName, const QString &tagColour);

	/*!
	 * @brief Delete tag from database file.
	 *
	 * @note Emits tagDeleted() when tag deleted.
	 *
	 * @param[in] ids List of tag identifiers.
	 * @return True on success, false on any error.
	 */
	bool deleteTags(QList<int> ids);

	/*!
	 * @brief Delete all tags from database file.
	 *
	 * @note Emits tagDeleted() when tags deleted.
	 *
	 * @return True on success, false on any error.
	 */
	bool deleteAllTags(void);

	/*!
	 * @brief Get tag data from database file.
	 *
	 * @param[in] id Tag identifier.
	 * @return Tag data.
	 */
	TagEntry getTagData(int id) const;

	/*!
	 * @brief Get all tags from database file.
	 *
	 * @return List of tag entries.
	 */
	QList<TagEntry> getAllTags(void) const;

	/*!
	 * @brief Get number of assignments of given tag.
	 *
	 * @param[in] tagId Tag identifier.
	 * @return Number of messages the tag is assigned to, -1 on any error.
	 */
	int getTagAssignmentCount(int tagId) const;

	/*!
	 * @brief Get all tags related to given message.
	 *
	 * @param[in] userName Account identifier.
	 * @param[in] msgId Message identifier.
	 * @return List of tags related to message.
	 */
	QList<TagEntry> getMessageTags(const QString &userName,
	    const quint64 msgId) const;

	/*!
	 * @brief Delete all tags for message ID
	 *        in message_tags table.
	 *
	 * @note Emits tagAssignmentChanged() when tag assignment changes.
	 *
	 * @param[in] userName Account identifier.
	 * @param[in] testing True for a testing environment account.
	 * @param[in] msgId ID of the message.
	 * @return True on success, false on any error.
	 */
	bool removeAllTagsFromMsg(const QString &userName, bool testing,
	    qint64 msgId);

	/*!
	 * @brief Assign existing tag to message.
	 *
	 * @note Emits tagAssignmentChanged() when tag assignment changes.
	 *
	 * @param[in] userName Account identifier.
	 * @param[in] testing True for a testing environment account.
	 * @param[in] msgId Message identifier.
	 * @param[in] tagIds List of tag identifiers.
	 * @return True on success, false on any error.
	 */
	bool assignTagToMsg(const QString &userName, bool testing,
	    qint64 msgId, QList<int> tagIds);

	/*!
	 * @brief Remove tag from message.
	 *
	 * @note Emits tagAssignmentChanged() when tag assignment changes.
	 *
	 * @param[in] userName Account identifier.
	 * @param[in] testing True for a testing environment account.
	 * @param[in] msgId Message identifier.
	 * @param[in] tagIds List of tag identifiers.
	 * @return True on success, false on any error.
	 */
	bool removeTagFromMsg(const QString &userName, bool testing,
	    qint64 msgId, QList<int> tagIds);

	/*!
	 * @brief Remove tag from all messages in account specified by username.
	 *
	 * @note Emits tagAssignmentChanged() when tag assignment changes.
	 *
	 * @param[in] userName Account identifier.
	 * @param[in] testing True for a testing environment account.
	 * @return True on success, false on any error.
	 */
	bool removeAllMsgTagsFromAccount(const QString &userName, bool testing);

	/*!
	 * @brief Get message IDs related from tag containing sought text.
	 *
	 * @param[in] text Text to search for in the tag names.
	 * @return List of message IDs related to tag text.
	 */
	QList<qint64> getMsgIdsContainSearchTagText(const QString &text) const;

signals:
	/*!
	 * @brief Emitted when new tag has been inserted into the database.
	 *
	 * @param[in] entries Newly created tag entries.
	 */
	void tagInserted(const QList<TagEntry> &entries);

	/*!
	 * @brief Emitted when tags have been updated.
	 *
	 * @param[in] entries Updated tag entries.
	 */
	void tagUpdated(const QList<TagEntry> &entries);

	/*!
	 * @brief Emitted when tags have been deleted.
	 *
	 * @param[in] ids Deleted tag identifiers.
	 */
	void tagDeleted(const QList<int> &ids);

	/*!
	 *  @brief Emitted when tag to message assignment has changed.
	 *
	 *  @param[in] assignments List of modified tag assignments.
	 */
	void tagAssignmentChanged(const QList<TagAssignment> &assignments);

protected:
	/*!
	 * @brief Returns list of tables.
	 *
	 * @return List of pointers to tables.
	 */
	virtual
	QList<class SQLiteTbl *> listOfTables(void) const 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;
};
