/**********************************************************************
 * LeechCraft - modular cross-platform feature rich internet client.
 * Copyright (C) 2006-2014  Georg Rudoy
 *
 * Boost Software License - Version 1.0 - August 17th, 2003
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer,
 * must be included in all copies of the Software, in whole or in part, and
 * all derivative works of the Software, unless such copies or derivative
 * works are solely in the form of machine-executable object code generated by
 * a source language processor.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 **********************************************************************/

#include "messagelistactionsmanager.h"
#include <QUrl>
#include <QMessageBox>
#include <QTextDocument>
#include <QtDebug>
#include <vmime/messageIdSequence.hpp>
#include <util/xpc/util.h>
#include <util/sll/futures.h>
#include <interfaces/core/ientitymanager.h>
#include "core.h"
#include "message.h"
#include "vmimeconversions.h"

namespace LeechCraft
{
namespace Snails
{
	class MessageListActionsProvider
	{
	public:
		virtual QList<MessageListActionInfo> GetMessageActions (const Message_ptr&, Account*) const = 0;
	};

	namespace
	{
		class GithubProvider : public MessageListActionsProvider
		{
		public:
			QList<MessageListActionInfo> GetMessageActions (const Message_ptr& msg, Account*) const override
			{
				const auto& headers = msg->GetVmimeHeader ();
				if (!headers)
					return {};

				const auto header = headers->findField ("X-GitHub-Sender");
				if (!header)
					return {};

				const auto& referencesField = headers->References ();
				if (!referencesField)
					return {};

				const auto& refSeq = referencesField->getValue<vmime::messageIdSequence> ();
				if (!refSeq)
					return {};

				const auto& ref = refSeq->getMessageIdAt (0);
				if (!ref)
					return {};

				const auto& addrReq = QString::fromUtf8 (ref->getLeft ().c_str ());
				if (addrReq.isEmpty ())
					return {};

				return {
						{
							QObject::tr ("Open"),
							QObject::tr ("Open the page on GitHub."),
							QIcon::fromTheme ("document-open"),
							[addrReq] (const Message_ptr&)
							{
								const QUrl fullUrl { "https://github.com/" + addrReq };
								const auto& entity = Util::MakeEntity (fullUrl, {}, FromUserInitiated | OnlyHandle);
								Core::Instance ().GetProxy ()->GetEntityManager ()->HandleEntity (entity);
							}
						}
					};
			}
		};

		class BugzillaProvider : public MessageListActionsProvider
		{
		public:
			QList<MessageListActionInfo> GetMessageActions (const Message_ptr& msg, Account*) const override
			{
				const auto& headers = msg->GetVmimeHeader ();
				if (!headers)
					return {};

				const auto header = headers->findField ("X-Bugzilla-URL");
				if (!header)
					return {};

				const auto& urlText = header->getValue<vmime::text> ();
				if (!urlText)
					return {};

				const auto& url = StringizeCT (*urlText);

				const auto& referencesField = headers->References ();
				if (!referencesField)
					return {};

				const auto& refSeq = referencesField->getValue<vmime::messageIdSequence> ();
				if (!refSeq)
					return {};

				const auto& ref = refSeq->getMessageIdAt (0);
				if (!ref)
					return {};

				const auto& left = QString::fromUtf8 (ref->getLeft ().c_str ());
				const auto bugId = left.section ('-', 1, 1);

				return {
						{
							QObject::tr ("Open"),
							QObject::tr ("Open the bug page on Bugzilla."),
							QIcon::fromTheme ("document-open"),
							[url, bugId] (const Message_ptr&)
							{
								const QUrl fullUrl { url + "show_bug.cgi?id=" + bugId };
								const auto& entity = Util::MakeEntity (fullUrl, {}, FromUserInitiated | OnlyHandle);
								Core::Instance ().GetProxy ()->GetEntityManager ()->HandleEntity (entity);
							}
						}
					};
			}
		};

		class ReviewboardProvider : public MessageListActionsProvider
		{
		public:
			QList<MessageListActionInfo> GetMessageActions (const Message_ptr& msg, Account*) const override
			{
				const auto& headers = msg->GetVmimeHeader ();
				if (!headers)
					return {};

				const auto header = headers->findField ("X-ReviewRequest-URL");
				if (!header)
					return {};

				const auto& urlText = header->getValue<vmime::text> ();
				if (!urlText)
					return {};

				const auto& url = StringizeCT (*urlText);

				return {
						{
							QObject::tr ("Open"),
							QObject::tr ("Open the review page on ReviewBoard."),
							QIcon::fromTheme ("document-open"),
							[url] (const Message_ptr&)
							{
								const auto& entity = Util::MakeEntity (QUrl { url }, {}, FromUserInitiated | OnlyHandle);
								Core::Instance ().GetProxy ()->GetEntityManager ()->HandleEntity (entity);
							}
						}
					};
			}
		};

