/*
  SPDX-FileCopyrightText: 2010 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
  SPDX-FileCopyrightText: 2010 Leo Franchi <lfranchi@kde.org>

  SPDX-License-Identifier: LGPL-2.0-or-later
*/

#pragma once

#include "recipient.h"
#include <KMime/Message>

#include "identity/identity.h"
#include <Libkleo/Enum>
#include <QList>
#include <QObject>
#include <QUrl>

class QTimer;
class KJob;
class QWidget;
class MessageDispatcher;

class ComposerViewBaseTest;

namespace Sonnet
{
class DictionaryComboBox;
}

namespace Kleo
{
class ExpiryChecker;
class KeyResolver;
}

class IdentityCombo;
class IdentityManager;
class RecipientsEditor;

namespace MessageComposer
{
class RichTextComposerNg;
class InfoPart;
class GlobalPart;
class ComposerJob;
class AttachmentControllerBase;
class AttachmentModel;
class SignatureController;

/**
 * @brief The ComposerViewBase class
 */
class ComposerViewBase : public QObject
{
    Q_OBJECT
public:
    explicit ComposerViewBase(QObject *parent = nullptr, QWidget *widget = nullptr);
    ~ComposerViewBase() override;

    enum Confirmation {
        LetUserConfirm,
        NoConfirmationNeeded,
    };
    enum MissingAttachment {
        NoMissingAttachmentFound,
        FoundMissingAttachmentAndSending,
        FoundMissingAttachmentAndAddedAttachment,
        FoundMissingAttachmentAndCancel,
    };

    enum FailedType {
        Sending,
        AutoSave,
        Draft,
    };

    /**
     * Set the message to be opened in the composer window, and set the internal data
     * structures to keep track of it.
     */
    void setMessage(const KMime::Message::Ptr &newMsg);

    /**
     * Send the message with the specified method, saving it in the specified folder.
     */
    void send();

    /**
     * Returns true if there is at least one composer job running.
     */
    [[nodiscard]] bool isComposing() const;

    /**
     * Add the given attachment to the message.
     */
    void addAttachment(const QUrl &url, const QString &comment, bool sync);
    void addAttachment(const QString &name, const QString &filename, const QByteArray &data, const QByteArray &mimeType);
    void addAttachmentPart(KMime::Content *part);

    void fillComposer(MessageComposer::ComposerJob *composer);

    /**
     * Header fields in recipients editor.
     */
    [[nodiscard]] QString to() const;
    [[nodiscard]] QString cc() const;
    [[nodiscard]] QString bcc() const;
    [[nodiscard]] QString from() const;
    [[nodiscard]] QString replyTo() const;
    [[nodiscard]] QString subject() const;

    /// \returns the mail id
    ///
    /// The mail id allows to uniquely identify a message composer dialog. It's used
    /// when autosaving a draft and as identifier when sending an email.
    [[nodiscard]] QString mailId() const;

    /// \returns whether the mail id is empty
    [[nodiscard]] bool mailIdIsEmpty() const;

    /// \returns the current identitfy
    [[nodiscard]] KIdentityManagementCore::Identity identity() const;

    /// Set the current identity.
    void setIdentity(const KIdentityManagementCore::Identity &identiy);

    /// The following are for setting the various options and widgets in the composer.
    void setAttachmentModel(MessageComposer::AttachmentModel *model);
    [[nodiscard]] MessageComposer::AttachmentModel *attachmentModel();

    void setAttachmentController(MessageComposer::AttachmentControllerBase *controller);
    [[nodiscard]] MessageComposer::AttachmentControllerBase *attachmentController();

    void setRecipientsEditor(RecipientsEditor *recEditor);
    [[nodiscard]] RecipientsEditor *recipientsEditor();

    void setSignatureController(MessageComposer::SignatureController *sigController);
    [[nodiscard]] MessageComposer::SignatureController *signatureController();

    void setEditor(MessageComposer::RichTextComposerNg *editor);
    [[nodiscard]] MessageComposer::RichTextComposerNg *editor() const;

    [[nodiscard]] Sonnet::DictionaryComboBox *dictionary() const;
    void setDictionary(Sonnet::DictionaryComboBox *dictionary);

