/*
 * 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 <QDialog>
#include <QItemSelection>
#include <QList>
#include <QMap>
#include <QSet>

#include "src/datovka_shared/identifiers/account_id.h"
#include "src/datovka_shared/io/draft_db.h" /* DraftEntry */
#include "src/datovka_shared/isds/message_interface.h"
#include "src/delegates/progress_delegate.h"
#include "src/identifiers/account_id_db.h"
#include "src/models/attachments_model.h"
#include "src/models/data_box_contacts_model.h"
#include "src/models/progress_proxy_model.h"
#include "src/worker/task_send_message.h"

class MessageDb; /* Forward declaration. */
class MessageDbSet; /* Forward declaration. */
class MsgId; /* Forward declaration. */
class QPrinter; /* Forward declaration. */

namespace Ui {
	class DlgSendMessage;
}

namespace Isds {
	class DmAtt;
	class DmExtFile;
}

namespace Json {
	class DmDraft;
}

/*!
 * @brief Send message dialogue.
 */
class DlgSendMessage : public QDialog {
	Q_OBJECT

public:
	/*!
	 * @brief Action to be performed.
	 */
	enum Action {
		ACT_NEW, /* Create new message. */
		ACT_REPLY, /* Fill dialogue as a reply on a message. */
		ACT_FORWARD, /* Forward supplied messages as ZFO attachments. */
		ACT_NEW_FROM_TMPLT, /* Use existing message as a template. */
		ACT_NEW_FROM_DRAFT /* Fill data from draft. */
	};

	/*!
	 * @brief Constructor.
	 *
	 * @param[in] acntIdDbList List of available accounts.
	 * @param[in] action What king of action should be performed.
	 * @param[in] msgIds  List of messages used to fill the dialogue.
	 * @param[in] acntId The account the dialogue has been invoked from.
	 * @param[in] composeSerialised Serialised content data.
	 * @param[in] draftEntry Draft entry.
	 * @param[in] mw Pointer to main window.
	 * @param[in] parent Parent widget.
	 */
	DlgSendMessage(const QList<AcntIdDb> &acntIdDbList, enum Action action,
	    const QList<MsgId> &msgIds, const AcntId &acntId,
	    const QString &composeSerialised, const DraftEntry &draftEntry,
	    class MainWindow *mw, QWidget *parent = Q_NULLPTR);

	/*!
	 * @brief Destructor.
	 */
	virtual
	~DlgSendMessage(void);

protected:
	/*!
	 * @brief Check window geometry and set table columns width.
	 *
	 * @param[in] event Widget show event.
	 */
	virtual
	void showEvent(QShowEvent *event) Q_DECL_OVERRIDE;

public Q_SLOTS:
	/*!
	 * @brief Check if a worker is working on the background and show
	 *     a dialogue whether the user wants to close this dialogue.
	 *
	 * @note Used instead of closeEvent().
	 *     https://stackoverflow.com/questions/13129380/show-a-dialog-before-closing-the-program-in-qt
	 *
	 * @param[in] resultCode Dialog result code.
	 */
	virtual
	void done(int resultCode) Q_DECL_OVERRIDE;

private Q_SLOTS:
	/*!
	 * @brief Enable/disable author info tab.
	 */
	void dmPublishOwnIdStateChanged(int state);

	/*!
	 * @brief Disable records management upload if immediate download is disabled.
	 */
	void immDownloadStateChanged(int state);

	/*!
	 * @brief Enable immediate download if records management upload is enabled.
	 */
	void immRecMgmtUploadStateChanged(int state);

	/*!
	 * @brief Check input fields' sanity and activate search button.
	 */
	void checkInputFields(void);

	/*!
	 * @brief Add recipient from contacts held in database.
	 */
	void addRecipientFromLocalContact(void);

	/*!
	 * @brief Add recipients found via ISDS search.
	 */
	void addRecipientFromISDSSearch(void);

	/*!
	 * @brief Manually add a data box identifier.
	 */
	void addRecipientManually(void);

	/*!
	 * @brief Activates control elements based on selected recipients.
	 *
	 * @param[in] selected Newly selected items.
	 * @param[in] deselected Deselected items.
	 */
	void recipientSelectionChanged(const QItemSelection &selected,
	    const QItemSelection &deselected);

	/*!
	 * @brief Deletes selected row from the recipient list.
	 */
	void deleteRecipientEntries(void);

	/*!
	 * @brief Show/hide optional form elements.
	 */
	void showOptionalFormElements(void);

	/*!
	 * @brief Add attachment file.
	 */
	void addAttachmentFile(void);