		QUrl GetUnsubscribeUrl (const QString& text)
		{
			const auto& parts = text.split (',', QString::SkipEmptyParts);

			QUrl email;
			QUrl url;
			for (auto part : parts)
			{
				part = part.simplified ();
				if (part.startsWith ('<'))
					part = part.mid (1, part.size () - 2);

				const auto& ascii = part.toAscii ();

				if (ascii.startsWith ("mailto:"))
				{
					const auto& testEmail = QUrl::fromEncoded (ascii);
					if (testEmail.isValid ())
						email = testEmail;
				}
				else
				{
					const auto& testUrl = QUrl::fromEncoded (ascii);
					if (testUrl.isValid ())
						url = testUrl;
				}
			}

			return email.isValid () ? email : url;
		}

		QString GetListName (const Message_ptr& msg)
		{
			const auto& addrString = "<em>" + Qt::escape (msg->GetAddressString (Message::Address::From)) + "</em>";

			const auto& headers = msg->GetVmimeHeader ();
			if (!headers)
				return addrString;

			const auto header = headers->findField ("List-Id");
			if (!header)
				return addrString;

			const auto& vmimeText = header->getValue<vmime::text> ();
			if (!vmimeText)
				return addrString;

			return "<em>" + Qt::escape (StringizeCT (*vmimeText)) + "</em>";
		}

		void HandleUnsubscribeText (const QString& text, const Message_ptr& msg, Account *acc)
		{
			const auto& url = GetUnsubscribeUrl (text);

			const auto& addrString = GetListName (msg);
			if (url.scheme () == "mailto")
			{
				if (QMessageBox::question (nullptr,
						QObject::tr ("Unsubscription confirmation"),
						QObject::tr ("Are you sure you want to unsubscribe from %1? "
							"This will send an email to %2.")
							.arg (addrString)
							.arg ("<em>" + Qt::escape (url.path ()) + "</em>"),
						QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
					return;

				const auto& msg = std::make_shared<Message> ();
				msg->SetAddress (Message::Address::To, { {}, url.path () });

				const auto& subjQuery = url.queryItemValue ("subject");
				msg->SetSubject (subjQuery.isEmpty () ? "unsubscribe" : subjQuery);

				Util::ExecuteFuture ([acc] (auto msg) { return acc->SendMessage (msg); },
						[url]
						{
							const auto& entity = Util::MakeNotification ("Snails",
									QObject::tr ("Successfully sent unsubscribe request to %1.")
										.arg ("<em>" + url.path () + "</em>"),
									PInfo_);
							Core::Instance ().GetProxy ()->GetEntityManager ()->HandleEntity (entity);
						},
						nullptr,
						msg);
			}
			else
			{
				if (QMessageBox::question (nullptr,
						QObject::tr ("Unsubscription confirmation"),
						QObject::tr ("Are you sure you want to unsubscribe from %1? "
							"This will open the following web page in your browser: %2")
							.arg (addrString)
							.arg ("<br/><em>" + url.toString () + "</em>"),
						QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
					return;

				const auto& entity = Util::MakeEntity (url, {}, FromUserInitiated | OnlyHandle);
				Core::Instance ().GetProxy ()->GetEntityManager ()->HandleEntity (entity);
			}
		}

		class UnsubscribeProvider : public MessageListActionsProvider
		{
		public:
			QList<MessageListActionInfo> GetMessageActions (const Message_ptr& msg, Account *acc) const override
			{
				const auto& headers = msg->GetVmimeHeader ();
				if (!headers)
					return {};

				const auto header = headers->findField ("List-Unsubscribe");
				if (!header)
					return {};

				return {
						{
							QObject::tr ("Unsubscribe"),
							QObject::tr ("Try canceling receiving further messages like this."),
							QIcon::fromTheme ("news-unsubscribe"),
							[header, acc] (const Message_ptr& msg)
							{
								const auto& vmimeText = header->getValue<vmime::text> ();
								if (!vmimeText)
									return;

								HandleUnsubscribeText (StringizeCT (*vmimeText), msg, acc);
							}
						}
					};
			}
		};
	}

	MessageListActionsManager::MessageListActionsManager (Account *acc, QObject *parent)
	: QObject { parent }
	, Acc_ { acc }
	{
		Providers_ << std::make_shared<GithubProvider> ();
		Providers_ << std::make_shared<BugzillaProvider> ();
		Providers_ << std::make_shared<ReviewboardProvider> ();
		Providers_ << std::make_shared<UnsubscribeProvider> ();
	}

	QList<MessageListActionInfo> MessageListActionsManager::GetMessageActions (const Message_ptr& msg) const
	{
		QList<MessageListActionInfo> result;
		for (const auto provider : Providers_)
			result += provider->GetMessageActions (msg, Acc_);
		return result;
	}
}
}