    [[nodiscard]] MessageDispatcher *messageDispatcher() const;
    void setMessageDispatcher(MessageDispatcher *messageDispatcher);

    /**
     * Widgets for editing differ in client classes, so
     *  values are set before sending.
     */
    void setFrom(const QString &from);
    void setSubject(const QString &subject);

    /**
     * The following are various settings the user can modify when composing a message. If they are not set,
     *  the default values will be used.
     */
    void setCryptoOptions(bool sign, bool encrypt, Kleo::CryptoMessageFormat format, bool neverEncryptDrafts = false);
    void setMDNRequested(bool mdnRequested);
    void setUrgent(bool urgent);

    void setAutoSaveInterval(int interval);
    void setCustomHeader(const QMap<QByteArray, QString> &customHeader);

    /**
     * Enables/disables autosaving depending on the value of the autosave
     * interval.
     */
    void updateAutoSave();

    /**
     * Sets the filename to use when autosaving something. This is used when the client recovers
     * the autosave files: It calls this method, so that the composer uses the same filename again.
     * That way, the recovered autosave file is properly cleaned up in cleanupAutoSave():
     */
    void setAutoSaveFileName(const QString &fileName);

    /**
     * Stop autosaving and delete the autosaved message.
     */
    void cleanupAutoSave();

    void setParentWidgetForGui(QWidget *);

    /**
     * Check if the mail has references to attachments, but no attachments are added to it.
     * If missing attachments are found, a dialog to add new attachments is shown.
     * @param attachmentKeywords a list with the keywords that indicate an attachment should be present
     * @return NoMissingAttachmentFound, if there is attachment in email
     *         FoundMissingAttachmentAndCancelSending, if mail might miss attachment but sending
     *         FoundMissingAttachmentAndAddedAttachment, if mail might miss attachment and we added an attachment
     *         FoundMissingAttachmentAndCancel, if mail might miss attachment and cancel sending
     */
    [[nodiscard]] ComposerViewBase::MissingAttachment checkForMissingAttachments(const QStringList &attachmentKeywords);

    [[nodiscard]] bool hasMissingAttachments(const QStringList &attachmentKeywords);

    void saveMailSettings();

    [[nodiscard]] QDate followUpDate() const;
    void setFollowUpDate(const QDate &followUpDate);

    void clearFollowUp();

    [[nodiscard]] KMime::Message::Ptr msg() const;

    [[nodiscard]] bool requestDeleveryConfirmation() const;
    void setRequestDeleveryConfirmation(bool requestDeleveryConfirmation);

    [[nodiscard]] std::shared_ptr<Kleo::ExpiryChecker> expiryChecker();

public Q_SLOTS:
    void identityChanged(const KIdentityManagementCore::Identity &ident, const KIdentityManagementCore::Identity &oldIdent, bool msgCleared = false);

    /// Save the message in the autosave folder.
    ///
    /// This message will be reopened automatically in the next section if
    /// it wasn't cleaned up with cleanupAutoSave
    void autoSaveMessage();

    /// Save the message as a draft.
    void slotSaveDraft();

    /// Generate a KMime::Message::Ptr from the current state of the composer
    ///
    /// \param callback The callback will be called when the message is generated.
    void generateMessage(std::function<void(QList<KMime::Message::Ptr>)> callback);

    /// Send the message
    void queueMessage(const KMime::Message::Ptr &message);

Q_SIGNALS:
    void closeWindow();

    /**
     * Message sending failed with given error message.
     */
    void failed(const QString &errorMessage, MessageComposer::ComposerViewBase::FailedType type = Sending);

    /**
     * The composer was modified. This can happen behind the users' back
     *  when, for example, and autosaved message was recovered.
     */
    void modified(bool isModified);

    /**
     * Enabling or disabling HTML in the editor is affected
     *  by various client options, so when that would otherwise happen,
     *  hand it off to the client to enact it for real.
     */
    void disableHtml(MessageComposer::ComposerViewBase::Confirmation);
    void enableHtml();
    void tooManyRecipient(bool);

private Q_SLOTS:
    void slotSendComposeResult(KJob *);
    void slotAutoSaveComposeResult(KJob *job);
    void slotSaveDraftComposeResult(KJob *job);
    void slotComposerCreated();

Q_SIGNALS:
    void composerCreated();

private:
    /**
     * Searches the mime tree, where root is the root node, for embedded images,
     * extracts them froom the body and adds them to the editor.
     */
    void collectImages(KMime::Content *root);
    [[nodiscard]] bool inlineSigningEncryptionSelected() const;
    /**
     * Applies the user changes to the message object of the composer
     * and signs/encrypts the message if activated.
     * Disables the controls of the composer window.
     */
    void readyForSending();