	/*!
	 * @brief Activates control elements based on selected attachments.
	 *
	 * @param[in] selected Newly selected items.
	 * @param[in] deselected Deselected items.
	 */
	void attachmentSelectionChanged(const QItemSelection &selected,
	    const QItemSelection &deselected);

	/*!
	 * @brief Remove selected attachment entries.
	 */
	void deleteSelectedAttachmentFiles(void);

	/*!
	 * @brief Open attachment in default application.
	 *
	 * @param[in] index Index identifying the line. If invalid index passed
	 *                  then selected item is taken from the selection
	 *                  model.
	 */
	void openSelectedAttachment(const QModelIndex &index = QModelIndex());

	/*!
	 * @brief Select text message tab.
	 */
	void selectTabTextMessage(void);

	/*!
	 * @brief Set account information and database for selected account.
	 *
	 * @param[in] fromComboIdx Index of selected 'From' combo box item.
	 */
	void setAccountInfo(int fromComboIdx);

	/*!
	 * @brief Send message/multiple messages.
	 */
	void sendMessage(void);

	/*!
	 * @brief Gathers status after sending messages via ISDS interface.
	 *     Shows result table if all data collected.
	 *
	 * @param[in] acntId Account identifier.
	 * @param[in] transactId Unique transaction identifier.
	 * @param[in] result Sending status.
	 * @param[in] resultDesc Result description string.
	 * @param[in] dbIDRecipient Recipient data box identifier.
	 * @param[in] recipientName Recipient data box name.
	 * @param[in] isPDZ True if message was attempted to send as commercial
	 *                  message.
	 * @param[in] isVodz True if message was a high-volume message.
	 * @param[in] dmId Sent message identifier.
	 * @param[in] processFlags Message processig flags.
	 * @param[in] recMgmtHierarchyId Predefined target record management hierarchy id.
	 */
	void collectSendMessageStatus(const AcntId &acntId,
	    const QString &transactId, int result, const QString &resultDesc,
	    const QString &dbIDRecipient, const QString &recipientName,
	    bool isPDZ, bool isVodz, qint64 dmId, int processFlags,
	    const QString &recMgmtHierarchyId);

	/*!
	 * @brief Paste copied file(s) into attachment table.
	 */
	void pasteAttachmentsFromClipboard(void);

	/*!
	 * @brief Paste attachment(s) via filter.
	 *
	 * @param[in] dlgPtr Pointer to dialogue window.
	 */
	static
	void pasteAttachmentsViaFilter(QObject *dlgPtr);

	/*!
	 * @brief Append PDF to attachments.
	 */
	void appendPdf(void);

	/*!
	 * @brief Shows a modification warning when PDF source modified after
	 *     it has been appended into attachments.
	 */
	void watchAppendedPdfModification(void);

	/*!
	 * @brief Show preview dialogue with attachment content to be used
	 *     to generate a PDF attachment.
	 */
	void preview(void);

	/*!
	 * @brief Fill preview dialogue content.
	 *
	 * @param[in,out] printer Printer device.
	 */
	void previewGenerate(QPrinter *printer);

	/*!
	 * @brief View PDF.
	 */
	void viewPdf(void);

	/*!
	 * @brief Text message content change.
	 */
	void textMessageChanged(void);

	/*!
	 * @brief Watch uploaded data.
	 *
	 * @param[in] acntId Account identifier.
	 * @param[in] transactId Transaction identifier.
	 * @param[in] uploadTotal Expected total upload.
	 * @param[in] uploadCurrent Cumulative current upload progress.
	 */
	void watchUploadProgress(const AcntId &acntId,
	    const QString &transactId, qint64 uploadTotal, qint64 uploadCurrent);

	/*!
	 * @brief Watch finished uploads.
	 *
	 * @param[in] acntId Account identifier.
	 * @param[in] transactId Transaction identifier.
	 * @param[in] result Return value, depends on the emitter.
	 * @param[in] resultDesc Result description, often the error description.
	 * @param[in] resultVal Result value.
	 */
	void watchUploadProgressFinished(const AcntId &acntId,
	    const QString &transactId, int result, const QString &resultDesc,
	    const QVariant &resultVal);

private:
	/*!
	 * @brief Initialises the dialogue content.
	 *
	 * @param[in] action Specifies how the dialogue should be initialised.
	 * @param[in] msgIds Identifiers of messages to fill the dialogue with.
	 */
	void initContent(enum Action action, const QList<MsgId> &msgIds);

