/* globals slug:true, slugify, LDAP, getLdapUsername:true, getLdapUserUniqueID:true, getDataToSyncUserData:true, syncUserData:true, sync:true, addLdapUser:true  */

const logger = new Logger('LDAPSync', {});

slug = function slug(text) {
	if (RocketChat.settings.get('UTF8_Names_Slugify') !== true) {
		return text;
	}
	text = slugify(text, '.');
	return text.replace(/[^0-9a-z-_.]/g, '');
};


getLdapUsername = function getLdapUsername(ldapUser) {
	const usernameField = RocketChat.settings.get('LDAP_Username_Field');

	if (usernameField.indexOf('#{') > -1) {
		return usernameField.replace(/#{(.+?)}/g, function(match, field) {
			return ldapUser.object[field];
		});
	}

	return ldapUser.object[usernameField];
};


getLdapUserUniqueID = function getLdapUserUniqueID(ldapUser) {
	let Unique_Identifier_Field = RocketChat.settings.get('LDAP_Unique_Identifier_Field');

	if (Unique_Identifier_Field !== '') {
		Unique_Identifier_Field = Unique_Identifier_Field.replace(/\s/g, '').split(',');
	} else {
		Unique_Identifier_Field = [];
	}

	let LDAP_Domain_Search_User_ID = RocketChat.settings.get('LDAP_Domain_Search_User_ID');

	if (LDAP_Domain_Search_User_ID !== '') {
		LDAP_Domain_Search_User_ID = LDAP_Domain_Search_User_ID.replace(/\s/g, '').split(',');
	} else {
		LDAP_Domain_Search_User_ID = [];
	}

	Unique_Identifier_Field = Unique_Identifier_Field.concat(LDAP_Domain_Search_User_ID);

	if (Unique_Identifier_Field.length > 0) {
		Unique_Identifier_Field = Unique_Identifier_Field.find((field) => {
			return !_.isEmpty(ldapUser.object[field]);
		});
		if (Unique_Identifier_Field) {
			Unique_Identifier_Field = {
				attribute: Unique_Identifier_Field,
				value: ldapUser.raw[Unique_Identifier_Field].toString('hex')
			};
		}
		return Unique_Identifier_Field;
	}
};


getDataToSyncUserData = function getDataToSyncUserData(ldapUser, user) {
	const syncUserData = RocketChat.settings.get('LDAP_Sync_User_Data');
	const syncUserDataFieldMap = RocketChat.settings.get('LDAP_Sync_User_Data_FieldMap').trim();

	if (syncUserData && syncUserDataFieldMap) {
		const fieldMap = JSON.parse(syncUserDataFieldMap);
		const userData = {};
		const emailList = [];
		_.map(fieldMap, function(userField, ldapField) {
			switch (userField) {
				case 'email':
					if (!ldapUser.object.hasOwnProperty(ldapField)) {
						logger.debug(`user does not have attribute: ${ ldapField }`);
						return;
					}

					if (_.isObject(ldapUser.object[ldapField])) {
						_.map(ldapUser.object[ldapField], function(item) {
							emailList.push({ address: item, verified: true });
						});
					} else {
						emailList.push({ address: ldapUser.object[ldapField], verified: true });
					}
					break;

				case 'name':
					const templateRegex = /#{(\w+)}/gi;
					let match = templateRegex.exec(ldapField);
					let tmpLdapField = ldapField;

					if (match == null) {
						if (!ldapUser.object.hasOwnProperty(ldapField)) {
							logger.debug(`user does not have attribute: ${ ldapField }`);
							return;
						}
						tmpLdapField = ldapUser.object[ldapField];
					} else {
						logger.debug('template found. replacing values');
						while (match != null) {
							const tmplVar = match[0];
							const tmplAttrName = match[1];

							if (!ldapUser.object.hasOwnProperty(tmplAttrName)) {
								logger.debug(`user does not have attribute: ${ tmplAttrName }`);
								return;
							}

							const attrVal = ldapUser.object[tmplAttrName];
							logger.debug(`replacing template var: ${ tmplVar } with value from ldap: ${ attrVal }`);
							tmpLdapField = tmpLdapField.replace(tmplVar, attrVal);
							match = templateRegex.exec(ldapField);
						}
					}

					if (user.name !== tmpLdapField) {
						userData.name = tmpLdapField;
						logger.debug(`user.name changed to: ${ tmpLdapField }`);
					}
					break;
			}
		});

		if (emailList.length > 0) {
			if (JSON.stringify(user.emails) !== JSON.stringify(emailList)) {
				userData.emails = emailList;
			}
		}

		const uniqueId = getLdapUserUniqueID(ldapUser);

		if (uniqueId && (!user.services || !user.services.ldap || user.services.ldap.id !== uniqueId.value || user.services.ldap.idAttribute !== uniqueId.attribute)) {
			userData['services.ldap.id'] = uniqueId.value;
			userData['services.ldap.idAttribute'] = uniqueId.attribute;
		}

		if (user.ldap !== true) {
			userData.ldap = true;
		}

		if (_.size(userData)) {
			return userData;
		}
	}
};


