# Copyright (c) 2018, JINGROW
# License: MIT. See LICENSE

import json

import jingrow
from jingrow import _
from jingrow.desk.pagetype.notification_log.notification_log import (
	enqueue_create_notification,
	get_title,
	get_title_html,
)
from jingrow.desk.pagetype.notification_settings.notification_settings import (
	is_email_notifications_enabled,
	is_email_notifications_enabled_for_type,
)
from jingrow.model.page import Page
from jingrow.utils import cint, get_fullname, get_link_to_form, getdate


class EnergyPointLog(Page):
	# begin: auto-generated types
	# This code is auto-generated. Do not modify anything in this block.

	from typing import TYPE_CHECKING

	if TYPE_CHECKING:
		from jingrow.types import DF

		points: DF.Int
		reason: DF.Text | None
		reference_pagetype: DF.Link | None
		reference_name: DF.Data | None
		revert_of: DF.Link | None
		reverted: DF.Check
		rule: DF.Link | None
		seen: DF.Check
		type: DF.Literal["Auto", "Appreciation", "Criticism", "Review", "Revert"]
		user: DF.Link

	# end: auto-generated types
	def validate(self):
		self.map_milestone_reference()
		if self.type in ["Appreciation", "Criticism"] and self.user == self.owner:
			jingrow.throw(_("You cannot give review points to yourself"))

	def map_milestone_reference(self):
		# link energy point to the original reference, if set by milestone
		if self.reference_pagetype == "Milestone":
			self.reference_pagetype, self.reference_name = jingrow.db.get_value(
				"Milestone", self.reference_name, ["reference_type", "reference_name"]
			)

	def after_insert(self):
		alert_dict = get_alert_dict(self)
		if alert_dict:
			jingrow.publish_realtime(
				"energy_point_alert", message=alert_dict, user=self.user, after_commit=True
			)

		jingrow.cache.hdel("energy_points", self.user)

		if self.type != "Review" and jingrow.get_cached_value(
			"Notification Settings", self.user, "energy_points_system_notifications"
		):
			reference_user = self.user if self.type == "Auto" else self.owner
			notification_pg = {
				"type": "Energy Point",
				"document_type": self.reference_pagetype,
				"document_name": self.reference_name,
				"subject": get_notification_message(self),
				"from_user": reference_user,
				"email_content": f"<div>{self.reason}</div>" if self.reason else None,
			}

			enqueue_create_notification(self.user, notification_pg)

	def on_trash(self):
		if self.type == "Revert":
			reference_log = jingrow.get_pg("Energy Point Log", self.revert_of)
			reference_log.reverted = 0
			reference_log.save()

	@jingrow.whitelist()
	def revert(self, reason, ignore_permissions=False):
		if not ignore_permissions:
			jingrow.only_for("System Manager")

		if self.type != "Auto":
			jingrow.throw(_("This document cannot be reverted"))

		if self.get("reverted"):
			return

		self.reverted = 1
		self.save(ignore_permissions=True)

		return jingrow.get_pg(
			{
				"pagetype": "Energy Point Log",
				"points": -(self.points),
				"type": "Revert",
				"user": self.user,
				"reason": reason,
				"reference_pagetype": self.reference_pagetype,
				"reference_name": self.reference_name,
				"revert_of": self.name,
			}
		).insert(ignore_permissions=True)


def get_notification_message(pg):
	owner_name = get_fullname(pg.owner)
	points = pg.points
	title = get_title(pg.reference_pagetype, pg.reference_name)

	if pg.type == "Auto":
		owner_name = jingrow.bold("You")
		if points == 1:
			message = _("{0} gained {1} point for {2} {3}")
		else:
			message = _("{0} gained {1} points for {2} {3}")
		message = message.format(owner_name, jingrow.bold(points), pg.rule, get_title_html(title))
	elif pg.type == "Appreciation":
		if points == 1:
			message = _("{0} appreciated your work on {1} with {2} point")
		else:
			message = _("{0} appreciated your work on {1} with {2} points")
		message = message.format(jingrow.bold(owner_name), get_title_html(title), jingrow.bold(points))
	elif pg.type == "Criticism":
		if points == 1:
			message = _("{0} criticized your work on {1} with {2} point")
		else:
			message = _("{0} criticized your work on {1} with {2} points")

		message = message.format(jingrow.bold(owner_name), get_title_html(title), jingrow.bold(points))
	elif pg.type == "Revert":
		if points == 1:
			message = _("{0} reverted your point on {1}")
		else:
			message = _("{0} reverted your points on {1}")
		message = message.format(jingrow.bold(owner_name), get_title_html(title))

	return message


