// Copyright (c) 2015, JINGROW and Contributors
// MIT License. See license.txt

jingrow.provide("jingrow.perm");

// backward compatibilty
Object.assign(window, {
	READ: "read",
	WRITE: "write",
	CREATE: "create",
	DELETE: "delete",
	SUBMIT: "submit",
	CANCEL: "cancel",
	AMEND: "amend",
});

$.extend(jingrow.perm, {
	rights: [
		"select",
		"read",
		"write",
		"create",
		"delete",
		"submit",
		"cancel",
		"amend",
		"report",
		"import",
		"export",
		"print",
		"email",
		"share",
	],

	pagetype_perm: {},

	has_perm: (pagetype, permlevel = 0, ptype = "read", pg) => {
		const perms = jingrow.perm.get_perm(pagetype, pg);
		return !!perms?.[permlevel]?.[ptype];
	},

	get_perm: (pagetype, pg) => {
		// if document object is passed, get fresh pg based perms
		// (with ownership and user perms applied) else cached pagetype perms

		if (pg && !pg.__islocal) {
			return jingrow.perm._get_perm(pagetype, pg);
		}

		return (jingrow.perm.pagetype_perm[pagetype] ??= jingrow.perm._get_perm(pagetype));
	},

	_get_perm: (pagetype, pg) => {
		let perm = [{ read: 0, permlevel: 0 }];

		let meta = jingrow.get_pg("PageType", pagetype);
		const user = jingrow.session.user;

		if (user === "Administrator" || jingrow.user_roles.includes("Administrator")) {
			perm[0].read = 1;
		}

		if (!meta) return perm;

		perm = jingrow.perm.get_role_permissions(meta);
		const base_perm = perm[0];

		if (pg) {
			// apply user permissions via pageinfo (which is processed server-side)
			let pageinfo = jingrow.model.get_pageinfo(pagetype, pg.name);
			if (pageinfo && pageinfo.permissions) {
				Object.keys(pageinfo.permissions).forEach((ptype) => {
					base_perm[ptype] = pageinfo.permissions[ptype];
				});
			}

			// if owner
			if (pg.owner !== user) {
				for (const right of jingrow.perm.rights) {
					if (base_perm[right] && !base_perm.rights_without_if_owner.has(right)) {
						base_perm[right] = 0;
					}
				}
			}

			// apply permissions from shared
			if (pageinfo && pageinfo.shared) {
				for (const s of pageinfo.shared) {
					if (s.user !== user) continue;

					for (const right of ["read", "write", "submit", "share"]) {
						if (!base_perm[right]) base_perm[right] = s[right];
					}

					if (s.read) {
						// also give print, email permissions if read
						// and these permissions exist at level [0]
						base_perm.email =
							jingrow.boot.user.can_email.indexOf(pagetype) !== -1 ? 1 : 0;
						base_perm.print =
							jingrow.boot.user.can_print.indexOf(pagetype) !== -1 ? 1 : 0;
					}
				}
			}
		}

		if (!base_perm.read && jingrow.model.can_read(pagetype)) {
			// read via sharing
			base_perm.read = 1;
		}

		return perm;
	},

	get_role_permissions: (meta) => {
		/** Returns a `dict` of evaluated Role Permissions like:
		{
			"read": 1,
			"write": 0,
			"rights_without_if_owner": {"read", "write"}  // for permlevel 0
		}
		*/

		let perm = [{ read: 0, permlevel: 0 }];

		(meta.permissions || []).forEach((p) => {
			const permlevel = cint(p.permlevel);
			const current_perm = (perm[permlevel] ??= { permlevel });

			if (permlevel === 0) {
				current_perm.rights_without_if_owner ??= new Set();
			}

			// if user has this role
			if (jingrow.user_roles.includes(p.role)) {
				jingrow.perm.rights.forEach((right) => {
					if (!p[right]) return;

					current_perm[right] = 1;

					if (permlevel === 0 && !p.if_owner) {
						current_perm.rights_without_if_owner.add(right);
					}
				});
			}
		});

		// fill gaps with empty object
		perm = perm.map((p) => p || {});
		return perm;
	},

	get_match_rules: (pagetype, ptype) => {
		let match_rules = [];

		if (!ptype) ptype = "read";

		let perm = jingrow.perm.get_perm(pagetype);

		let user_permissions = jingrow.defaults.get_user_permissions();

		if (user_permissions && !$.isEmptyObject(user_permissions)) {
			let rules = {};
			let fields_to_check = jingrow.meta.get_fields_to_check_permissions(pagetype);
			$.each(fields_to_check, (i, df) => {
				const user_permissions_for_pagetype = user_permissions[df.options] || [];
				const allowed_records = jingrow.perm.get_allowed_pgs_for_pagetype(
					user_permissions_for_pagetype,
					pagetype
				);
				if (allowed_records.length) {
					rules[df.label] = allowed_records;
				}
			});
			if (!$.isEmptyObject(rules)) {
				match_rules.push(rules);
			}
		}

		const base_perm = perm[0];
		if (base_perm.read && !base_perm.rights_without_if_owner.has("read")) {
			match_rules.push({ Owner: jingrow.session.user });
		}
		return match_rules;
	},

	get_field_display_status: (df, pg, perm, explain) => {
		// returns the display status of a particular field
		// returns one of "Read", "Write" or "None"
		if (!perm && pg) {
			perm = jingrow.perm.get_perm(pg.pagetype, pg);
		}

		if (!perm) {
			let is_hidden = df && (cint(df.hidden) || cint(df.hidden_due_to_dependency));
			let is_read_only = df && (cint(df.read_only) || cint(df.is_virtual));
			return is_hidden ? "None" : is_read_only ? "Read" : "Write";
		}

		if (!df.permlevel) df.permlevel = 0;
		let p = perm[df.permlevel];
		let status = "None";

		// permission
		if (p) {
			if (p.write && !df.disabled && !df.is_virtual) {
				status = "Write";
			} else if (p.read) {
				status = "Read";
			}
		}
		if (explain) console.log("By Permission:" + status);

		// hidden
		if (cint(df.hidden)) status = "None";
		if (explain) console.log("By Hidden:" + status);

		// hidden due to dependency
		if (cint(df.hidden_due_to_dependency)) status = "None";
		if (explain) console.log("By Hidden Due To Dependency:" + status);

		if (!pg) {
			return status;
		}

		// submit
		if (status === "Write" && cint(pg.pagestatus) > 0) status = "Read";
		if (explain) console.log("By Submit:" + status);

		// allow on submit
		// let allow_on_submit = df.fieldtype==="Table" ? 0 : cint(df.allow_on_submit);
		let allow_on_submit = cint(df.allow_on_submit);
		if (status === "Read" && allow_on_submit && cint(pg.pagestatus) === 1 && p.write) {
			status = "Write";
		}
		if (explain) console.log("By Allow on Submit:" + status);

		// workflow state
		if (status === "Read" && cur_frm && cur_frm.state_fieldname) {
			// fields updated by workflow must be read-only
			if (
				cint(cur_frm.read_only) ||
				cur_frm.states.update_fields.includes(df.fieldname) ||
				df.fieldname == cur_frm.state_fieldname
			) {
				status = "Read";
			}
		}
		if (explain) console.log("By Workflow:" + status);

		// read only field is checked
		if (status === "Write" && (cint(df.read_only) || df.fieldtype === "Read Only")) {
			status = "Read";
		}
		if (explain) console.log("By Read Only:" + status);

		if (status === "Write" && df.set_only_once && !pg.__islocal) {
			status = "Read";
		}
		if (explain) console.log("By Set Only Once:" + status);

		return status;
	},

	is_visible: (df, pg, perm) => {
		if (typeof df === "string") {
			// df is fieldname
			df = jingrow.meta.get_pagefield(pg.pagetype, df, pg.parent || pg.name);
		}

		let status = jingrow.perm.get_field_display_status(df, pg, perm);

		return status === "None" ? false : true;
	},

	get_allowed_pgs_for_pagetype: (user_permissions, pagetype) => {
		// returns pgs from the list of user permissions that are allowed under provided pagetype
		return jingrow.perm.filter_allowed_pgs_for_pagetype(user_permissions, pagetype, false);
	},

	filter_allowed_pgs_for_pagetype: (user_permissions, pagetype, with_default_pg = true) => {
		// returns pgs from the list of user permissions that are allowed under provided pagetype
		// also returns default pg when with_default_pg is set
		const filtered_perms = (user_permissions || []).filter((perm) => {
			return perm.applicable_for === pagetype || !perm.applicable_for;
		});

		const allowed_pgs = filtered_perms.map((perm) => perm.pg);

		if (with_default_pg) {
			const default_pg = filtered_perms
				.filter((perm) => perm.is_default)
				.map((record) => record.pg);

			return {
				allowed_records: allowed_pgs,
				default_pg: default_pg[0],
			};
		} else {
			return allowed_pgs;
		}
	},
});
