# Copyright (c) 2015, JINGROW and Contributors
# License: MIT. See LICENSE

"""assign/unassign to ToDo"""

import json

import jingrow
import jingrow.share
import jingrow.utils
from jingrow import _
from jingrow.desk.pagetype.notification_log.notification_log import (
	enqueue_create_notification,
	get_title,
	get_title_html,
)
from jingrow.desk.form.page_follow import follow_document
from jingrow.utils.data import strip_html


class DuplicateToDoError(jingrow.ValidationError):
	pass


def get(args=None):
	"""get assigned to"""
	if not args:
		args = jingrow.local.form_dict

	return jingrow.get_all(
		"ToDo",
		fields=["allocated_to as owner", "name"],
		filters={
			"reference_type": args.get("pagetype"),
			"reference_name": args.get("name"),
			"status": ("not in", ("Cancelled", "Closed")),
		},
		limit=5,
	)


@jingrow.whitelist()
def add(args=None, *, ignore_permissions=False):
	"""add in someone's to do list
	args = {
	        "assign_to": [],
	        "pagetype": ,
	        "name": ,
	        "description": ,
	        "assignment_rule":
	}

	"""
	if not args:
		args = jingrow.local.form_dict

	users_with_duplicate_todo = []
	shared_with_users = []

	for assign_to in jingrow.parse_json(args.get("assign_to")):
		filters = {
			"reference_type": args["pagetype"],
			"reference_name": args["name"],
			"status": "Open",
			"allocated_to": assign_to,
		}
		if not ignore_permissions:
			jingrow.get_pg(args["pagetype"], args["name"]).check_permission()

		if jingrow.get_all("ToDo", filters=filters):
			users_with_duplicate_todo.append(assign_to)
		else:
			from jingrow.utils import nowdate

			description = str(args.get("description", ""))
			has_content = strip_html(description) or "<img" in description
			if not has_content:
				args["description"] = _("Assignment for {0} {1}").format(args["pagetype"], args["name"])

			d = jingrow.get_pg(
				{
					"pagetype": "ToDo",
					"allocated_to": assign_to,
					"reference_type": args["pagetype"],
					"reference_name": args["name"],
					"description": args.get("description"),
					"priority": args.get("priority", "Medium"),
					"status": "Open",
					"date": args.get("date", nowdate()),
					"assigned_by": args.get("assigned_by", jingrow.session.user),
					"assignment_rule": args.get("assignment_rule"),
				}
			).insert(ignore_permissions=True)

			# set assigned_to if field exists
			if jingrow.get_meta(args["pagetype"]).get_field("assigned_to"):
				jingrow.db.set_value(args["pagetype"], args["name"], "assigned_to", assign_to)

			pg = jingrow.get_pg(args["pagetype"], args["name"])

			# if assignee does not have permissions, share or inform
			if not jingrow.has_permission(pg=pg, user=assign_to):
				if jingrow.get_system_settings("disable_document_sharing"):
					msg = _("User {0} is not permitted to access this document.").format(
						jingrow.bold(assign_to)
					)
					msg += "<br>" + _(
						"As document sharing is disabled, please give them the required permissions before assigning."
					)
					jingrow.throw(msg, title=_("Missing Permission"))
				else:
					jingrow.share.add(pg.pagetype, pg.name, assign_to)
					shared_with_users.append(assign_to)

			# make this document followed by assigned user
			if jingrow.get_cached_value("User", assign_to, "follow_assigned_documents"):
				follow_document(args["pagetype"], args["name"], assign_to)

			# notify
			notify_assignment(
				d.assigned_by,
				d.allocated_to,
				d.reference_type,
				d.reference_name,
				action="ASSIGN",
				description=args.get("description"),
			)

	if shared_with_users:
		user_list = format_message_for_assign_to(shared_with_users)
		jingrow.msgprint(
			_("Shared with the following Users with Read access:{0}").format(user_list, alert=True)
		)

	if users_with_duplicate_todo:
		user_list = format_message_for_assign_to(users_with_duplicate_todo)
		jingrow.msgprint(_("Already in the following Users ToDo list:{0}").format(user_list, alert=True))

	return get(args)


@jingrow.whitelist()
def add_multiple(args=None):
	if not args:
		args = jingrow.local.form_dict

	pagename_list = json.loads(args["name"])

	for pagename in pagename_list:
		args.update({"name": pagename})
		add(args)