def get_alert_dict(pg):
	alert_dict = jingrow._dict()
	owner_name = get_fullname(pg.owner)
	if pg.reference_pagetype:
		pg_link = get_link_to_form(pg.reference_pagetype, pg.reference_name)
	points = pg.points
	bold_points = jingrow.bold(pg.points)
	if pg.type == "Auto":
		if points == 1:
			message = _("You gained {0} point")
		else:
			message = _("You gained {0} points")
		alert_dict.message = message.format(bold_points)
		alert_dict.indicator = "green"
	elif pg.type == "Appreciation":
		if points == 1:
			message = _("{0} appreciated your work on {1} with {2} point")
		else:
			message = _("{0} appreciated your work on {1} with {2} points")
		alert_dict.message = message.format(owner_name, pg_link, bold_points)
		alert_dict.indicator = "green"
	elif pg.type == "Criticism":
		if points == 1:
			message = _("{0} criticized your work on {1} with {2} point")
		else:
			message = _("{0} criticized your work on {1} with {2} points")

		alert_dict.message = message.format(owner_name, pg_link, bold_points)
		alert_dict.indicator = "red"
	elif pg.type == "Revert":
		if points == 1:
			message = _("{0} reverted your point on {1}")
		else:
			message = _("{0} reverted your points on {1}")
		alert_dict.message = message.format(
			owner_name,
			pg_link,
		)
		alert_dict.indicator = "red"

	return alert_dict


def create_energy_points_log(ref_pagetype, ref_name, pg, apply_only_once=False):
	pg = jingrow._dict(pg)
	if pg.rule:
		log_exists = check_if_log_exists(
			ref_pagetype, ref_name, pg.rule, None if apply_only_once else pg.user
		)
		if log_exists:
			return jingrow.get_pg("Energy Point Log", log_exists)

	new_log = jingrow.new_pg("Energy Point Log")
	new_log.reference_pagetype = ref_pagetype
	new_log.reference_name = ref_name
	new_log.update(pg)
	new_log.insert(ignore_permissions=True)
	return new_log


def check_if_log_exists(ref_pagetype, ref_name, rule, user=None):
	"""'Checks if Energy Point Log already exists"""
	filters = jingrow._dict(
		{"rule": rule, "reference_pagetype": ref_pagetype, "reference_name": ref_name, "reverted": 0}
	)

	if user:
		filters.user = user

	return jingrow.db.exists("Energy Point Log", filters)


def create_review_points_log(user, points, reason=None, pagetype=None, pagename=None):
	return jingrow.get_pg(
		{
			"pagetype": "Energy Point Log",
			"points": points,
			"type": "Review",
			"user": user,
			"reason": reason,
			"reference_pagetype": pagetype,
			"reference_name": pagename,
		}
	).insert(ignore_permissions=True)


@jingrow.whitelist()
def add_review_points(user, points):
	jingrow.only_for("System Manager")
	create_review_points_log(user, points)


@jingrow.whitelist()
def get_energy_points(user):
	points = get_user_energy_and_review_points(user)
	return jingrow._dict(points.get(user, {}))


