# Copyright (c) 2022, JINGROW
# For license information, please see license.txt

from urllib.parse import quote

from rq import get_current_job

import jingrow
from jingrow import _
from jingrow.desk.pagetype.notification_log.notification_log import enqueue_create_notification
from jingrow.model.page import Page
from jingrow.monitor import add_data_to_monitor
from jingrow.utils import now, time_diff_in_seconds
from jingrow.utils.data import cint


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

		created_at: DF.Datetime | None
		ended_at: DF.Datetime | None
		enqueued_by: DF.Data | None
		exception: DF.LongText | None
		job_id: DF.Link | None
		ref_pagename: DF.DynamicLink | None
		ref_pagetype: DF.Link | None
		status: DF.Literal["Queued", "Finished", "Failed"]

	# end: auto-generated types
	@property
	def created_at(self):
		return self.creation

	@property
	def enqueued_by(self):
		return self.owner

	@property
	def queued_pg(self):
		return getattr(self, "to_be_queued_pg", jingrow.get_pg(self.ref_pagetype, self.ref_pagename))

	@staticmethod
	def clear_old_logs(days=30):
		from jingrow.query_builder import Interval
		from jingrow.query_builder.functions import Now

		table = jingrow.qb.PageType("Submission Queue")
		jingrow.db.delete(table, filters=(table.modified < (Now() - Interval(days=days))))

	def insert(self, to_be_queued_pg: Page, action: str):
		self.status = "Queued"
		self.to_be_queued_pg = to_be_queued_pg
		self.action_for_queuing = action
		super().insert(ignore_permissions=True)

	def lock(self):
		self.queued_pg.lock()

	def unlock(self):
		self.queued_pg.unlock()

	def update_job_id(self, job_id):
		jingrow.db.set_value(
			self.pagetype,
			self.name,
			{"job_id": job_id},
			update_modified=False,
		)
		jingrow.db.commit()

	def after_insert(self):
		self.queue_action(
			"background_submission",
			to_be_queued_pg=self.queued_pg,
			action_for_queuing=self.action_for_queuing,
			timeout=600,
			enqueue_after_commit=True,
		)

	def background_submission(self, to_be_queued_pg: Page, action_for_queuing: str):
		# Set the job id for that submission pagetype
		self.update_job_id(get_current_job().id)

		_action = action_for_queuing.lower()
		if _action == "update":
			_action = "submit"

		try:
			getattr(to_be_queued_pg, _action)()
			add_data_to_monitor(
				pagetype=to_be_queued_pg.pagetype,
				pagename=to_be_queued_pg.name,
				action=_action,
				execution_time=time_diff_in_seconds(now(), self.created_at),
				enqueued_by=self.enqueued_by,
			)
			values = {"status": "Finished"}
		except Exception:
			values = {"status": "Failed", "exception": jingrow.get_traceback(with_context=True)}
			jingrow.db.rollback()

		values["ended_at"] = now()
		jingrow.db.set_value(self.pagetype, self.name, values, update_modified=False)
		self.notify(values["status"], action_for_queuing)

	def notify(self, submission_status: str, action: str):
		if submission_status == "Failed":
			pagetype = self.pagetype
			pagename = self.name
			message = _("Action {0} failed on {1} {2}. View it {3}")
		else:
			pagetype = self.ref_pagetype
			pagename = self.ref_pagename
			message = _("Action {0} completed successfully on {1} {2}. View it {3}")

		message_replacements = (
			jingrow.bold(action),
			jingrow.bold(str(self.ref_pagetype)),
			jingrow.bold(str(self.ref_pagename)),
		)

		time_diff = time_diff_in_seconds(now(), self.created_at)
		if cint(time_diff) <= 60:
			jingrow.publish_realtime(
				"msgprint",
				{
					"message": message.format(
						*message_replacements,
						f"<a href='/app/{quote(pagetype.lower().replace(' ', '-'))}/{quote(pagename)}'><b>here</b></a>",
					),
					"alert": True,
					"indicator": "red" if submission_status == "Failed" else "green",
				},
				user=self.enqueued_by,
			)
		else:
			notification_pg = {
				"type": "Alert",
				"document_type": pagetype,
				"document_name": pagename,
				"subject": message.format(*message_replacements, "here"),
			}

			notify_to = jingrow.db.get_value("User", self.enqueued_by, fieldname="email")
			enqueue_create_notification([notify_to], notification_pg)

	@jingrow.whitelist()
	def unlock_pg(self):
		# NOTE: this can lead to some weird unlocking/locking behaviours.
		# for example: hitting unlock on a submission could lead to unlocking of another submission
		# of the same reference document.

		if self.status != "Queued":
			return

		self.queued_pg.unlock()
		jingrow.msgprint(_("Page Unlocked"))


def queue_submission(pg: Page, action: str, alert: bool = True):
	queue = jingrow.new_pg("Submission Queue")
	queue.ref_pagetype = pg.pagetype
	queue.ref_pagename = pg.name
	queue.insert(pg, action)

	if alert:
		jingrow.msgprint(
			_("Queued for Submission. You can track the progress over {0}.").format(
				f"<a href='/app/submission-queue/{queue.name}'><b>here</b></a>"
			),
			indicator="green",
			alert=True,
		)


@jingrow.whitelist()
def get_latest_submissions(pagetype, pagename):
	# NOTE: not used creation as orderby intentianlly as we have used update_modified=False everywhere
	# hence assuming modified will be equal to creation for submission queue documents

	latest_submission = jingrow.db.get_value(
		"Submission Queue",
		filters={"ref_pagetype": pagetype, "ref_pagename": pagename},
		fieldname=["name", "exception", "status"],
	)

	out = None
	if latest_submission:
		out = {
			"latest_submission": latest_submission[0],
			"exc": format_tb(latest_submission[1]),
			"status": latest_submission[2],
		}

	return out


def format_tb(traceback: str | None = None):
	if not traceback:
		return

	return traceback.strip().split("\n")[-1]
