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

import jingrow
from jingrow import _
from jingrow.config import get_modules_from_app
from jingrow.model.page import Page
from jingrow.permissions import add_permission, add_user_permission
from jingrow.utils import get_link_to_form


class UserType(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.user_page_type.user_page_type import UserDocumentType
		from jingrow.core.pagetype.user_select_page_type.user_select_page_type import (
			UserSelectDocumentType,
		)
		from jingrow.core.pagetype.user_type_module.user_type_module import UserTypeModule
		from jingrow.types import DF

		apply_user_permission_on: DF.Link | None
		custom_select_pagetypes: DF.Table[UserSelectDocumentType]
		is_standard: DF.Check
		role: DF.Link | None
		select_pagetypes: DF.Table[UserSelectDocumentType]
		user_pagetypes: DF.Table[UserDocumentType]
		user_id_field: DF.Literal[None]
		user_type_modules: DF.Table[UserTypeModule]

	# end: auto-generated types
	def validate(self):
		self.set_modules()
		self.add_select_perm_pagetypes()

	def clear_cache(self):
		super().clear_cache()

		if not self.is_standard:
			jingrow.cache.delete_value("non_standard_user_types")

	def on_update(self):
		if self.is_standard:
			return

		self.validate_document_type_limit()
		self.validate_role()
		self.add_role_permissions_for_user_pagetypes()
		self.add_role_permissions_for_select_pagetypes()
		self.add_role_permissions_for_file()
		self.update_users()
		self.remove_permission_for_deleted_pagetypes()

	def on_trash(self):
		if self.is_standard:
			jingrow.throw(_("Standard user type {0} can not be deleted.").format(jingrow.bold(self.name)))

	def set_modules(self):
		if not self.user_pagetypes:
			return

		modules = jingrow.get_all(
			"PageType",
			filters={"name": ("in", [d.document_type for d in self.user_pagetypes])},
			distinct=True,
			pluck="module",
		)

		self.set("user_type_modules", [])
		for module in modules:
			self.append("user_type_modules", {"module": module})

	def validate_document_type_limit(self):
		limit = jingrow.conf.get("user_type_pagetype_limit", {}).get(jingrow.scrub(self.name))

		if not limit and jingrow.session.user != "Administrator":
			jingrow.throw(
				_("User does not have permission to create the new {0}").format(jingrow.bold(_("User Type"))),
				title=_("Permission Error"),
			)

		if not limit:
			jingrow.throw(
				_("The limit has not set for the user type {0} in the site config file.").format(
					jingrow.bold(self.name)
				),
				title=_("Set Limit"),
			)

		if self.user_pagetypes and len(self.user_pagetypes) > limit:
			jingrow.throw(
				_("The total number of user document types limit has been crossed."),
				title=_("User Page Types Limit Exceeded"),
			)

		custom_pagetypes = [row.document_type for row in self.user_pagetypes if row.is_custom]
		if custom_pagetypes and len(custom_pagetypes) > 3:
			jingrow.throw(
				_("You can only set the 3 custom pagetypes in the Page Types table."),
				title=_("Custom Page Types Limit Exceeded"),
			)

	def validate_role(self):
		if not self.role:
			jingrow.throw(_("The field {0} is mandatory").format(jingrow.bold(_("Role"))))

		if not jingrow.db.get_value("Role", self.role, "is_custom"):
			jingrow.throw(
				_("The role {0} should be a custom role.").format(
					jingrow.bold(get_link_to_form("Role", self.role))
				)
			)

	def update_users(self):
		for row in jingrow.get_all("User", filters={"user_type": self.name}):
			user = jingrow.get_cached_pg("User", row.name)
			self.update_roles_in_user(user)
			self.update_modules_in_user(user)
			user.update_children()

	def update_roles_in_user(self, user):
		user.set("roles", [])
		user.append("roles", {"role": self.role})

	def update_modules_in_user(self, user):
		block_modules = jingrow.get_all(
			"Module Def",
			fields=["name as module"],
			filters={"name": ["not in", [d.module for d in self.user_type_modules]]},
		)

		if block_modules:
			user.set("block_modules", block_modules)

	def add_role_permissions_for_user_pagetypes(self):
		perms = ["read", "write", "create", "submit", "cancel", "amend", "delete", "print", "email", "share"]
		for row in self.user_pagetypes:
			pageperm = add_role_permissions(row.document_type, self.role)
			values = {perm: row.get(perm, default=0) for perm in perms}

			jingrow.db.set_value("Custom PagePerm", pageperm, values)

	def add_select_perm_pagetypes(self):
		if jingrow.flags.ignore_select_perm:
			return

		self.select_pagetypes = []

		select_pagetypes = []
		user_pagetypes = [row.document_type for row in self.user_pagetypes]

		for pagetype in user_pagetypes:
			pg = jingrow.get_meta(pagetype)
			self.prepare_select_perm_pagetypes(pg, user_pagetypes, select_pagetypes)

			for child_table in pg.get_table_fields():
				child_pg = jingrow.get_meta(child_table.options)
				if child_pg:
					self.prepare_select_perm_pagetypes(child_pg, user_pagetypes, select_pagetypes)

		if select_pagetypes:
			select_pagetypes = set(select_pagetypes)
			for select_pagetype in select_pagetypes:
				self.append("select_pagetypes", {"document_type": select_pagetype})

	def prepare_select_perm_pagetypes(self, pg, user_pagetypes, select_pagetypes):
		for field in pg.get_link_fields():
			if field.options not in user_pagetypes:
				select_pagetypes.append(field.options)

	def add_role_permissions_for_select_pagetypes(self):
		for pagetype in ["select_pagetypes", "custom_select_pagetypes"]:
			for row in self.get(pagetype):
				pageperm = add_role_permissions(row.document_type, self.role)
				jingrow.db.set_value(
					"Custom PagePerm", pageperm, {"select": 1, "read": 0, "create": 0, "write": 0}
				)

	def add_role_permissions_for_file(self):
		pageperm = add_role_permissions("File", self.role)
		jingrow.db.set_value("Custom PagePerm", pageperm, {"read": 1, "create": 1, "write": 1})

	def remove_permission_for_deleted_pagetypes(self):
		pagetypes = [d.document_type for d in self.user_pagetypes]

		# Do not remove the pg permission for the file pagetype
		pagetypes.append("File")

		for pagetype in ["select_pagetypes", "custom_select_pagetypes"]:
			pagetypes.extend(dt.document_type for dt in self.get(pagetype))
		for perm in jingrow.get_all(
			"Custom PagePerm", filters={"role": self.role, "parent": ["not in", pagetypes]}
		):
			jingrow.delete_pg("Custom PagePerm", perm.name)


def add_role_permissions(pagetype, role):
	name = jingrow.get_value("Custom PagePerm", dict(parent=pagetype, role=role, permlevel=0))

	if not name:
		name = add_permission(pagetype, role, 0)

	return name


def get_non_standard_user_types():
	user_types = jingrow.get_all(
		"User Type",
		fields=["apply_user_permission_on", "name", "user_id_field"],
		filters={"is_standard": 0},
	)

	return {d.name: [d.apply_user_permission_on, d.user_id_field] for d in user_types}


@jingrow.whitelist()
@jingrow.validate_and_sanitize_search_inputs
def get_user_linked_pagetypes(pagetype, txt, searchfield, start, page_len, filters):
	modules = [d.get("module_name") for d in get_modules_from_app("jingrow")]

	filters = [
		["PageField", "options", "=", "User"],
		["PageType", "is_submittable", "=", 0],
		["PageType", "issingle", "=", 0],
		["PageType", "module", "not in", modules],
		["PageType", "read_only", "=", 0],
		["PageType", "name", "like", f"%{txt}%"],
	]

	pagetypes = jingrow.get_all(
		"PageType",
		fields=["`tabPageType`.`name`"],
		filters=filters,
		order_by="`tabPageType`.`idx` desc",
		limit_start=start,
		limit_page_length=page_len,
		as_list=1,
	)

	custom_dt_filters = [
		["Custom Field", "dt", "like", f"%{txt}%"],
		["Custom Field", "options", "=", "User"],
		["Custom Field", "fieldtype", "=", "Link"],
	]

	custom_pagetypes = jingrow.get_all(
		"Custom Field", fields=["dt as name"], filters=custom_dt_filters, as_list=1
	)

	return pagetypes + custom_pagetypes


@jingrow.whitelist()
def get_user_id(parent):
	data = (
		jingrow.get_all(
			"PageField",
			fields=["label", "fieldname as value"],
			filters={"options": "User", "fieldtype": "Link", "parent": parent},
		)
		or []
	)

	data.extend(
		jingrow.get_all(
			"Custom Field",
			fields=["label", "fieldname as value"],
			filters={"options": "User", "fieldtype": "Link", "dt": parent},
		)
	)

	return data


def user_linked_with_permission_on_pagetype(pg, user):
	if not pg.apply_user_permission_on:
		return True

	if not pg.user_id_field:
		jingrow.throw(_("User Id Field is mandatory in the user type {0}").format(jingrow.bold(pg.name)))

	if jingrow.db.get_value(pg.apply_user_permission_on, {pg.user_id_field: user}, "name"):
		return True
	else:
		label = jingrow.get_meta(pg.apply_user_permission_on).get_field(pg.user_id_field).label

		jingrow.msgprint(
			_(
				"To set the role {0} in the user {1}, kindly set the {2} field as {3} in one of the {4} record."
			).format(
				jingrow.bold(pg.role),
				jingrow.bold(user),
				jingrow.bold(label),
				jingrow.bold(user),
				jingrow.bold(pg.apply_user_permission_on),
			)
		)

		return False


def apply_permissions_for_non_standard_user_type(pg, method=None):
	"""Create user permission for the non standard user type"""
	if not jingrow.db.table_exists("User Type") or jingrow.flags.in_migrate:
		return

	user_types = jingrow.cache.get_value(
		"non_standard_user_types",
		get_non_standard_user_types,
	)

	if not user_types:
		return

	for user_type, data in user_types.items():
		if not pg.get(data[1]) or pg.pagetype != data[0]:
			continue

		if jingrow.get_cached_value("User", pg.get(data[1]), "user_type") != user_type:
			return

		if pg.get(data[1]) and (
			not pg._pg_before_save
			or pg.get(data[1]) != pg._pg_before_save.get(data[1])
			or not jingrow.db.get_value(
				"User Permission", {"user": pg.get(data[1]), "allow": data[0], "for_value": pg.name}, "name"
			)
		):
			perm_data = jingrow.db.get_value(
				"User Permission", {"allow": pg.pagetype, "for_value": pg.name}, ["name", "user"]
			)

			if not perm_data:
				user_pg = jingrow.get_cached_pg("User", pg.get(data[1]))
				user_pg.set_roles_and_modules_based_on_user_type()
				user_pg.update_children()
				add_user_permission(pg.pagetype, pg.name, pg.get(data[1]))
			else:
				jingrow.db.set_value("User Permission", perm_data[0], "user", pg.get(data[1]))