@jingrow.whitelist()
def get_user_energy_and_review_points(user=None, from_date=None, as_dict=True):
	conditions = ""
	given_points_condition = ""
	values = jingrow._dict()
	if user:
		conditions = "WHERE `user` = %(user)s"
		values.user = user
	if from_date:
		conditions += "WHERE" if not conditions else "AND"
		given_points_condition += "AND `creation` >= %(from_date)s"
		conditions += " `creation` >= %(from_date)s OR `type`='Review'"
		values.from_date = from_date

	points_list = jingrow.db.sql(
		f"""
		SELECT
			SUM(CASE WHEN `type` != 'Review' THEN `points` ELSE 0 END) AS energy_points,
			SUM(CASE WHEN `type` = 'Review' THEN `points` ELSE 0 END) AS review_points,
			SUM(CASE
				WHEN `type`='Review' AND `points` < 0 {given_points_condition}
				THEN ABS(`points`)
				ELSE 0
			END) as given_points,
			`user`
		FROM `tabEnergy Point Log`
		{conditions}
		GROUP BY `user`
		ORDER BY `energy_points` DESC
	""",
		values=values,
		as_dict=1,
	)

	if not as_dict:
		return points_list

	dict_to_return = jingrow._dict()
	for d in points_list:
		dict_to_return[d.pop("user")] = d
	return dict_to_return


@jingrow.whitelist()
def review(pg, points, to_user, reason, review_type="Appreciation"):
	current_review_points = get_energy_points(jingrow.session.user).review_points
	pg = pg.as_dict() if hasattr(pg, "as_dict") else jingrow._dict(json.loads(pg))
	points = abs(cint(points))
	if current_review_points < points:
		jingrow.msgprint(_("You do not have enough review points"))
		return

	review_pg = create_energy_points_log(
		pg.pagetype,
		pg.name,
		{
			"type": review_type,
			"reason": reason,
			"points": points if review_type == "Appreciation" else -points,
			"user": to_user,
		},
	)

	# deduct review points from reviewer
	create_review_points_log(
		user=jingrow.session.user,
		points=-points,
		reason=reason,
		pagetype=review_pg.pagetype,
		pagename=review_pg.name,
	)

	return review_pg


@jingrow.whitelist()
def get_reviews(pagetype, pagename):
	return jingrow.get_all(
		"Energy Point Log",
		filters={
			"reference_pagetype": pagetype,
			"reference_name": pagename,
			"type": ["in", ("Appreciation", "Criticism")],
		},
		fields=["points", "owner", "type", "user", "reason", "creation"],
	)


def send_weekly_summary():
	send_summary("Weekly")


def send_monthly_summary():
	send_summary("Monthly")


def send_summary(timespan):
	from jingrow.social.pagetype.energy_point_settings.energy_point_settings import (
		is_energy_point_enabled,
	)
	from jingrow.utils.user import get_enabled_system_users

	if not is_energy_point_enabled():
		return

	if not is_email_notifications_enabled_for_type(jingrow.session.user, "Energy Point"):
		return

	from_date = jingrow.utils.add_to_date(None, weeks=-1)
	if timespan == "Monthly":
		from_date = jingrow.utils.add_to_date(None, months=-1)

	user_points = get_user_energy_and_review_points(from_date=from_date, as_dict=False)

	# do not send report if no activity found
	if not user_points or not user_points[0].energy_points:
		return

	from_date = getdate(from_date)
	to_date = getdate()

	# select only those users that have energy point email notifications enabled
	all_users = [
		user.email
		for user in get_enabled_system_users()
		if is_email_notifications_enabled_for_type(user.name, "Energy Point")
	]

	jingrow.sendmail(
		subject=f"{timespan} energy points summary",
		recipients=all_users,
		template="energy_points_summary",
		args={
			"top_performer": user_points[0],
			"top_reviewer": max(user_points, key=lambda x: x["given_points"]),
			"standings": user_points[:10],  # top 10
			"footer_message": get_footer_message(timespan).format(from_date, to_date),
		},
		with_container=1,
	)


def get_footer_message(timespan):
	if timespan == "Monthly":
		return _("Stats based on last month's performance (from {0} to {1})")
	else:
		return _("Stats based on last week's performance (from {0} to {1})")
