# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE

from __future__ import annotations

from typing import TYPE_CHECKING, Literal, Optional

import frappe

if TYPE_CHECKING:
	from frappe.email.doctype.email_queue.email_queue import EmailQueue


def sendmail_to_system_managers(subject, content):
	frappe.sendmail(recipients=get_system_managers(), subject=subject, content=content)


@frappe.whitelist()
def get_contact_list(txt: str, page_length: int = 20, extra_filters: str | None = None) -> list[dict]:
	"""Return email ids for a multiselect field."""
	if extra_filters:
		extra_filters = frappe.parse_json(extra_filters)

	filters = [
		["Contact Email", "email_id", "is", "set"],
	]
	if extra_filters:
		filters.extend(extra_filters)

	fields = ["first_name", "middle_name", "last_name", "company_name"]
	contacts = frappe.get_list(
		"Contact",
		fields=["full_name", "`tabContact Email`.email_id"],
		filters=filters,
		or_filters=[[field, "like", f"%{txt}%"] for field in fields]
		+ [["Contact Email", "email_id", "like", f"%{txt}%"]],
		limit_page_length=page_length,
	)

	# The multiselect field will store the `label` as the selected value.
	# The `value` is just used as a unique key to distinguish between the options.
	# https://github.com/frappe/frappe/blob/6c6a89bcdd9454060a1333e23b855d0505c9ebc2/frappe/public/js/frappe/form/controls/autocomplete.js#L29-L35
	return [
		frappe._dict(
			value=d.email_id,
			label=d.email_id,
			description=d.full_name,
		)
		for d in contacts
	]


def get_system_managers():
	return frappe.db.sql_list(
		"""select parent FROM `tabHas Role`
		WHERE role='System Manager'
		AND parent!='Administrator'
		AND parent IN (SELECT email FROM tabUser WHERE enabled=1)"""
	)


@frappe.whitelist()
def relink(name: str, reference_doctype: str | None = None, reference_name: str | None = None):
	frappe.db.sql(
		"""update
			`tabCommunication`
		set
			reference_doctype = %s,
			reference_name = %s,
			status = "Linked"
		where
			communication_type = "Communication" and
			name = %s""",
		(reference_doctype, reference_name, name),
	)


@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_communication_doctype(
	doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: str | list | dict
):
	user_perms = frappe.utils.user.UserPermissions(frappe.session.user)
	user_perms.build_permissions()
	can_read = user_perms.can_read
	from frappe import _
	from frappe.modules import load_doctype_module

	com_doctypes = []
	if len(txt) < 2:
		for name in frappe.get_hooks("communication_doctypes"):
			try:
				module = load_doctype_module(name, suffix="_dashboard")
				if hasattr(module, "get_data"):
					for i in module.get_data()["transactions"]:
						com_doctypes += i["items"]
			except ImportError:
				pass
	else:
		com_doctypes = [
			d[0] for d in frappe.db.get_values("DocType", {"issingle": 0, "istable": 0, "hide_toolbar": 0})
		]

	results = []
	txt_lower = txt.lower().replace("%", "")

	for dt in list(set(com_doctypes)):
		if dt in can_read:
			if txt_lower in dt.lower() or txt_lower in _(dt).lower():
				results.append([dt])

	return results