	/*!
	 * @brief Set dialogue content with ZFO attachments.
	 *
	 * @param[in] msgIds Identifiers of messages to fill the dialogue with.
	 */
	void fillContentAsForward(const QList<MsgId> &msgIds);

	/*!
	 * @brief Set the dialogue content as reply.
	 *
	 * @param[in] msgIds Identifiers of messages to fill the dialogue with.
	 */
	void fillContentAsReply(const QList<MsgId> &msgIds);

	/*!
	 * @brief Set the dialogue content according to the envelope.
	 *
	 * @param[in] envel Envelope.
	 */
	void fillContentFromEnvelope(const Isds::Envelope &envel);

	/*!
	 * @brief Set the dialogue content from template message.
	 *
	 * @param[in] msgIds Identifiers of messages to fill the dialogue with.
	 */
	void fillContentFromTemplate(const QList<MsgId> &msgIds);

	/*!
	 * @brief Set the dilaogue content according to serialised compose data.
	 *
	 * @param[in] composeSerialised Serialised CLI::CmdCompose data.
	 */
	void fillContentCompose(const QString &composeSerialised);

	/*!
	 * @brief Set the dialogue content from draft.
	 *
	 * @param[in] draftEntry Draft entry.
	 */
	void fillContentFromDraft(const DraftEntry &draftEntry);

	/*!
	 * @brief Inserts attachment file.
	 *
	 * @param[in] filePath List of file paths.
	 * @param[in] createdFromText True if generated from text.
	 */
	void insertAttachmentFile(const QString &filePath,
	    bool createdFromText = false);

	/*!
	 * @brief Inserts attachment files.
	 *
	 * @param[in] filePaths List of file paths.
	 */
	void insertAttachmentFiles(const QStringList &filePaths);

	/*!
	 * @brief Calculates and shows total attachment size.
	 */
	bool calculateAndShowTotalAttachSize(void);

	/*!
	 * @brief Append data box into recipient list.
	 *
	 * @param[in] boxId Data box identifier.
	 * @param[in] canUseInitReply True if can use prepaid reply commercial messages.
	 */
	void addRecipientBox(const QString &boxId, bool canUseInitReply);

	/*!
	 * @brief Appends data boxes into recipient list.
	 *
	 * @note This is a convenience method that calls addRecipientBox().
	 *
	 * @param[in] boxIds List of data box identifiers.
	 */
	void addRecipientBoxes(const QStringList &boxIds);

	/*!
	 * @brief Queries ISDS for remaining PDZ credit.
	 *
	 * @param[in] acntId Account identifier.
	 * @param[in] dbId Data box identifier.
	 * @return String containing the amount of remaining credit in Czech
	 *         crowns.
	 */
	static
	QString getPDZCreditFromISDS(const AcntId &acntId,
	    const QString &dbId);

	/*!
	 * @brief Sets envelope content according to dialogue content.
	 *
	 * @param[out] envelope Envelope to be set.
	 * @return True on success.
	 */
	bool buildEnvelope(Isds::Envelope &envelope) const;

	/*!
	 * @brief Appends attachments to list of document descriptors.
	 *
	 * @param[out] documents List to append data to.
	 * @return True on success.
	 */
	bool buildDocuments(QList<Isds::Document> &documents) const;

	/*!
	 * @brief Collect draft data from the forms.
	 *
	 * @param[out] dmDraft Draft.
	 * @return True on success.
	 */
	bool buildDraft(Json::DmDraft &dmDraft) const;

	/*!
	 * @brief Save draft message.
	 */
	bool saveDraft(void);

	/*!
	 * @brief Start upload attachment tasks.
	 *
	 * @return True if some uploads could be started.
	 */
	bool startUploadAttachments(void);

	/*!
	 * @brief Collect return values of upload attachment tasks.
	 *
	 * @param[out] dmExtFiles List to append data to.
	 * @return True on success.
	 */
	bool collectUploadAttachments(QList<Isds::DmExtFile> &dmExtFiles) const;

	/*!
	 * @brief Notify user about the sending of a PDZ. Ask for permission.
	 *
	 * @param[in] recipEntries List of recipients.
	 * @return True if granted or none required, false when user want so abort.
	 */
	bool askPermissionToSendPDZ(const QList<BoxContactsModel::PartialEntry> &recipEntries);

	/*!
	 * @brief Send message via standard ISDS interface for third party apps.
	 *
	 * @param[in] recipEntries List of recipients.
	 * @param[in] isVodz True if message is a VoDZ.
	 */
	void sendMessageISDS(
	    const QList<BoxContactsModel::PartialEntry> &recipEntries,
	    bool isVodz);