syncUserData = function syncUserData(user, ldapUser) {
	logger.info('Syncing user data');
	logger.debug('user', {'email': user.email, '_id': user._id});
	logger.debug('ldapUser', ldapUser);

	const userData = getDataToSyncUserData(ldapUser, user);
	if (user && user._id && userData) {
		logger.debug('setting', JSON.stringify(userData, null, 2));
		if (userData.name) {
			RocketChat._setRealName(user._id, userData.name);
			delete userData.name;
		}
		Meteor.users.update(user._id, { $set: userData });
		user = Meteor.users.findOne({_id: user._id});
	}

	if (RocketChat.settings.get('LDAP_Username_Field') !== '') {
		const username = slug(getLdapUsername(ldapUser));
		if (user && user._id && username !== user.username) {
			logger.info('Syncing user username', user.username, '->', username);
			RocketChat._setUsername(user._id, username);
		}
	}

	if (user && user._id && RocketChat.settings.get('LDAP_Sync_User_Avatar') === true) {
		const avatar = ldapUser.raw.thumbnailPhoto || ldapUser.raw.jpegPhoto;
		if (avatar) {
			logger.info('Syncing user avatar');
			const rs = RocketChatFile.bufferToStream(avatar);
			RocketChatFileAvatarInstance.deleteFile(encodeURIComponent(`${ user.username }.jpg`));
			const ws = RocketChatFileAvatarInstance.createWriteStream(encodeURIComponent(`${ user.username }.jpg`), 'image/jpeg');
			ws.on('end', Meteor.bindEnvironment(function() {
				Meteor.setTimeout(function() {
					RocketChat.models.Users.setAvatarOrigin(user._id, 'ldap');
					RocketChat.Notifications.notifyLogged('updateAvatar', {username: user.username});
				}, 500);
			}));
			rs.pipe(ws);
		}
	}
};

addLdapUser = function addLdapUser(ldapUser, username, password) {
	const userObject = {
		username
	};

	const userData = getDataToSyncUserData(ldapUser, {});

	if (userData && userData.emails) {
		userObject.email = userData.emails[0].address;
	} else if (ldapUser.object.mail && ldapUser.object.mail.indexOf('@') > -1) {
		userObject.email = ldapUser.object.mail;
	} else if (RocketChat.settings.get('LDAP_Default_Domain') !== '') {
		userObject.email = `${ username }@${ RocketChat.settings.get('LDAP_Default_Domain') }`;
	} else {
		const error = new Meteor.Error('LDAP-login-error', 'LDAP Authentication succeded, there is no email to create an account. Have you tried setting your Default Domain in LDAP Settings?');
		logger.error(error);
		throw error;
	}

	logger.debug('New user data', userObject);

	if (password) {
		userObject.password = password;
	}

	try {
		userObject._id = Accounts.createUser(userObject);
	} catch (error) {
		logger.error('Error creating user', error);
		throw error;
	}

	syncUserData(userObject, ldapUser);

	return {
		userId: userObject._id
	};
};

sync = function sync() {
	if (RocketChat.settings.get('LDAP_Enable') !== true) {
		return;
	}

	const ldap = new LDAP();

	try {
		ldap.connectSync();

		const users = RocketChat.models.Users.findLDAPUsers();

		if (RocketChat.settings.get('LDAP_Import_Users') === true && RocketChat.settings.get('LDAP_Username_Field') !== '') {
			const ldapUsers = ldap.searchUsersSync('*');
			ldapUsers.forEach(function(ldapUser) {
				const username = slug(getLdapUsername(ldapUser));
				// Look to see if user already exists
				const userQuery = {
					username
				};

				logger.debug('userQuery', userQuery);

				const user = Meteor.users.findOne(userQuery);

				if (!user) {
					addLdapUser(ldapUser, username);
				} else if (user.ldap !== true && RocketChat.settings.get('LDAP_Merge_Existing_Users') === true) {
					syncUserData(user, ldapUser);
				}
			});
		}

		users.forEach(function(user) {
			let ldapUser;

			if (user.services && user.services.ldap && user.services.ldap.id) {
				ldapUser = ldap.getUserByIdSync(user.services.ldap.id, user.services.ldap.idAttribute);
			} else {
				ldapUser = ldap.getUserByUsernameSync(user.username);
			}

			if (ldapUser) {
				syncUserData(user, ldapUser);
			} else {
				logger.info('Can\'t sync user', user.username);
			}
		});
	} catch (error) {
		logger.error(error);
		return error;
	}

	ldap.disconnect();
	return true;
};

let interval;
let timeout;

RocketChat.settings.get('LDAP_Sync_User_Data', function(key, value) {
	Meteor.clearInterval(interval);
	Meteor.clearTimeout(timeout);

	if (value === true) {
		logger.info('Enabling LDAP user sync');
		interval = Meteor.setInterval(sync, 1000 * 60 * 60);
		timeout = Meteor.setTimeout(function() {
			sync();
		}, 1000 * 60 * 10);
	} else {
		logger.info('Disabling LDAP user sync');
	}
});