// Copyright (c) 2015, InfluxFramework LLC
// MIT License. See license.txt

influxframework.provide("influxframework.perm");

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

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

	doctype_perm: {},

	has_perm: (doctype, permlevel, ptype, doc) => {
		if (!permlevel) permlevel = 0;
		if (!influxframework.perm.doctype_perm[doctype]) {
			influxframework.perm.doctype_perm[doctype] = influxframework.perm.get_perm(doctype);
		}

		let perms = influxframework.perm.doctype_perm[doctype];

		if (!perms || !perms[permlevel]) return false;

		let perm = !!perms[permlevel][ptype];

		if (permlevel === 0 && perm && doc) {
			let docinfo = influxframework.model.get_docinfo(doctype, doc.name);
			if (docinfo && !docinfo.permissions[ptype]) perm = false;
		}

		return perm;
	},

	get_perm: (doctype, doc) => {
		let perm = [{ read: 0, permlevel: 0 }];

		let meta = influxframework.get_doc("DocType", doctype);
		const user = influxframework.session.user;

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

		if (!meta) return perm;

		perm = influxframework.perm.get_role_permissions(meta);

		if (doc) {
			// apply user permissions via docinfo (which is processed server-side)
			let docinfo = influxframework.model.get_docinfo(doctype, doc.name);
			if (docinfo && docinfo.permissions) {
				Object.keys(docinfo.permissions).forEach((ptype) => {
					perm[0][ptype] = docinfo.permissions[ptype];
				});
			}

			// if owner
			if (!$.isEmptyObject(perm[0].if_owner)) {
				if (doc.owner === user) {
					$.extend(perm[0], perm[0].if_owner);
				} else {
					// not owner, remove permissions
					$.each(perm[0].if_owner, (ptype) => {
						if (perm[0].if_owner[ptype]) {
							perm[0][ptype] = 0;
						}
					});
				}
			}

			// apply permissions from shared
			if (docinfo && docinfo.shared) {
				for (let i = 0; i < docinfo.shared.length; i++) {
					let s = docinfo.shared[i];
					if (s.user === user) {
						perm[0]["read"] = perm[0]["read"] || s.read;
						perm[0]["write"] = perm[0]["write"] || s.write;
						perm[0]["submit"] = perm[0]["submit"] || s.submit;
						perm[0]["share"] = perm[0]["share"] || s.share;

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

		if (influxframework.model.can_read(doctype) && !perm[0].read) {
			// read via sharing
			perm[0].read = 1;
		}

		return perm;
	},

	get_role_permissions: (meta) => {
		let perm = [{ read: 0, permlevel: 0 }];
		// Returns a `dict` of evaluated Role Permissions
		(meta.permissions || []).forEach((p) => {
			// if user has this role
			let permlevel = cint(p.permlevel);
			if (!perm[permlevel]) {
				perm[permlevel] = {};
				perm[permlevel]["permlevel"] = permlevel;
			}

			if (influxframework.user_roles.includes(p.role)) {
				influxframework.perm.rights.forEach((right) => {
					let value = perm[permlevel][right] || p[right] || 0;
					if (value) {
						perm[permlevel][right] = value;
					}
				});
			}
		});

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

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

		if (!ptype) ptype = "read";

		let perm = influxframework.perm.get_perm(doctype);

		let user_permissions = influxframework.defaults.get_user_permissions();

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

		if (perm[0].if_owner && perm[0].read) {
			match_rules.push({ Owner: influxframework.session.user });
		}
		return match_rules;
	},

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

		if (!perm) {
			return df && (cint(df.hidden) || cint(df.hidden_due_to_dependency)) ? "None" : "Write";
		}

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

		// permission
		if (p) {
			if (p.write && !df.disabled) {
				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 (!doc) {
			return status;
		}

		// submit
		if (status === "Write" && cint(doc.docstatus) > 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(doc.docstatus) === 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) ||
				in_list(cur_frm.states.update_fields, 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 && !doc.__islocal) {
			status = "Read";
		}
		if (explain) console.log("By Set Only Once:" + status);

		return status;
	},

	is_visible: (df, doc, perm) => {
		if (typeof df === "string") {
			// df is fieldname
			df = influxframework.meta.get_docfield(doc.doctype, df, doc.parent || doc.name);
		}

		let status = influxframework.perm.get_field_display_status(df, doc, perm);

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

	get_allowed_docs_for_doctype: (user_permissions, doctype) => {
		// returns docs from the list of user permissions that are allowed under provided doctype
		return influxframework.perm.filter_allowed_docs_for_doctype(user_permissions, doctype, false);
	},

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

		const allowed_docs = filtered_perms.map((perm) => perm.doc);

		if (with_default_doc) {
			const default_doc =
				allowed_docs.length === 1
					? allowed_docs
					: filtered_perms.filter((perm) => perm.is_default).map((record) => record.doc);

			return {
				allowed_records: allowed_docs,
				default_doc: default_doc[0],
			};
		} else {
			return allowed_docs;
		}
	},
});