def close_all_assignments(pagetype, name, ignore_permissions=False):
	assignments = jingrow.get_all(
		"ToDo",
		fields=["allocated_to", "name"],
		filters=dict(reference_type=pagetype, reference_name=name, status=("!=", "Cancelled")),
	)
	if not assignments:
		return False

	for assign_to in assignments:
		set_status(
			pagetype,
			name,
			todo=assign_to.name,
			assign_to=assign_to.allocated_to,
			status="Closed",
			ignore_permissions=ignore_permissions,
		)

	return True


@jingrow.whitelist()
def remove(pagetype, name, assign_to, ignore_permissions=False):
	return set_status(pagetype, name, "", assign_to, status="Cancelled", ignore_permissions=ignore_permissions)


@jingrow.whitelist()
def remove_multiple(pagetype, names, ignore_permissions=False):
	pagename_list = json.loads(names)

	for name in pagename_list:
		assignments = get({"pagetype": pagetype, "name": name})

		if not assignments:
			continue

		for assignment in assignments:
			remove(pagetype, name, assignment.get("owner"), ignore_permissions)


@jingrow.whitelist()
def close(pagetype: str, name: str, assign_to: str, ignore_permissions=False):
	if assign_to != jingrow.session.user:
		jingrow.throw(_("Only the assignee can complete this to-do."))

	return set_status(pagetype, name, "", assign_to, status="Closed", ignore_permissions=ignore_permissions)


def set_status(pagetype, name, todo=None, assign_to=None, status="Cancelled", ignore_permissions=False):
	"""remove from todo"""

	if not ignore_permissions:
		jingrow.get_pg(pagetype, name).check_permission()
	try:
		if not todo:
			todo = jingrow.db.get_value(
				"ToDo",
				{
					"reference_type": pagetype,
					"reference_name": name,
					"allocated_to": assign_to,
					"status": ("!=", status),
				},
			)
		if todo:
			todo = jingrow.get_pg("ToDo", todo)
			todo.status = status
			todo.save(ignore_permissions=True)

			notify_assignment(todo.assigned_by, todo.allocated_to, todo.reference_type, todo.reference_name)
	except jingrow.DoesNotExistError:
		pass

	# clear assigned_to if field exists
	if jingrow.get_meta(pagetype).get_field("assigned_to") and status in ("Cancelled", "Closed"):
		jingrow.db.set_value(pagetype, name, "assigned_to", None)

	return get({"pagetype": pagetype, "name": name})


def clear(pagetype, name, ignore_permissions=False):
	"""
	Clears assignments, return False if not assigned.
	"""
	assignments = jingrow.get_all(
		"ToDo",
		fields=["allocated_to", "name"],
		filters=dict(reference_type=pagetype, reference_name=name),
	)
	if not assignments:
		return False

	for assign_to in assignments:
		set_status(
			pagetype,
			name,
			todo=assign_to.name,
			assign_to=assign_to.allocated_to,
			status="Cancelled",
			ignore_permissions=ignore_permissions,
		)

	return True


def notify_assignment(assigned_by, allocated_to, pg_type, pg_name, action="CLOSE", description=None):
	"""
	Notify assignee that there is a change in assignment
	"""
	if not (assigned_by and allocated_to and pg_type and pg_name):
		return

	assigned_user = jingrow.db.get_value("User", allocated_to, ["language", "enabled"], as_dict=True)

	# return if self assigned or user disabled
	if assigned_by == allocated_to or not assigned_user.enabled:
		return

	# Search for email address in description -- i.e. assignee
	user_name = jingrow.get_cached_value("User", jingrow.session.user, "full_name")
	title = get_title(pg_type, pg_name)
	description_html = f"<div>{description}</div>" if description else None

	if action == "CLOSE":
		subject = _("Your assignment on {0} {1} has been removed by {2}", lang=assigned_user.language).format(
			jingrow.bold(_(pg_type)), get_title_html(title), jingrow.bold(user_name)
		)
	else:
		user_name = jingrow.bold(user_name)
		document_type = jingrow.bold(_(pg_type, lang=assigned_user.language))
		title = get_title_html(title)
		subject = _("{0} assigned a new task {1} {2} to you", lang=assigned_user.language).format(
			user_name, document_type, title
		)

	notification_pg = {
		"type": "Assignment",
		"document_type": pg_type,
		"subject": subject,
		"document_name": pg_name,
		"from_user": jingrow.session.user,
		"email_content": description_html,
	}

	enqueue_create_notification(allocated_to, notification_pg)


def format_message_for_assign_to(users):
	return "<br><br>" + "<br>".join(users)
