import jingrow
from jingrow import _
from jingrow.core.utils import get_parent_pg
from jingrow.desk.pagetype.notification_settings.notification_settings import (
	is_email_notifications_enabled_for_type,
)
from jingrow.desk.pagetype.todo.todo import ToDo
from jingrow.email.pagetype.email_account.email_account import EmailAccount
from jingrow.utils import get_formatted_email, get_url, parse_addr


class EmailMixin:
	"""Mixin class to handle email mails."""

	def get_owner(self):
		"""Get owner of the communication pgs parent."""
		parent_pg = get_parent_pg(self)
		return parent_pg.owner if parent_pg else None

	def get_all_email_addresses(self, exclude_displayname=False):
		"""Get all Email addresses mentioned in the pg along with display name."""
		return (
			self.to_list(exclude_displayname=exclude_displayname)
			+ self.cc_list(exclude_displayname=exclude_displayname)
			+ self.bcc_list(exclude_displayname=exclude_displayname)
		)

	def get_email_with_displayname(self, email_address):
		"""Returns email address after adding displayname."""
		display_name, email = parse_addr(email_address)
		if display_name and display_name != email:
			return email_address

		# emailid to emailid with display name map.
		email_map = {parse_addr(email)[1]: email for email in self.get_all_email_addresses()}
		return email_map.get(email, email)

	def mail_recipients(self, is_inbound_mail_communcation=False):
		"""Build to(recipient) list to send an email."""
		# Incase of inbound mail, recipients already received the mail, no need to send again.
		if is_inbound_mail_communcation:
			return []

		if hasattr(self, "_final_recipients"):
			return self._final_recipients

		to = self.to_list()
		self._final_recipients = list(filter(lambda id: id != "Administrator", to))
		return self._final_recipients

	def get_mail_recipients_with_displayname(self, is_inbound_mail_communcation=False):
		"""Build to(recipient) list to send an email including displayname in email."""
		to_list = self.mail_recipients(is_inbound_mail_communcation=is_inbound_mail_communcation)
		return [self.get_email_with_displayname(email) for email in to_list]

	def mail_cc(self, is_inbound_mail_communcation=False, include_sender=False):
		"""Build cc list to send an email.

		* if email copy is requested by sender, then add sender to CC.
		* If this pg is created through inbound mail, then add pg owner to cc list
		* remove all the thread_notify disabled users.
		* Remove standard users from email list
		"""
		if hasattr(self, "_final_cc"):
			return self._final_cc

		cc = self.cc_list()

		if include_sender:
			sender = self.sender_mailid
			# if user has selected send_me_a_copy, use their email as sender
			if jingrow.session.user not in jingrow.STANDARD_USERS:
				sender = jingrow.db.get_value("User", jingrow.session.user, "email")
			cc.append(sender)

		if is_inbound_mail_communcation:
			# inform parent document owner incase communication is created through inbound mail
			if pg_owner := self.get_owner():
				cc.append(pg_owner)
			cc = set(cc) - {self.sender_mailid}
			assignees = set(self.get_assignees())
			# Check and remove If user disabled notifications for incoming emails on assigned document.
			for assignee in assignees.copy():
				if not is_email_notifications_enabled_for_type(assignee, "threads_on_assigned_document"):
					assignees.remove(assignee)
			cc.update(assignees)

		cc = set(cc) - set(self.filter_thread_notification_disbled_users(cc))
		cc = cc - set(self.mail_recipients(is_inbound_mail_communcation=is_inbound_mail_communcation))

		# # Incase of inbound mail, to and cc already received the mail, no need to send again.
		if is_inbound_mail_communcation:
			cc = cc - set(self.cc_list() + self.to_list())

		self._final_cc = [m for m in cc if m and m not in jingrow.STANDARD_USERS]
		return self._final_cc

	def get_mail_cc_with_displayname(self, is_inbound_mail_communcation=False, include_sender=False):
		cc_list = self.mail_cc(
			is_inbound_mail_communcation=is_inbound_mail_communcation, include_sender=include_sender
		)
		return [self.get_email_with_displayname(email) for email in cc_list if email]

	def mail_bcc(self, is_inbound_mail_communcation=False):
		"""
		* Thread_notify check
		* Email unsubscribe list
		* remove standard users.
		"""
		if hasattr(self, "_final_bcc"):
			return self._final_bcc

		bcc = set(self.bcc_list())
		if is_inbound_mail_communcation:
			bcc = bcc - {self.sender_mailid}
		bcc = bcc - set(self.filter_thread_notification_disbled_users(bcc))
		bcc = bcc - set(self.mail_recipients(is_inbound_mail_communcation=is_inbound_mail_communcation))

		# Incase of inbound mail, to and cc & bcc already received the mail, no need to send again.
		if is_inbound_mail_communcation:
			bcc = bcc - set(self.bcc_list() + self.to_list())

		self._final_bcc = [m for m in bcc if m not in jingrow.STANDARD_USERS]
		return self._final_bcc

	def get_mail_bcc_with_displayname(self, is_inbound_mail_communcation=False):
		bcc_list = self.mail_bcc(is_inbound_mail_communcation=is_inbound_mail_communcation)
		return [self.get_email_with_displayname(email) for email in bcc_list if email]

	def mail_sender(self):
		email_account = self.get_outgoing_email_account()
		if not self.sender_mailid and email_account:
			return email_account.email_id
		return self.sender_mailid

	def mail_sender_fullname(self):
		email_account = self.get_outgoing_email_account()
		if not self.sender_full_name:
			return (email_account and email_account.name) or _("Notification")
		return self.sender_full_name

	def get_mail_sender_with_displayname(self):
		return get_formatted_email(self.mail_sender_fullname(), mail=self.mail_sender())

	def get_content(self, print_format=None):
		if print_format and jingrow.db.get_single_value("System Settings", "attach_view_link"):
			return self.content + self.get_attach_link(print_format)
		return self.content

	def get_attach_link(self, print_format):
		"""Returns public link for the attachment via `templates/emails/print_link.html`."""
		return jingrow.get_template("templates/emails/print_link.html").render(
			{
				"url": get_url(),
				"pagetype": self.reference_pagetype,
				"name": self.reference_name,
				"print_format": print_format,
				"key": get_parent_pg(self).get_document_share_key(),
			}
		)

	def get_outgoing_email_account(self):
		if not hasattr(self, "_outgoing_email_account"):
			if self.email_account:
				self._outgoing_email_account = EmailAccount.find(self.email_account)
			else:
				self._outgoing_email_account = EmailAccount.find_outgoing(
					match_by_email=self.sender_mailid, match_by_pagetype=self.reference_pagetype
				)

				if self.sent_or_received == "Sent" and self._outgoing_email_account:
					if jingrow.db.exists("Email Account", self._outgoing_email_account.name):
						self.db_set("email_account", self._outgoing_email_account.name)

		return self._outgoing_email_account

	def get_incoming_email_account(self):
		if not hasattr(self, "_incoming_email_account"):
			self._incoming_email_account = EmailAccount.find_incoming(
				match_by_email=self.sender_mailid, match_by_pagetype=self.reference_pagetype
			)
		return self._incoming_email_account

	def mail_attachments(self, print_format=None, print_html=None, print_language=None):
		final_attachments = []

		if print_format or print_html:
			d = {
				"print_format": print_format,
				"html": print_html,
				"print_format_attachment": 1,
				"pagetype": self.reference_pagetype,
				"name": self.reference_name,
				"lang": print_language or jingrow.local.lang,
			}
			final_attachments.append(d)

		final_attachments.extend({"fid": a["name"]} for a in self.get_attachments() or [])
		return final_attachments

	def get_unsubscribe_message(self):
		email_account = self.get_outgoing_email_account()
		if email_account and email_account.send_unsubscribe_message:
			return _("Leave this conversation")
		return ""

	def exclude_emails_list(self, is_inbound_mail_communcation=False, include_sender=False) -> list:
		"""List of mail id's excluded while sending mail."""
		all_ids = self.get_all_email_addresses(exclude_displayname=True)

		final_ids = (
			self.mail_recipients(is_inbound_mail_communcation=is_inbound_mail_communcation)
			+ self.mail_bcc(is_inbound_mail_communcation=is_inbound_mail_communcation)
			+ self.mail_cc(
				is_inbound_mail_communcation=is_inbound_mail_communcation, include_sender=include_sender
			)
		)

		return list(set(all_ids) - set(final_ids))

	def get_assignees(self):
		"""Get owners of the reference document."""
		filters = {
			"status": "Open",
			"reference_name": self.reference_name,
			"reference_type": self.reference_pagetype,
		}

		if self.reference_pagetype and self.reference_name:
			return ToDo.get_owners(filters)
		else:
			return []

	@staticmethod
	def filter_thread_notification_disbled_users(emails):
		"""Filter users based on notifications for email threads setting is disabled."""
		if not emails:
			return []

		return jingrow.get_all("User", pluck="email", filters={"email": ["in", emails], "thread_notify": 0})

	@staticmethod
	def filter_disabled_users(emails):
		""" """
		if not emails:
			return []

		return jingrow.get_all("User", pluck="email", filters={"email": ["in", emails], "enabled": 0})

	def sendmail_input_dict(
		self,
		print_html=None,
		print_format=None,
		send_me_a_copy=None,
		print_letterhead=None,
		is_inbound_mail_communcation=None,
		print_language=None,
	) -> dict:
		outgoing_email_account = self.get_outgoing_email_account()
		if not outgoing_email_account:
			return {}

		recipients = self.get_mail_recipients_with_displayname(
			is_inbound_mail_communcation=is_inbound_mail_communcation
		)
		cc = self.get_mail_cc_with_displayname(
			is_inbound_mail_communcation=is_inbound_mail_communcation, include_sender=send_me_a_copy
		)
		bcc = self.get_mail_bcc_with_displayname(is_inbound_mail_communcation=is_inbound_mail_communcation)

		if not (recipients or cc):
			return {}

		final_attachments = self.mail_attachments(
			print_format=print_format, print_html=print_html, print_language=print_language
		)
		incoming_email_account = self.get_incoming_email_account()
		return {
			"recipients": recipients,
			"cc": cc,
			"bcc": bcc,
			"expose_recipients": "header",
			"sender": self.get_mail_sender_with_displayname(),
			"reply_to": incoming_email_account and incoming_email_account.email_id,
			"subject": self.subject,
			"content": self.get_content(print_format=print_format),
			"reference_pagetype": self.reference_pagetype,
			"reference_name": self.reference_name,
			"attachments": final_attachments,
			"message_id": self.message_id,
			"unsubscribe_message": self.get_unsubscribe_message(),
			"delayed": True,
			"email": self.name,
			"read_receipt": self.read_receipt,
			"is_notification": (self.sent_or_received == "Received" and True) or False,
			"print_letterhead": print_letterhead,
			"send_after": self.send_after,
		}

	def send_email(
		self,
		print_html=None,
		print_format=None,
		send_me_a_copy=None,
		print_letterhead=None,
		is_inbound_mail_communcation=None,
		print_language=None,
		now=False,
	):
		if input_dict := self.sendmail_input_dict(
			print_html=print_html,
			print_format=print_format,
			send_me_a_copy=send_me_a_copy,
			print_letterhead=print_letterhead,
			is_inbound_mail_communcation=is_inbound_mail_communcation,
			print_language=print_language,
		):
			jingrow.sendmail(now=now, **input_dict)