    enum RecipientExpansion {
        UseExpandedRecipients,
        UseUnExpandedRecipients,
    };
    void fillComposer(MessageComposer::ComposerJob *composer, ComposerViewBase::RecipientExpansion expansion, bool autoresize);
    /// Fill the composer with the signing/encryption key of the sender as well as the email
    /// addresses from the recipients.
    [[nodiscard]] Kleo::KeyResolver *fillKeyResolver(bool encryptSomething);

    /// Create one or multiple MessageComposer::Composer depending on the crypto settings
    /// Emits composerCreated when finished.
    void generateCryptoMessages();
    void fillGlobalPart(MessageComposer::GlobalPart *globalPart);
    void fillInfoPart(MessageComposer::InfoPart *part, RecipientExpansion expansion);

    void updateRecipients(const KIdentityManagementCore::Identity &ident, const KIdentityManagementCore::Identity &oldIdent, Recipient::Type type);

    void markAllAttachmentsForSigning(bool sign);
    void markAllAttachmentsForEncryption(bool encrypt);

    /**
     * Writes out autosave data to the disk from the KMime::Message message.
     * Also appends the msgNum to the filename as a message can have a number of
     * KMime::Messages
     */
    void writeAutoSaveToDisk(const KMime::Message::Ptr &message);

    /**
     * Writes out draft data to the disk from the KMime::Message message.
     * Also appends the msgNum to the filename as a message can have a number of
     * KMime::Messages
     */
    void writeDraftToDisk(const KMime::Message::Ptr &message);

    /**
     * Returns the autosave interval in milliseconds (as needed for QTimer).
     */
    int autoSaveInterval() const;

    /**
     * Initialize autosaving (timer and filename).
     */
    void initAutoSave();

    KMime::Message::Ptr m_msg;
    MessageComposer::AttachmentControllerBase *m_attachmentController = nullptr;
    MessageComposer::AttachmentModel *m_attachmentModel = nullptr;
    MessageComposer::SignatureController *m_signatureController = nullptr;
    RecipientsEditor *m_recipientsEditor = nullptr;
    IdentityCombo *m_identityCombo = nullptr;
    KIdentityManagementCore::Identity m_identity;
    MessageComposer::RichTextComposerNg *m_editor = nullptr;
    Sonnet::DictionaryComboBox *m_dictionary = nullptr;
    MessageDispatcher *m_messageDispatcher = nullptr;
    QWidget *m_parentWidget = nullptr;

    // List of active composer jobs. For example, saving as draft, autosaving and printing
    // all create a composer, which is added to this list as long as it is active.
    // Used mainly to prevent closing the window if a composer is active
    QList<MessageComposer::ComposerJob *> m_composers;

    bool m_sign = false;
    bool m_encrypt = false;
    bool m_neverEncrypt = false;
    bool m_mdnRequested = false;
    bool m_urgent = false;
    bool m_requestDeleveryConfirmation = false;
    Kleo::CryptoMessageFormat m_cryptoMessageFormat;
    QString mExpandedFrom;
    QString m_from;
    QString m_subject;
    QStringList mExpandedTo, mExpandedCc, mExpandedBcc, mExpandedReplyTo;
    QMap<QByteArray, QString> m_customHeader;

    int m_pendingQueueJobs = 0;

    QTimer *m_autoSaveTimer = nullptr;
    mutable QString m_autoSaveUUID;
    bool m_autoSaveErrorShown = false; // Stops an error message being shown every time autosave is executed.
    int m_autoSaveInterval;

    std::shared_ptr<Kleo::ExpiryChecker> mExpiryChecker;

    QDate mFollowUpDate;
    QByteArray m_bearerToken;

    friend ComposerViewBaseTest;
};
} // namespace
