<?php
if (!defined('ABSPATH')) exit;
if (!class_exists('MCWP2FA')) :

require_once dirname(__FILE__) . '/authenticator.php';
require_once dirname(__FILE__) . '/utils.php';

class MCWP2FA {
	const FLAG_META_KEY = 'mc_2fa_enabled';
	const SECRET_META_KEY = 'mc_2fa_secret';
	const INVALID_CODE_MESSAGE = 'The 2FA code you entered is incorrect.';
	const TOOLTIP_MESSAGE = 'Please contact your administrator if you need assistance.';

	public static $cipher_algo = 'aes-256-cbc';
	public static $wp_2fa_option = 'mcWp2faConf';
	
	private $bvinfo;
	private $settings;
	private $invalid_code_message = self::INVALID_CODE_MESSAGE;
	private $tooltip_message = self::TOOLTIP_MESSAGE;

	public function __construct() {
		$this->settings = new MCWPSettings();
		$this->bvinfo = new MCInfo($this->settings);

		$whitelabel_info = $this->bvinfo->getLPWhitelabelInfo();

		if (isset($whitelabel_info['2fa_error_message']) && is_string($whitelabel_info['2fa_error_message'])) {
			$this->invalid_code_message = $whitelabel_info['2fa_error_message'];
		}

		if (isset($whitelabel_info['2fa_tooltip']) && is_string($whitelabel_info['2fa_tooltip'])) {
			$this->tooltip_message = $whitelabel_info['2fa_tooltip'];
		}
	}

	public static function isEnabled($settings) {
		$config = $settings->getOption(self::$wp_2fa_option);

		return (is_array($config) && array_key_exists('enabled', $config) &&
				$config['enabled'] === true);
	}

	public function init() {
		add_action('wp_enqueue_scripts', array($this, 'enqueue_dashicons'));
		add_filter('authenticate', array($this, 'authenticate'), 25, 3);
		add_action('login_form', array($this, 'custom_login_form'));
	}

	public function enqueue_dashicons() {
		wp_enqueue_style('dashicons');
	}

	public function authenticate($user, $username, $password) {
		if (!($user instanceof WP_User)) {
			return $user;
		}

		$has_2fa = get_user_meta($user->ID, MCWP2FA::FLAG_META_KEY, true);

		if ('1' === $has_2fa) {
			if (empty($_POST['twofa_code'])) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
				wp_send_json_success(array('twofa_enabled' => true));
				exit;
			} else {
				$encoded_secret_info = get_user_meta($user->ID, MCWP2FA::SECRET_META_KEY, true);

				$secret_info = MCWP2FAUtils::getSecretInfo($encoded_secret_info);
				$secret = $secret_info['secret'];
				$is_secret_encrypted = $secret_info['is_encrypted'];

				if (is_null($secret) || is_null($is_secret_encrypted)) {
					return new WP_Error('invalid_2fa_configuration', 'Please contact your administrator to login.');
				}

				if (defined('SECURE_AUTH_KEY') && $is_secret_encrypted === true) {
					$decryption_result = MCHelper::opensslDecrypt($secret, self::$cipher_algo, SECURE_AUTH_KEY);
					if ($decryption_result[0] === false) {
						return new WP_Error('2fa_secret_key_decryption_error', 'Please contact your administrator to login.');
					}
					$secret = $decryption_result[1];
				}

				if (empty($secret) || !is_string($secret) || 32 !== strlen($secret)) {
					return new WP_Error('invalid_2fa_configuration', 'Please contact your administrator to login.');
				}

				$submitted_code = MCHelper::getRawParam('POST', 'twofa_code');

				if (is_string($submitted_code) && ctype_digit($submitted_code) &&
						true === MCWP2FAAuthenticator::verifyCode($secret, $submitted_code)) {

					return $user;
				} else {
					return new WP_Error('invalid_2fa_code', esc_html($this->invalid_code_message));
				}
			}
		}

