# Copyright (c) 2021, JINGROW and Contributors
# See LICENSE
import jingrow
from jingrow.core.pagetype.pagetype.test_pagetype import new_pagetype
from jingrow.core.pagetype.user_permission.user_permission import (
	add_user_permissions,
	remove_applicable,
)
from jingrow.permissions import add_permission, has_user_permission
from jingrow.tests.utils import JingrowTestCase
from jingrow.website.pagetype.blog_post.test_blog_post import make_test_blog


class TestUserPermission(JingrowTestCase):
	def setUp(self):
		test_users = (
			"test_bulk_creation_update@example.com",
			"test_user_perm1@example.com",
			"nested_pg_user@example.com",
		)
		jingrow.db.delete("User Permission", {"user": ("in", test_users)})
		jingrow.delete_pg_if_exists("PageType", "Person")
		jingrow.db.sql_ddl("DROP TABLE IF EXISTS `tabPerson`")
		jingrow.delete_pg_if_exists("PageType", "Pg A")
		jingrow.db.sql_ddl("DROP TABLE IF EXISTS `tabDoc A`")

	def test_default_user_permission_validation(self):
		user = create_user("test_default_permission@example.com")
		param = get_params(user, "User", user.name, is_default=1)
		add_user_permissions(param)
		# create a duplicate entry with default
		perm_user = create_user("test_user_perm@example.com")
		param = get_params(user, "User", perm_user.name, is_default=1)
		self.assertRaises(jingrow.ValidationError, add_user_permissions, param)

	def test_default_user_permission_corectness(self):
		user = create_user("test_default_corectness_permission_1@example.com")
		param = get_params(user, "User", user.name, is_default=1, hide_descendants=1)
		add_user_permissions(param)
		# create a duplicate entry with default
		perm_user = create_user("test_default_corectness2@example.com")
		test_blog = make_test_blog()
		param = get_params(perm_user, "Blog Post", test_blog.name, is_default=1, hide_descendants=1)
		add_user_permissions(param)
		jingrow.db.delete("User Permission", filters={"for_value": test_blog.name})
		jingrow.delete_pg("Blog Post", test_blog.name)

	def test_default_user_permission(self):
		jingrow.set_user("Administrator")
		user = create_user("test_user_perm1@example.com", "Website Manager")
		for category in ["general", "public"]:
			if not jingrow.db.exists("Blog Category", category):
				jingrow.get_pg({"pagetype": "Blog Category", "title": category}).insert()

		param = get_params(user, "Blog Category", "general", is_default=1)
		add_user_permissions(param)

		param = get_params(user, "Blog Category", "public")
		add_user_permissions(param)

		jingrow.set_user("test_user_perm1@example.com")
		pg = jingrow.new_pg("Blog Post")

		self.assertEqual(pg.blog_category, "general")
		jingrow.set_user("Administrator")

	def test_apply_to_all(self):
		"""Create User permission for User having access to all applicable Pagetypes"""
		user = create_user("test_bulk_creation_update@example.com")
		param = get_params(user, "User", user.name)
		is_created = add_user_permissions(param)
		self.assertEqual(is_created, 1)

	def test_for_apply_to_all_on_update_from_apply_all(self):
		user = create_user("test_bulk_creation_update@example.com")
		param = get_params(user, "User", user.name)

		# Initially create User Permission document with apply_to_all checked
		is_created = add_user_permissions(param)

		self.assertEqual(is_created, 1)
		is_created = add_user_permissions(param)

		# User Permission should not be changed
		self.assertEqual(is_created, 0)

	def test_for_applicable_on_update_from_apply_to_all(self):
		"""Update User Permission from all to some applicable Pagetypes"""
		user = create_user("test_bulk_creation_update@example.com")
		param = get_params(user, "User", user.name, applicable=["Comment", "Contact"])

		# Initially create User Permission document with apply_to_all checked
		is_created = add_user_permissions(get_params(user, "User", user.name))

		self.assertEqual(is_created, 1)

		is_created = add_user_permissions(param)
		jingrow.db.commit()

		removed_apply_to_all = jingrow.db.exists("User Permission", get_exists_param(user))
		is_created_applicable_first = jingrow.db.exists(
			"User Permission", get_exists_param(user, applicable="Comment")
		)
		is_created_applicable_second = jingrow.db.exists(
			"User Permission", get_exists_param(user, applicable="Contact")
		)

		# Check that apply_to_all is removed
		self.assertIsNone(removed_apply_to_all)

		# Check that User Permissions for applicable is created
		self.assertIsNotNone(is_created_applicable_first)
		self.assertIsNotNone(is_created_applicable_second)
		self.assertEqual(is_created, 1)

	def test_for_apply_to_all_on_update_from_applicable(self):
		"""Update User Permission from some to all applicable Pagetypes"""
		user = create_user("test_bulk_creation_update@example.com")
		param = get_params(user, "User", user.name)

		# create User permissions that with applicable
		is_created = add_user_permissions(
			get_params(user, "User", user.name, applicable=["Comment", "Contact"])
		)

		self.assertEqual(is_created, 1)

		is_created = add_user_permissions(param)
		is_created_apply_to_all = jingrow.db.exists("User Permission", get_exists_param(user))
		removed_applicable_first = jingrow.db.exists(
			"User Permission", get_exists_param(user, applicable="Comment")
		)
		removed_applicable_second = jingrow.db.exists(
			"User Permission", get_exists_param(user, applicable="Contact")
		)

		# To check that a User permission with apply_to_all exists
		self.assertIsNotNone(is_created_apply_to_all)

		# Check that all User Permission with applicable is removed
		self.assertIsNone(removed_applicable_first)
		self.assertIsNone(removed_applicable_second)
		self.assertEqual(is_created, 1)

	def test_user_perm_for_nested_pagetype(self):
		"""Test if descendants' visibility is controlled for a nested PageType."""
		from jingrow.core.pagetype.pagetype.test_pagetype import new_pagetype

		user = create_user("nested_pg_user@example.com", "Blogger")
		if not jingrow.db.exists("PageType", "Person"):
			pg = new_pagetype(
				"Person",
				fields=[{"label": "Person Name", "fieldname": "person_name", "fieldtype": "Data"}],
				unique=0,
			)
			pg.is_tree = 1
			pg.insert()

		parent_record = jingrow.get_pg({"pagetype": "Person", "person_name": "Parent", "is_group": 1}).insert()

		child_record = jingrow.get_pg(
			{
				"pagetype": "Person",
				"person_name": "Child",
				"is_group": 0,
				"parent_person": parent_record.name,
			}
		).insert()

		add_user_permissions(get_params(user, "Person", parent_record.name))

		# check if adding perm on a group record, makes child record visible
		self.assertTrue(has_user_permission(jingrow.get_pg("Person", parent_record.name), user.name))
		self.assertTrue(has_user_permission(jingrow.get_pg("Person", child_record.name), user.name))

		#  give access of Parent PageType to Blogger role
		add_permission("Person", "Blogger")
		jingrow.set_user(user.name)
		visible_names = jingrow.get_list(
			pagetype="Person",
			pluck="person_name",
		)

		user_permission = jingrow.get_pg(
			"User Permission", {"allow": "Person", "for_value": parent_record.name}
		)
		user_permission.hide_descendants = 1
		user_permission.save(ignore_permissions=True)

		# check if adding perm on a group record with hide_descendants enabled,
		# hides child records
		self.assertTrue(has_user_permission(jingrow.get_pg("Person", parent_record.name), user.name))
		self.assertFalse(has_user_permission(jingrow.get_pg("Person", child_record.name), user.name))

		visible_names_after_hide_descendants = jingrow.get_list(
			"Person",
			pluck="person_name",
		)
		self.assertEqual(visible_names, ["Child", "Parent"])
		self.assertEqual(visible_names_after_hide_descendants, ["Parent"])
		jingrow.set_user("Administrator")

	def test_user_perm_on_new_pg_with_field_default(self):
		"""Test User Perm impact on jingrow.new_pg. with *field* default value"""
		jingrow.set_user("Administrator")
		user = create_user("new_pg_test@example.com", "Blogger")

		# make a pagetype "Pg A" with 'pagetype' link field and default value ToDo
		if not jingrow.db.exists("PageType", "Pg A"):
			pg = new_pagetype(
				"Pg A",
				fields=[
					{
						"label": "PageType",
						"fieldname": "pg",
						"fieldtype": "Link",
						"options": "PageType",
						"default": "ToDo",
					}
				],
				unique=0,
			)
			pg.insert()

		# make User Perm on PageType 'ToDo' in Assignment Rule (unrelated pagetype)
		add_user_permissions(get_params(user, "PageType", "ToDo", applicable=["Assignment Rule"]))
		jingrow.set_user("new_pg_test@example.com")

		new_pg = jingrow.new_pg("Pg A")

		# User perm is created on ToDo but for pagetype Assignment Rule only
		# it should not have impact on Pg A
		self.assertEqual(new_pg.pg, "ToDo")

		jingrow.set_user("Administrator")
		remove_applicable(["Assignment Rule"], "new_pg_test@example.com", "PageType", "ToDo")

	def test_user_perm_on_new_pg_with_user_default(self):
		"""Test User Perm impact on jingrow.new_pg. with *user* default value"""
		from jingrow.core.pagetype.session_default_settings.session_default_settings import (
			clear_session_defaults,
			set_session_default_values,
		)

		jingrow.set_user("Administrator")
		user = create_user("user_default_test@example.com", "Blogger")

		# make a pagetype "Pg A" with 'pagetype' link field
		if not jingrow.db.exists("PageType", "Pg A"):
			pg = new_pagetype(
				"Pg A",
				fields=[
					{
						"label": "PageType",
						"fieldname": "pg",
						"fieldtype": "Link",
						"options": "PageType",
					}
				],
				unique=0,
			)
			pg.insert()

		# create a 'PageType' session default field
		if not jingrow.db.exists("Session Default", {"ref_pagetype": "PageType"}):
			settings = jingrow.get_single("Session Default Settings")
			settings.append("session_defaults", {"ref_pagetype": "PageType"})
			settings.save()

		# make User Perm on PageType 'ToDo' in Assignment Rule (unrelated pagetype)
		add_user_permissions(get_params(user, "PageType", "ToDo", applicable=["Assignment Rule"]))

		# User default Pagetype value is ToDo via Session Defaults
		jingrow.set_user("user_default_test@example.com")
		set_session_default_values({"pg": "ToDo"})

		new_pg = jingrow.new_pg("Pg A")

		# User perm is created on ToDo but for pagetype Assignment Rule only
		# it should not have impact on Pg A
		self.assertEqual(new_pg.pg, "ToDo")

		jingrow.set_user("Administrator")
		clear_session_defaults()
		remove_applicable(["Assignment Rule"], "user_default_test@example.com", "PageType", "ToDo")


def create_user(email, *roles):
	"""create user with role system manager"""
	if jingrow.db.exists("User", email):
		return jingrow.get_pg("User", email)

	user = jingrow.new_pg("User")
	user.email = email
	user.first_name = email.split("@", 1)[0]

	if not roles:
		roles = ("System Manager",)

	user.add_roles(*roles)
	return user


def get_params(user, pagetype, pagename, is_default=0, hide_descendants=0, applicable=None):
	"""Return param to insert"""
	param = {
		"user": user.name,
		"pagetype": pagetype,
		"pagename": pagename,
		"is_default": is_default,
		"apply_to_all_pagetypes": 1,
		"applicable_pagetypes": [],
		"hide_descendants": hide_descendants,
	}
	if applicable:
		param.update({"apply_to_all_pagetypes": 0})
		param.update({"applicable_pagetypes": applicable})
	return param


def get_exists_param(user, applicable=None):
	"""param to check existing Page"""
	param = {
		"user": user.name,
		"allow": "User",
		"for_value": user.name,
	}
	if applicable:
		param.update({"applicable_for": applicable})
	else:
		param.update({"apply_to_all_pagetypes": 1})
	return param
