# Copyright (c) 2019, JINGROW
# License: MIT. See LICENSE
import json

import jingrow
from jingrow.database.schema import add_column
from jingrow.desk.notifications import notify_mentions
from jingrow.exceptions import ImplicitCommitError
from jingrow.model.page import Page
from jingrow.model.utils import is_virtual_pagetype
from jingrow.website.utils import clear_cache


class Comment(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

		comment_by: DF.Data | None
		comment_email: DF.Data | None
		comment_type: DF.Literal["Comment", "Like", "Info", "Label", "Workflow", "Created", "Submitted", "Cancelled", "Updated", "Deleted", "Assigned", "Assignment Completed", "Attachment", "Attachment Removed", "Shared", "Unshared", "Bot", "Relinked", "Edit", "Rename", "Merge", "Restore"]
		content: DF.HTMLEditor | None
		ip_address: DF.Data | None
		published: DF.Check
		reference_name: DF.DynamicLink | None
		reference_owner: DF.Data | None
		reference_pagetype: DF.Link | None
		seen: DF.Check
		subject: DF.Text | None
	# end: auto-generated types

	no_feed_on_delete = True

	def after_insert(self):
		notify_mentions(self.reference_pagetype, self.reference_name, self.content)
		self.notify_change("add")

	def validate(self):
		if not self.comment_email:
			self.comment_email = jingrow.session.user
		self.content = jingrow.utils.sanitize_html(self.content, always_sanitize=True)

	def on_update(self):
		update_comment_in_pg(self)
		if self.is_new():
			self.notify_change("update")

	def on_trash(self):
		self.remove_comment_from_cache()
		self.notify_change("delete")

	def notify_change(self, action):
		key_map = {
			"Like": "like_logs",
			"Assigned": "assignment_logs",
			"Assignment Completed": "assignment_logs",
			"Comment": "comments",
			"Attachment": "attachment_logs",
			"Attachment Removed": "attachment_logs",
		}
		key = key_map.get(self.comment_type)
		if not key:
			return

		jingrow.publish_realtime(
			"pageinfo_update",
			{"pg": self.as_dict(), "key": key, "action": action},
			pagetype=self.reference_pagetype,
			pagename=self.reference_name,
			after_commit=True,
		)

	def remove_comment_from_cache(self):
		_comments = get_comments_from_parent(self)
		for c in list(_comments):
			if c.get("name") == self.name:
				_comments.remove(c)

		update_comments_in_parent(self.reference_pagetype, self.reference_name, _comments)


def on_pagetype_update():
	jingrow.db.add_index("Comment", ["reference_pagetype", "reference_name"])


def update_comment_in_pg(pg):
	"""Updates `_comments` (JSON) property in parent Page.
	Creates a column `_comments` if property does not exist.

	Only user created Communication or Comment of type Comment are saved.

	`_comments` format

	        {
	                "comment": [String],
	                "by": [user],
	                "name": [Comment Page name]
	        }"""

	# only comments get updates, not likes, assignments etc.
	if pg.pagetype == "Comment" and pg.comment_type != "Comment":
		return

	def get_truncated(content):
		return (content[:97] + "...") if len(content) > 100 else content

	if pg.reference_pagetype and pg.reference_name and pg.content:
		_comments = get_comments_from_parent(pg)

		updated = False
		for c in _comments:
			if c.get("name") == pg.name:
				c["comment"] = get_truncated(pg.content)
				updated = True

		if not updated:
			_comments.append(
				{
					"comment": get_truncated(pg.content),
					# "comment_email" for Comment and "sender" for Communication
					"by": getattr(pg, "comment_email", None) or getattr(pg, "sender", None) or pg.owner,
					"name": pg.name,
				}
			)

		update_comments_in_parent(pg.reference_pagetype, pg.reference_name, _comments)


def get_comments_from_parent(pg):
	"""
	get the list of comments cached in the document record in the column
	`_comments`
	"""
	try:
		if is_virtual_pagetype(pg.reference_pagetype):
			_comments = "[]"
		else:
			_comments = jingrow.db.get_value(pg.reference_pagetype, pg.reference_name, "_comments") or "[]"

	except Exception as e:
		if jingrow.db.is_missing_table_or_column(e):
			_comments = "[]"

		else:
			raise

	try:
		return json.loads(_comments)
	except ValueError:
		return []


def update_comments_in_parent(reference_pagetype, reference_name, _comments):
	"""Updates `_comments` property in parent Page with given dict.

	:param _comments: Dict of comments."""
	if (
		not reference_pagetype
		or not reference_name
		or jingrow.db.get_value("PageType", reference_pagetype, "issingle")
		or is_virtual_pagetype(reference_pagetype)
	):
		return

	try:
		# use sql, so that we do not mess with the timestamp
		jingrow.db.sql(
			f"""update `tab{reference_pagetype}` set `_comments`=%s where name=%s""",  # nosec
			(json.dumps(_comments[-100:]), reference_name),
		)

	except Exception as e:
		if jingrow.db.is_missing_column(e) and getattr(jingrow.local, "request", None):
			pass
		elif jingrow.db.is_data_too_long(e):
			raise jingrow.DataTooLongException
		else:
			raise
	else:
		if jingrow.flags.in_patch:
			return

		# Clear route cache
		if route := jingrow.get_cached_value(reference_pagetype, reference_name, "route"):
			clear_cache(route)