def sendmail(
	recipients=None,
	sender="",
	subject="No Subject",
	message="No Message",
	as_markdown=False,
	delayed=True,
	reference_doctype=None,
	reference_name=None,
	unsubscribe_method=None,
	unsubscribe_params=None,
	unsubscribe_message=None,
	add_unsubscribe_link=1,
	attachments=None,
	content=None,
	doctype=None,
	name=None,
	reply_to=None,
	queue_separately=False,
	cc=None,
	bcc=None,
	message_id=None,
	in_reply_to=None,
	send_after=None,
	expose_recipients=None,
	send_priority=1,
	communication=None,
	retry=1,
	now=None,
	read_receipt=None,
	is_notification=False,
	inline_images=None,
	template=None,
	args=None,
	header=None,
	print_letterhead=False,
	with_container=False,
	email_read_tracker_url=None,
	x_priority: Literal[1, 3, 5] = 3,
	email_headers=None,
	raw_html=False,
	add_css=True,
) -> EmailQueue | None:
	"""Send email using user's default **Email Account** or global default **Email Account**.


	    :param recipients: List of recipients.
	    :param sender: Email sender. Default is current user or default outgoing account.
	    :param subject: Email Subject.
	    :param message: (or `content`) Email Content.
	    :param as_markdown: Convert content markdown to HTML.
	    :param delayed: Send via scheduled email sender **Email Queue**. Don't send immediately. Default is true
	    :param send_priority: Priority for Email Queue, default 1.
	    :param reference_doctype: (or `doctype`) Append as communication to this DocType.
	    :param reference_name: (or `name`) Append as communication to this document name.
	    :param unsubscribe_method: Unsubscribe url with options email, doctype, name. e.g. `/api/method/unsubscribe`
	    :param unsubscribe_params: Unsubscribe paramaters to be loaded on the unsubscribe_method [optional] (dict).
	    :param attachments: List of attachments.
	    :param reply_to: Reply-To Email Address.
	    :param message_id: Used for threading. If a reply is received to this email, Message-Id is sent back as In-Reply-To in received email.
	    :param in_reply_to: Used to send the Message-Id of a received email back as In-Reply-To.
	    :param send_after: Send after the given datetime.
	    :param expose_recipients: Controls recipient visibility. "header" shows all TO recipients in the To header.
	"footer" adds "This email was sent to..." text in footer. None (default) hides TO recipients from each other.
	Note: CC header is always visible regardless of this setting (as per email semantics).
	    :param communication: Communication link to be set in Email Queue record
	    :param inline_images: List of inline images as {"filename", "filecontent"}. All src properties will be replaced with random Content-Id
	    :param template: Name of html template from templates/emails folder
	    :param args: Arguments for rendering the template
	    :param header: Append header in email
	    :param with_container: Wraps email inside a styled container
	    :param x_priority: 1 = HIGHEST, 3 = NORMAL, 5 = LOWEST
	    :param email_headers: Additional headers to be added in the email, e.g. {"X-Custom-Header": "value"} or {"Custom-Header": "value"}. Automatically prepends "X-" to the header name if not present.
	    :param raw_html: Whether to treat email template as a complete HTML file
	    :param add_css: Whether to add CSS from hooks/email_css to the email template
	"""

	from frappe.utils.jinja import get_email_from_template

	if recipients is None:
		recipients = []
	if cc is None:
		cc = []
	if bcc is None:
		bcc = []

	text_content = None
	if template:
		message, text_content = get_email_from_template(template, args)

	message = content or message

	if as_markdown:
		from frappe.utils import md_to_html

		message = md_to_html(message)

	if not delayed:
		now = True

	from frappe.email.doctype.email_queue.email_queue import QueueBuilder

	builder = QueueBuilder(
		recipients=recipients,
		sender=sender,
		subject=subject,
		message=message,
		text_content=text_content,
		reference_doctype=doctype or reference_doctype,
		reference_name=name or reference_name,
		add_unsubscribe_link=add_unsubscribe_link,
		unsubscribe_method=unsubscribe_method,
		unsubscribe_params=unsubscribe_params,
		unsubscribe_message=unsubscribe_message,
		attachments=attachments,
		reply_to=reply_to,
		cc=cc,
		bcc=bcc,
		message_id=message_id,
		in_reply_to=in_reply_to,
		send_after=send_after,
		expose_recipients=expose_recipients,
		send_priority=send_priority,
		queue_separately=queue_separately,
		communication=communication,
		read_receipt=read_receipt,
		is_notification=is_notification,
		inline_images=inline_images,
		header=header,
		print_letterhead=print_letterhead,
		with_container=with_container,
		email_read_tracker_url=email_read_tracker_url,
		x_priority=x_priority,
		email_headers=email_headers,
		raw_html=raw_html,
		add_css=add_css,
	)

	# build email queue and send the email if send_now is True.

	q = builder.process(send_now=False)
	if now and q:
		frappe.db.after_commit.add(q.send)
	return q
