/**
 * MachForm
 * 
 * © 2007–2025 Appnitro Software. All rights reserved.
 * 
 * This source code is proprietary and may not be copied, modified,
 * or distributed in any form without express written permission from Appnitro Software.
 * 
 * License information: https://www.machform.com/license-agreement/
 */

// Common passwords list for pattern detection (OWASP recommendation)
var commonPasswords = [
	'password', 'password1', 'password123', '123456', '12345678', '123456789', '1234567890',
	'qwerty', 'qwerty123', 'abc123', 'monkey', 'letmein', 'dragon', 'master', 'admin',
	'welcome', 'login', 'princess', 'starwars', 'passw0rd', 'shadow', 'sunshine', 'football',
	'iloveyou', 'trustno1', 'batman', 'superman', 'michael', 'jennifer', 'hunter', 'thomas'
];

/**
 * Password Strength Calculator (OWASP Best Practices)
 * Checks: length >= 12, uppercase, lowercase, number, special char, no common patterns
 */
function calculatePasswordStrength(password) {
	var criteria = {
		length: password.length >= 12,
		uppercase: /[A-Z]/.test(password),
		lowercase: /[a-z]/.test(password),
		number: /[0-9]/.test(password),
		special: /[!@#$%^&*()_+\-=\[\]{}|;:'",.<>?\/\\`~]/.test(password),
		patterns: !hasCommonPatterns(password)
	};

	var score = Object.values(criteria).filter(Boolean).length;
	var level = 'weak';

	if (score >= 6) {
		level = 'strong';
	} else if (score >= 4) {
		level = 'good';
	} else if (score >= 3) {
		level = 'fair';
	}

	return {
		score: score,
		criteria: criteria,
		level: level
	};
}

/**
 * Check for common patterns that weaken passwords
 */
function hasCommonPatterns(password) {
	var lowerPass = password.toLowerCase();

	// Check against common passwords list
	if (commonPasswords.indexOf(lowerPass) !== -1) {
		return true;
	}

	// Check for sequential characters (abc, 123, etc.)
	var sequences = 'abcdefghijklmnopqrstuvwxyz0123456789';
	for (var i = 0; i < password.length - 2; i++) {
		var chunk = lowerPass.substring(i, i + 3);
		if (sequences.indexOf(chunk) !== -1) {
			return true;
		}
		// Check reverse sequences (zyx, 987, etc.)
		var reverseChunk = chunk.split('').reverse().join('');
		if (sequences.indexOf(reverseChunk) !== -1) {
			return true;
		}
	}

	// Check for repeated characters (aaa, 111, etc.)
	if (/(.)\1{2,}/.test(password)) {
		return true;
	}

	// Check for keyboard patterns
	var keyboardPatterns = ['qwerty', 'asdfgh', 'zxcvbn', 'qazwsx', '!@#$%^'];
	for (var j = 0; j < keyboardPatterns.length; j++) {
		if (lowerPass.indexOf(keyboardPatterns[j]) !== -1) {
			return true;
		}
	}

	return false;
}

/**
 * Update the password strength UI elements
 */
function updatePasswordStrengthUI(password) {
	var result = calculatePasswordStrength(password);

	// Update strength bar
	var $bar = $('#password-strength-bar');
	$bar.removeClass('strength-weak strength-fair strength-good strength-strong');

	// Update strength text
	var $text = $('#password-strength-text');
	$text.removeClass('strength-weak strength-fair strength-good strength-strong');

	if (password.length === 0) {
		$bar.css('width', '0');
		$text.text('');
	} else {
		// Clear inline width style so CSS class width takes effect
		$bar.css('width', '');
		$bar.addClass('strength-' + result.level);
		$text.addClass('strength-' + result.level);

		var levelLabels = {
			'weak': 'Weak',
			'fair': 'Fair',
			'good': 'Good',
			'strong': 'Strong'
		};
		$text.text(levelLabels[result.level]);
	}

	// Update criteria checkpoints
	var criteriaMap = {
		'length': result.criteria.length,
		'uppercase': result.criteria.uppercase,
		'lowercase': result.criteria.lowercase,
		'number': result.criteria.number,
		'special': result.criteria.special,
		'patterns': result.criteria.patterns
	};

	$('#password-criteria-list li').each(function() {
		var $li = $(this);
		var criteriaKey = $li.data('criteria');

		if (criteriaMap[criteriaKey]) {
			$li.removeClass('criteria-fail').addClass('criteria-pass');
		} else {
			$li.removeClass('criteria-pass').addClass('criteria-fail');
		}
	});

	return result;
}

$(function(){

	/***************************************************************************************************************/
	/* 1. Load Tooltips															   				   				   */
	/***************************************************************************************************************/
	
	//we're using tippy for the tooltip
	tippy('[data-tippy-content]',{
		trigger: 'click',
		placement: 'bottom',
		boundary: 'window',
		arrow: true
	});
	
	
	/***************************************************************************************************************/	
	/* 2. Attach event to 'Save Settings' button																   */
	/***************************************************************************************************************/
	$("#button_save_main_settings").on('click',function(){
		
		if($("#button_save_main_settings").text() != 'Saving...'){
				
				//display loader while saving
				$("#button_save_main_settings").prop("disabled",true);
				$("#button_save_main_settings").text('Saving...');
				$("#button_save_main_settings").after("<img style=\"margin-left: 10px\" src='images/loader_small_grey.gif' />");
				
				$("#ms_form").trigger("submit");
		}
		
		
		return false;
	});

	/***************************************************************************************************************/	
	/* 3. Dialog Box for change password																		   */
	/***************************************************************************************************************/
	
	$("#ms_change_password").on('click',function(){
		
		Swal.fire({
			title: 'Change User Password',
			html: $("#dialog-change-password").html(),
			width: '30%',
			customClass: {
				footer: 'swal2-footer-info',
				title: 'swal2-title-warning'
			},
			didOpen: () => {
				$("#dialog-change-password-input1").trigger("focus");

				// Initialize criteria list to fail state
				$('#password-criteria-list li').addClass('criteria-fail');

				// Attach real-time password strength checker
				$("#dialog-change-password-input1").on('input', function() {
					var password = $(this).val();
					updatePasswordStrengthUI(password);
				});
			},
			confirmButtonText: 'Save Password',
			cancelButtonText: 'Cancel',
			showCancelButton: true,
			showConfirmButton: true,
			showLoaderOnConfirm: true,
			allowOutsideClick: () => !Swal.isLoading(),
			preConfirm: (tagnames) => {
				var password_1 = $("#dialog-change-password-input1").val().trim();
				var password_2 = $("#dialog-change-password-input2").val().trim();
				var csrf_token = $("#ms_box_account").data("csrftoken");

				if(password_1 == "" || password_2 == ""){
					Swal.showValidationMessage('Error: Please fill in both password fields!');
					return false;
				}

				// Check password strength before proceeding
				var strengthResult = calculatePasswordStrength(password_1);
				if(strengthResult.level === 'weak' || strengthResult.level === 'fair'){
					Swal.showValidationMessage('Error: Password is too weak. Please meet at least 4 of the criteria.');
					return false;
				}

				if(password_1 != password_2){
					Swal.showValidationMessage('Error: Please enter the same password in both fields!');
					return false;
				}

				return axios.post('change_password.php', {
					np: password_1,
					csrf_token: csrf_token
				})
				.then(function (response) {
					if(response.data.status == 'ok'){
						Swal.fire({
							toast: true,
							position: 'center',
							icon: 'success',
							title: 'Password changed.',
							showConfirmButton: false,
							timer: 2000
						});

						return response;
					}else if(response.data.status == 'session_expired'){
						Swal.fire({
							width: '30%',
							position: 'center',
							icon: 'warning',
							title: response.data.expiry_title,
							html: response.data.expiry_message,
							showConfirmButton: true,
							showCancelButton: false,
							confirmButtonText: 'Sign In Again',
							allowOutsideClick: false
						}).then((result) => {
							if(result.isConfirmed) {
								window.location.replace('index.php?from=' + btoa(window.location.pathname + window.location.search));
							}
						});
					}else{
						Swal.showValidationMessage('Error: Unable to process. Please try again later.');
					}
				})
				.catch(function (error) {
					Swal.showValidationMessage("Error: " + error);;
				});
			}
		});

		return false;
	});

	/***************************************************************************************************************/	
	/* 4. 2-Step Verification Events																			   */
	/***************************************************************************************************************/

	//attach event to 'enable 2-step verification' checkbox
	$("#tsv_enable").on('click',function(){
		if($(this).prop("checked") == true){
			$("#ms_box_user_tsv .ms_box_email").slideDown();
		}else{
			$("#ms_box_user_tsv .ms_box_email").slideUp();
		}
	});

	//initialize qrcode if TSV is currently disabled
	if($("#tsv_enable").prop("checked") == false){
		var qrcode = new QRCode(document.getElementById("qrcode"), { width : 140, height : 140 });
		qrcode.makeCode($("#qrcode").data('totpdata'));	
	}

	//attach event to 'Verify Code' button
	$("#button_verify_tsv").on('click',function(){

		if($("#button_verify_tsv").text() != 'Verifying...'){
				
				//display loader while verifying
				$("#button_verify_tsv").prop("disabled",true);
				$("#button_verify_tsv").text('Verifying...');
				$("#button_verify_tsv").after("<img style=\"margin-left: 10px\" src='images/loader_small_grey.gif' />");
				
				var csrf_token  = $("#ms_box_account").data("csrftoken");

				//do the ajax call to verify the code
				axios.post('my_account_verify_tsv.php', {
					tsv_secret: $("#qrcode").data("tsvsecret"),
					tsv_code: $("#tsv_confirm_token").val(),
					csrf_token: csrf_token
				})
				.then(function (response) {
					if(response.data.status == 'ok'){
						//display success dialog
						Swal.fire({
							toast: true,
							position: 'center',
							icon: 'success',
							title: '2-Step Verification Activated!',
							showConfirmButton: true
						}).then((result) => {
							if(result.isConfirmed) {
								window.location.replace('my_account.php');
							}
						});
					}else if(response.data.status == 'session_expired'){
						Swal.fire({
							width: '30%',
							position: 'center',
							icon: 'warning',
							title: response.data.expiry_title,
							html: response.data.expiry_message,
							showConfirmButton: true,
							showCancelButton: false,
							confirmButtonText: 'Sign In Again',
							allowOutsideClick: false
						}).then((result) => {
							if(result.isConfirmed) {
								window.location.replace('index.php?from=' + btoa(window.location.pathname + window.location.search));
							}
						});
					}else{
						Swal.fire({
							title: 'Error!',
							html: `Invalid security code. <br/>Please double-check and enter the correct code.`,
							customClass: {
								title: 'swal2-title-warning',
								htmlContainer: 'swal2-html-container-bold'
							},
							icon: 'error',
							width: '30%',
							allowOutsideClick: true,
							didClose: () => {
								$("#button_verify_tsv").prop("disabled",false);
								$("#button_verify_tsv").text('Verify Code');
								$("#button_verify_tsv").next().remove();
							}
						});
					}

					$("#button_verify_tsv").prop("disabled",false);
					$("#button_verify_tsv").text('Verify Code');
					$("#button_verify_tsv").next().remove();
				})
				.catch(function (error) {
					alert('Unable to verify code!');

					$("#button_verify_tsv").prop("disabled",false);
					$("#button_verify_tsv").text('Verify Code');
					$("#button_verify_tsv").next().remove();
				});
				
		}

		return false;
	});

});