		return $user;
	}

	function custom_login_form() {
		$tooltip_message = $this->tooltip_message;
		$is_url = filter_var($tooltip_message, FILTER_VALIDATE_URL);

		$icon_css = 'font-size: 20px; color: #2271b1; cursor: pointer;';
		$icon_html = '<span
				id="twofa_help_icon"
				class="dashicons dashicons-editor-help"
				style="' . esc_attr($icon_css) . '"></span>';

		if ($is_url) {
			$tooltip_html = '<a
					href="' . esc_url($tooltip_message) . '"
					target="_blank"
					style="text-decoration: none;">' . $icon_html . '</a>';
		} else {
			$tooltip_html = '<span
					id="twofa_help_icon"
					class="dashicons dashicons-editor-help"
					title="' . esc_attr($tooltip_message) . '"
					style="' . esc_attr($icon_css) . '"></span>';
		}
		// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- not the best way
?>
		<style>
			.wp2fa-progress-bar {
				width: 100%;
				background-color: #f3f3f3;
				display: none;
				margin-bottom: 10px;
			}

			.wp2fa-progress-bar.show {
				display: block;
			}

			.wp2fa-progress-bar .progress-bar-inner {
				width: 0;
				height: 5px;
				background-color: #2271b1;
				animation: loader 1s ease infinite;
			}

			@keyframes loader {
				100% {width: 100%}
			}
		</style>

		<div class="wp2fa-progress-bar">
			<div class="progress-bar-inner"></div>
		</div>

		<script type="text/javascript">
			document.addEventListener('DOMContentLoaded', function() {
				const loginForm = document.getElementById('loginform');
				const usernameField = document.getElementById('user_login');
				const passwordField = document.getElementById('user_pass');
				const loginButton = document.getElementById('wp-submit');
				let loginError = document.getElementById('login_error');
				let isTwoFAEnabled = false;
				const progressBar = document.getElementsByClassName('wp2fa-progress-bar')[0];

				if (loginForm && usernameField && passwordField && loginButton) {
					loginForm.addEventListener('submit', handleSubmit);
				}

				function handleSubmit(event) {
					event.preventDefault();
					showProgressBar();
					disableLoginButton();

					const formData = new FormData(loginForm);

					fetch(loginForm.action, {
						method: 'POST',
						body: formData,
						credentials: 'same-origin'
					}).then(response => response.text())
					.then(text => {
						try {
							return JSON.parse(text);
						} catch (e) {
							return { success: false, html: text };
						}
					})
					.then(data => {
						if (data.success && data.data && data.data.twofa_enabled) {
							isTwoFAEnabled = true;
							showTwoFAField();
							clearLoginError();
						} else {
							if (data.html) {
								handleHtmlResponse(data.html);
							} else if (data.data && data.data.message) {
								displayError(data.data.message);
							} else {
								displayError('An unknown error occurred');
							}
							if (isTwoFAEnabled) {
								showTwoFAField();
							}
						}
					})
					.catch(error => {
						displayError('An error occurred while processing your request');
						if (isTwoFAEnabled) {
							showTwoFAField();
						}
					})
					.finally(() => {
						hideProgressBar();
						enableLoginButton();
					})
				}

				function handleHtmlResponse(html) {
					const parser = new DOMParser();
					const doc = parser.parseFromString(html, 'text/html');
					const errorElement = doc.getElementById('login_error');
					if (errorElement) {
						displayError(errorElement.innerText.trim());
					} else {
						proceedWithLogin();
					}
				}

				function showTwoFAField() {
					let twofaField = document.getElementById('twofa_code_field');
					if (!twofaField) {
						twofaField = document.createElement('p');
						twofaField.id = 'twofa_code_field';
						twofaField.innerHTML = `
							<label for="twofa_code" style="position: relative; display: block;">
								2FA Code
								<?php echo $tooltip_html; ?>
							</label>
							<input type="text" required name="twofa_code" id="twofa_code" class="input" value="" maxlength="6" minlength="6">
							`;
						passwordField.parentNode.insertBefore(twofaField, passwordField.nextSibling);
					}
					twofaField.style.display = 'block';
					document.getElementById('twofa_code').value = '';
				}

				function clearLoginError() {
					if (loginError) {
						loginError.style.display = 'none';
					}
				}

				function displayError(message) {
					if (!loginError) {
						loginError = document.createElement('div');
						loginError.id = 'login_error';
						loginForm.parentNode.insertBefore(loginError, loginForm);
					}
					loginError.textContent = message;
					loginError.style.display = 'block';
				}

				function proceedWithLogin() {
					loginForm.removeEventListener('submit', handleSubmit);
					loginForm.submit();
				}

				function showProgressBar() {
					progressBar.classList.add('show');
				}

				function hideProgressBar() {
					progressBar.classList.remove('show');
				}

				function disableLoginButton() {
					loginButton.disabled = true;
					loginButton.value = 'Verifying...';
				}

				function enableLoginButton() {
					loginButton.disabled = false;
					loginButton.value = 'Log In';
				}
			});
			</script>
<?php
	}
}
endif;