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

from collections import defaultdict
from json import loads

import jingrow
from jingrow import _
from jingrow.desk.desktop import save_new_widget
from jingrow.desk.utils import validate_route_conflict
from jingrow.model.page import Page
from jingrow.model.rename_pg import rename_pg
from jingrow.modules.export_file import delete_folder, export_to_files
from jingrow.utils import strip_html


class Workspace(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.core.pagetype.has_role.has_role import HasRole
		from jingrow.desk.pagetype.workspace_chart.workspace_chart import WorkspaceChart
		from jingrow.desk.pagetype.workspace_custom_block.workspace_custom_block import WorkspaceCustomBlock
		from jingrow.desk.pagetype.workspace_link.workspace_link import WorkspaceLink
		from jingrow.desk.pagetype.workspace_number_card.workspace_number_card import WorkspaceNumberCard
		from jingrow.desk.pagetype.workspace_quick_list.workspace_quick_list import WorkspaceQuickList
		from jingrow.desk.pagetype.workspace_shortcut.workspace_shortcut import WorkspaceShortcut
		from jingrow.types import DF

		charts: DF.Table[WorkspaceChart]
		content: DF.LongText | None
		custom_blocks: DF.Table[WorkspaceCustomBlock]
		for_user: DF.Data | None
		hide_custom: DF.Check
		indicator_color: DF.Literal["green", "cyan", "blue", "orange", "yellow", "gray", "grey", "red", "pink", "darkgrey", "purple", "light-blue"]
		is_hidden: DF.Check
		label: DF.Data
		links: DF.Table[WorkspaceLink]
		module: DF.Link | None
		number_cards: DF.Table[WorkspaceNumberCard]
		parent_page: DF.Data | None
		public: DF.Check
		quick_lists: DF.Table[WorkspaceQuickList]
		restrict_to_domain: DF.Link | None
		roles: DF.Table[HasRole]
		sequence_id: DF.Float
		shortcuts: DF.Table[WorkspaceShortcut]
		title: DF.Data
	# end: auto-generated types
	def validate(self):
		self.title = strip_html(self.title)

		if self.public and not is_workspace_manager() and not disable_saving_as_public():
			jingrow.throw(_("You need to be Workspace Manager to edit this document"))
		if self.has_value_changed("title"):
			validate_route_conflict(self.pagetype, self.title)
		else:
			validate_route_conflict(self.pagetype, self.name)

		try:
			if not isinstance(loads(self.content), list):
				raise
		except Exception:
			jingrow.throw(_("Content data shoud be a list"))

		for d in self.get("links"):
			if d.link_type == "Report" and d.is_query_report != 1:
				d.report_ref_pagetype = jingrow.get_value("Report", d.link_to, "ref_pagetype")

		for shortcut in self.get("shortcuts"):
			if shortcut.type == "Report":
				shortcut.report_ref_pagetype = jingrow.get_value("Report", shortcut.link_to, "ref_pagetype")

	def clear_cache(self):
		super().clear_cache()
		if self.for_user:
			jingrow.cache.hdel("bootinfo", self.for_user)
		else:
			jingrow.cache.delete_key("bootinfo")

	def on_update(self):
		if disable_saving_as_public():
			return

		if jingrow.conf.developer_mode and self.public:
			if self.module:
				export_to_files(record_list=[["Workspace", self.name]], record_module=self.module)

			if self.has_value_changed("title") or self.has_value_changed("module"):
				previous = self.get_pg_before_save()
				if previous and previous.get("module") and previous.get("title"):
					delete_folder(previous.get("module"), "Workspace", previous.get("title"))

	def before_export(self, pg):
		if pg.title != pg.label and pg.label == pg.name:
			self.name = pg.name = pg.label = pg.title

	def on_trash(self):
		if self.public and not is_workspace_manager():
			jingrow.throw(_("You need to be Workspace Manager to delete a public workspace."))

	def after_delete(self):
		if disable_saving_as_public():
			return

		if self.module and jingrow.conf.developer_mode:
			delete_folder(self.module, "Workspace", self.title)

	@staticmethod
	def get_module_wise_workspaces():
		workspaces = jingrow.get_all(
			"Workspace",
			fields=["name", "module"],
			filters={"for_user": "", "public": 1},
			order_by="creation",
		)

		module_workspaces = defaultdict(list)

		for workspace in workspaces:
			if not workspace.module:
				continue
			module_workspaces[workspace.module].append(workspace.name)

		return module_workspaces

	def get_link_groups(self):
		cards = []
		current_card = jingrow._dict(
			{
				"label": "Link",
				"type": "Card Break",
				"icon": None,
				"hidden": False,
			}
		)

		card_links = []

		for link in self.links:
			link = link.as_dict()
			if link.type == "Card Break":
				if card_links and (
					not current_card.get("only_for")
					or current_card.get("only_for") == jingrow.get_system_settings("country")
				):
					current_card["links"] = card_links
					cards.append(current_card)

				current_card = link
				card_links = []
			elif not link.get("only_for") or link.get("only_for") == jingrow.get_system_settings("country"):
				card_links.append(link)

		current_card["links"] = card_links
		cards.append(current_card)

		return cards

	def build_links_table_from_card(self, config):
		for idx, card in enumerate(config):
			links = loads(card.get("links"))

			# remove duplicate before adding
			for idx, link in enumerate(self.links):
				if link.get("label") == card.get("label") and link.get("type") == "Card Break":
					# count and set number of links for the card if link_count is 0
					if link.link_count == 0:
						for count, card_link in enumerate(self.links[idx + 1 :]):
							if card_link.get("type") == "Card Break":
								break
							link.link_count = count + 1

					del self.links[idx : idx + link.link_count + 1]

			self.append(
				"links",
				{
					"label": card.get("label"),
					"type": "Card Break",
					"icon": card.get("icon"),
					"description": card.get("description"),
					"hidden": card.get("hidden") or False,
					"link_count": card.get("link_count"),
					"idx": 1 if not self.links else self.links[-1].idx + 1,
				},
			)

			for link in links:
				self.append(
					"links",
					{
						"label": link.get("label"),
						"type": "Link",
						"link_type": link.get("link_type"),
						"link_to": link.get("link_to"),
						"onboard": link.get("onboard"),
						"only_for": link.get("only_for"),
						"dependencies": link.get("dependencies"),
						"is_query_report": link.get("is_query_report"),
						"idx": self.links[-1].idx + 1,
					},
				)


def disable_saving_as_public():
	return (
		jingrow.flags.in_install
		or jingrow.flags.in_uninstall
		or jingrow.flags.in_patch
		or jingrow.flags.in_test
		or jingrow.flags.in_fixtures
		or jingrow.flags.in_migrate
	)


def get_link_type(key):
	key = key.lower()

	link_type_map = {"pagetype": "PageType", "page": "Page", "report": "Report"}

	if key in link_type_map:
		return link_type_map[key]

	return "PageType"


def get_report_type(report):
	report_type = jingrow.get_value("Report", report, "report_type")
	return report_type in ["Query Report", "Script Report", "Custom Report"]


@jingrow.whitelist()
def new_page(new_page):
	if not loads(new_page):
		return

	page = loads(new_page)

	if page.get("public") and not is_workspace_manager():
		return
	elif (
		not page.get("public") and page.get("for_user") != jingrow.session.user and not is_workspace_manager()
	):
		jingrow.throw(_("Cannot create private workspace of other users"), jingrow.PermissionError)

	elif not jingrow.has_permission(pagetype="Workspace", ptype="create"):
		jingrow.flags.error_message = _("User {0} does not have the permission to create a Workspace.").format(
			jingrow.bold(jingrow.session.user)
		)
		raise jingrow.PermissionError

	pg = jingrow.new_pg("Workspace")
	pg.title = page.get("title")
	pg.icon = page.get("icon")
	pg.indicator_color = page.get("indicator_color")
	pg.content = page.get("content")
	pg.parent_page = page.get("parent_page")
	pg.label = page.get("label")
	pg.for_user = page.get("for_user")
	pg.public = page.get("public")
	pg.sequence_id = last_sequence_id(pg) + 1
	pg.save(ignore_permissions=True)

	return pg


@jingrow.whitelist()
def save_page(title, public, new_widgets, blocks):
	public = jingrow.parse_json(public)

	filters = {"public": public, "label": title}

	if not public:
		filters = {"for_user": jingrow.session.user, "label": title + "-" + jingrow.session.user}
	pages = jingrow.get_all("Workspace", filters=filters)
	if pages:
		pg = jingrow.get_pg("Workspace", pages[0])
	else:
		jingrow.throw(_("Workspace not found"), jingrow.DoesNotExistError)

	pg.content = blocks
	pg.save(ignore_permissions=True)

	save_new_widget(pg, title, blocks, new_widgets)

	return {"name": title, "public": public, "label": pg.label}


@jingrow.whitelist()
def update_page(name, title, icon, indicator_color, parent, public):
	public = jingrow.parse_json(public)
	pg = jingrow.get_pg("Workspace", name)

	if not pg.get("public") and pg.get("for_user") != jingrow.session.user and not is_workspace_manager():
		jingrow.throw(
			_("Need Workspace Manager role to edit private workspace of other users"),
			jingrow.PermissionError,
		)

	if pg:
		child_pgs = jingrow.get_all("Workspace", filters={"parent_page": pg.title, "public": pg.public})
		pg.title = title
		pg.icon = icon
		pg.indicator_color = indicator_color
		pg.parent_page = parent
		if pg.public != public:
			pg.sequence_id = jingrow.db.count("Workspace", {"public": public}, cache=True)
			pg.public = public
		pg.for_user = "" if public else pg.for_user or jingrow.session.user
		pg.label = new_name = f"{title}-{pg.for_user}" if pg.for_user else title
		pg.save(ignore_permissions=True)

		if name != new_name:
			rename_pg("Workspace", name, new_name, force=True, ignore_permissions=True)

		# update new name and public in child pages
		if child_pgs:
			for child in child_pgs:
				child_pg = jingrow.get_pg("Workspace", child.name)
				child_pg.parent_page = pg.title
				if child_pg.public != public:
					child_pg.public = public
				child_pg.for_user = "" if public else child_pg.for_user or jingrow.session.user
				child_pg.label = new_child_name = (
					f"{child_pg.title}-{child_pg.for_user}" if child_pg.for_user else child_pg.title
				)
				child_pg.save(ignore_permissions=True)

				if child.name != new_child_name:
					rename_pg("Workspace", child.name, new_child_name, force=True, ignore_permissions=True)

	return {"name": title, "public": public, "label": new_name}


def hide_unhide_page(page_name: str, is_hidden: bool):
	page = jingrow.get_pg("Workspace", page_name)

	if page.get("public") and not is_workspace_manager():
		jingrow.throw(
			_("Need Workspace Manager role to hide/unhide public workspaces"), jingrow.PermissionError
		)

	if not page.get("public") and page.get("for_user") != jingrow.session.user and not is_workspace_manager():
		jingrow.throw(_("Cannot update private workspace of other users"), jingrow.PermissionError)

	page.is_hidden = int(is_hidden)
	page.save(ignore_permissions=True)
	return True


@jingrow.whitelist()
def hide_page(page_name: str):
	return hide_unhide_page(page_name, 1)


@jingrow.whitelist()
def unhide_page(page_name: str):
	return hide_unhide_page(page_name, 0)


@jingrow.whitelist()
def duplicate_page(page_name, new_page):
	if not loads(new_page):
		return

	new_page = loads(new_page)

	if new_page.get("is_public") and not is_workspace_manager():
		return

	old_pg = jingrow.get_pg("Workspace", page_name)
	pg = jingrow.copy_pg(old_pg)
	pg.title = new_page.get("title")
	pg.icon = new_page.get("icon")
	pg.indicator_color = new_page.get("indicator_color")
	pg.parent_page = new_page.get("parent") or ""
	pg.public = new_page.get("is_public")
	pg.for_user = ""
	pg.label = pg.title
	pg.module = ""
	if not pg.public:
		pg.for_user = pg.for_user or jingrow.session.user
		pg.label = f"{pg.title}-{pg.for_user}"
	pg.name = pg.label
	if old_pg.public == pg.public:
		pg.sequence_id += 0.1
	else:
		pg.sequence_id = last_sequence_id(pg) + 1
	pg.insert(ignore_permissions=True)

	return pg


@jingrow.whitelist()
def delete_page(page):
	if not loads(page):
		return

	page = loads(page)

	if page.get("public") and not is_workspace_manager():
		jingrow.throw(
			_("Cannot delete public workspace without Workspace Manager role"),
			jingrow.PermissionError,
		)
	elif not page.get("public") and not is_workspace_manager():
		workspace_owner = jingrow.get_value("Workspace", page.get("name"), "for_user")
		if workspace_owner != jingrow.session.user:
			jingrow.throw(
				_("Cannot delete private workspace of other users"),
				jingrow.PermissionError,
			)

	if jingrow.db.exists("Workspace", page.get("name")):
		jingrow.get_pg("Workspace", page.get("name")).delete(ignore_permissions=True)

	return {"name": page.get("name"), "public": page.get("public"), "title": page.get("title")}


@jingrow.whitelist()
def sort_pages(sb_public_items, sb_private_items):
	if not loads(sb_public_items) and not loads(sb_private_items):
		return

	sb_public_items = loads(sb_public_items)
	sb_private_items = loads(sb_private_items)

	workspace_public_pages = get_page_list(["name", "title"], {"public": 1})
	workspace_private_pages = get_page_list(["name", "title"], {"for_user": jingrow.session.user})

	sorted = False
	if sb_private_items:
		sorted = sort_page(workspace_private_pages, sb_private_items) or sorted

	if sb_public_items and is_workspace_manager():
		sorted = sort_page(workspace_public_pages, sb_public_items) or sorted

	return sorted


def sort_page(workspace_pages, pages):
	for seq, d in enumerate(pages):
		for page in workspace_pages:
			if page.title == d.get("title"):
				new_seq = seq + 1
				new_parent = d.get("parent_page") or ""
				# Skip save if nothing actually changed
				existing = jingrow.db.get_value("Workspace", page.name, ["sequence_id", "parent_page"])
				if existing and float(existing[0] or 0) == new_seq and (existing[1] or "") == new_parent:
					break
				pg = jingrow.get_pg("Workspace", page.name)
				pg.sequence_id = new_seq
				pg.parent_page = new_parent
				pg.flags.ignore_links = True
				pg.save(ignore_permissions=True)
				break

	return True


def last_sequence_id(pg):
	pg_exists = jingrow.db.exists({"pagetype": "Workspace", "public": pg.public, "for_user": pg.for_user})

	if not pg_exists:
		return 0

	return jingrow.get_all(
		"Workspace",
		fields=["sequence_id"],
		filters={"public": pg.public, "for_user": pg.for_user},
		order_by="sequence_id desc",
	)[0].sequence_id


def get_page_list(fields, filters):
	return jingrow.get_all("Workspace", fields=fields, filters=filters, order_by="sequence_id asc")


def is_workspace_manager():
	return "Workspace Manager" in jingrow.get_roles()