	/*!
	 * @brief Store PDZ sending info and update window content according
	 *    to the supplied values.
	 */
	void setPDZInfos(const QList<Isds::PDZInfoRec> &pdzInfos);

	/*!
	 * @brief Create path for the generated PDF attachment file.
	 *
	 * @return Path to PDF file in Qt format.
	 */
	QString createdPdfPath(void) const;

	/*!
	 * @brief Create pdf file.
	 *
	 * @return Path where pdf file was created.
	 */
	QString createPdf(void);

	/*!
	 * @brief Pop up confirmation dialogue and delete selected row from
	 *     the recipient list.
	 */
	void askAndDeleteRecipientEntries(void);

	/*!
	 * @brief Delete selected recipient entries.
	 *
	 * @param[in] dlgPtr Pointer to dialogue window.
	 */
	static
	void deleteSelectedRecipientViaFilter(QObject *dlgPtr);

	/*!
	 * @brief Pop up confirmation dialogue and remove selected attachment
	 *     entries.
	 */
	void askAndDeleteSelectedAttachments(void);

	/*!
	 * @brief Delete selected attachment entries.
	 *
	 * @param[in] dlgPtr Pointer to dialogue window.
	 */
	static
	void deleteSelectedAttachmentFilesViaFilter(QObject *dlgPtr);

	Ui::DlgSendMessage *m_ui; /*!< UI generated from UI file. */

	const DraftEntry m_srcDraftEntry; /*!< Source draft entry. */

	const QList<AcntIdDb> m_acntIdDbList; /*!< Available accounts.*/

	AcntId m_acntId; /*!< Selected account. */
	QString m_boxId; /*!< Name of data box associated with selected user. */
	QString m_senderName; /*!< Sender (data box) name. */
	QString m_dbType; /*!< Data box type identifier string. */
	bool m_dbEffectiveOVM; /*! True if selected data box has effective OVM. */
	bool m_dbOpenAddressing; /*! True if selected box has open addressing.  */
	bool m_isLoggedIn; /*!< True if account has already logged in. */
	bool m_isVodz; /*!< True if message will send as big message. */

	QString m_lastAttAddPath; /*! Last attachment location. */
	QString m_pdzCredit; /*! String containing credit value. */

	MessageDbSet *m_dbSet; /*!< Pointer to database container. */

	BoxContactsModel m_recipTableModel; /*!< Data box table model. */
	AttachmentTblModel m_attachModel; /*!< Attachment table model. */

	ProgressBarProxyModel m_recipProgressProxyModel; /*!< Generates progress-bars on top of the recipient model. */
	ProgressDelegate m_recipProgressDelegate; /*!< Draws progress bars into the recipient table view. */

	ProgressBarProxyModel m_attachProgressProxyModel; /*!< Generates progress-bars on top of the attachment model. */
	ProgressDelegate m_attachProgressDelegate; /*!< Draws progress bars into the attachment table view. */

	QString m_recMgmtHierarchyId; /*!< Target records management hierarchy identifier. */

	/* Used to collect sending results. */
	QSet<QString> m_transactIds; /*!< Temporary transaction identifiers. */
	QList<TaskSendMessage::ResultData> m_sentMsgResultList; /*!< Send status list. */

	QList<QString> m_attachsToBeUploaded; /*!< List of transaction identifiers of attachment upload tasks. */
	QMap<QString, Isds::DmExtFile> m_attachsUploaded; /*!< Map of succeeded attachment upload tasks. */
	QMap<QString, QString> m_attachsFailed; /*!< Map failed attachment upload transaction identifiers to error messages. */
	QSet<QString> m_attachsSucceeded; /*!< List of succeeded attachment upload transaction identifiers. */

	QMap<QString, int> m_msgsToBeSent; /*!< Send message transaction id to row number mapping. */
	QMap<QString, QString> m_msgsFailed; /*!< Map failed send message transaction identifiers to error messages. */
	QSet<QString> m_msgsSucceeded; /*!< List of succeeded send message transaction identifiers. */

	enum Isds::Type::DmType m_dmType; /*!< Message type. */
	QString m_dmSenderRefNumber; /*!< Message reference number. */
	QList<Isds::PDZInfoRec> m_pdzInfos; /*!< PDZ info list. */

	QString m_tmpDirPath; /*!< Directory where to store user-generated files, e.g. created PDF attachment. */

	QSize m_dfltSize; /*!< Remembered dialogue default size. */

	class MainWindow *const m_mw; /*!< Pointer to main window. */
};
