<?php
/**
 * @copyright    OCTemplates
 * @support      https://octemplates.net/
 * @license      LICENSE.txt
 */

class ControllerAccountOctSmartRegister extends Controller {
	private $error = [];

	public function index() {
		if ($this->customer->isLogged()) {
			$this->response->redirect($this->url->link('account/account', '', true));
		}

		$this->load->model('account/customer');
		$this->load->language('octemplates/module/oct_smart_register');
		$this->document->setTitle($this->language->get('heading_title'));

		$data['breadcrumbs'] = [];
		$data['breadcrumbs'][] = [
			'text' => $this->language->get('text_home'),
			'href' => $this->url->link('common/home')
		];
		$data['breadcrumbs'][] = [
			'text' => $this->language->get('text_account'),
			'href' => $this->url->link('account/account', '', true)
		];
		$data['breadcrumbs'][] = [
			'text' => $this->language->get('text_register'),
			'href' => $this->url->link('account/register', '', true)
		];

		$data['text_account_already'] = sprintf(
			$this->language->get('text_account_already'),
			$this->url->link('account/login', '', true)
		);

		$data['error_warning'] = isset($this->error['warning']) ? $this->error['warning'] : '';

		$data['action'] = $this->url->link('account/oct_smart_register/validate', '', true);
		$data['account_url'] = $this->url->link('account/account', '', true);

		$this->load->model('account/customer_group');
		$data['customer_groups'] = $this->model_account_customer_group->getCustomerGroups();

		$data['newsletter'] = isset($this->request->post['newsletter']) ? $this->request->post['newsletter'] : '';

		if ($this->config->get('captcha_' . $this->config->get('config_captcha') . '_status') &&
			in_array('register', (array)$this->config->get('config_captcha_page'))) {
			$data['captcha'] = $this->load->controller('extension/captcha/' . $this->config->get('config_captcha'), $this->error);
		} else {
			$data['captcha'] = '';
		}

		if ($this->config->get('config_account_id')) {
			$this->load->model('catalog/information');
			$information_info = $this->model_catalog_information->getInformation($this->config->get('config_account_id'));
			if ($information_info) {
				$data['text_agree'] = sprintf(
					$this->language->get('text_agree'),
					$this->url->link('information/information/agree', 'information_id=' . $this->config->get('config_account_id'), true),
					$information_info['title']
				);
			} else {
				$data['text_agree'] = '';
			}
		} else {
			$data['text_agree'] = '';
		}

		$oct_smart_register_data = $this->config->get('oct_smart_register_data');
		$data['newsletter_status'] = isset($oct_smart_register_data['newsletter']) ? $oct_smart_register_data['newsletter'] : '';
		$data['customer_group_status'] = !empty($oct_smart_register_data['customer_group']);
		$data['customer_group_id'] = (int)$this->config->get('config_customer_group_id');

		$oct_promo_data = $this->config->get('theme_oct_promo_data');
		$phone_masks = [];
		$allowed_countries = [];

		if (isset($oct_promo_data['phone_countries']) && is_array($oct_promo_data['phone_countries'])) {
			foreach ($oct_promo_data['phone_countries'] as $country) {
				if (!empty($country['country_code']) && !empty($country['mask'])) {
					$country_code_lower = strtolower($country['country_code']);
					$phone_masks[$country_code_lower] = [
						'mask' => $country['mask'],
						'regex' => !empty($country['regex']) ? $country['regex'] : ''
					];
					$allowed_countries[] = $country_code_lower;
				}
			}
		}
		$data['phone_masks'] = json_encode($phone_masks);
		$data['allowed_countries'] = json_encode($allowed_countries);

		$data['default_phone_country'] = !empty($oct_promo_data['default_phone_country'])
			? strtolower($oct_promo_data['default_phone_country'])
			: 'ua';

		$csrf_reg_token = bin2hex(random_bytes(32));
		$this->session->data['csrf_reg_token'] = $csrf_reg_token;
		$data['csrf_reg_token'] = $csrf_reg_token;

		$data['agree'] = isset($this->request->post['agree']) ? $this->request->post['agree'] : false;

		$data['customer_fields_block'] = $this->getCustomerFields($data);

		$data['column_left'] = $this->load->controller('common/column_left');
		$data['column_right'] = $this->load->controller('common/column_right');
		$data['content_top'] = $this->load->controller('common/content_top');
		$data['content_bottom'] = $this->load->controller('common/content_bottom');
		$data['footer'] = $this->load->controller('common/footer');
		$data['header'] = $this->load->controller('common/header');

		$this->response->setOutput($this->load->view('account/oct_smart_register', $data));
	}

	public function validate() {
		if (isset($this->request->server['HTTP_X_REQUESTED_WITH']) &&
			!empty($this->request->server['HTTP_X_REQUESTED_WITH']) &&
			strtolower($this->request->server['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {

			$json = [];
			$data['account_url'] = $this->url->link('account/account', '', true);

			$this->language->load('octemplates/module/oct_smart_register');
			$this->load->model('account/customer');

			if (!$this->validateCsrf()) {
				$json['warning'] = $this->language->get('error_csrf');
				$this->response->addHeader('Content-Type: application/json');
				$this->response->setOutput(json_encode($json));
				return;
			}

			if (isset($this->request->post['email_check_confirm']) && !empty(trim($this->request->post['email_check_confirm']))) {
				$json['warning'] = $this->language->get('error_bot_detected');
				$this->response->addHeader('Content-Type: application/json');
				$this->response->setOutput(json_encode($json));
				return;
			}

			$oct_smart_register_data = $this->config->get('oct_smart_register_data');
			$data['language_id'] = (int)$this->config->get('config_language_id');

			if (!$json) {
				$this->load->language('octemplates/module/oct_smart_register');

				$data['language_id'] = (int)$this->config->get('config_language_id');
				$data['fields'] = $this->getFieldList('customer', 'default');

				$data['register_fields'] = [
					'password' => [
						'required' => 1,
						'display' => 'all',
						'sort_order' => 1,
						'status' => 'on',
						'custom_field' => 0,
						'custom_field_id' => 0,
						'type' => 'password',
						'localization' => '',
						'merge_field' => '',
					],
					'email' => [
						'required' => 1,
						'display' => 'all',
						'sort_order' => 1,
						'status' => 'on',
						'custom_field' => 0,
						'custom_field_id' => 0,
						'type' => 'text',
						'localization' => '',
						'merge_field' => '',
					],
					'telephone' => [
						'required' => 1,
						'display' => 'all',
						'sort_order' => 1,
						'status' => 'on',
						'custom_field' => 0,
						'custom_field_id' => 0,
						'type' => 'text',
						'localization' => '',
						'merge_field' => '',
					]
				];

				$data['merged_fields'] = array_merge($data['fields'], $data['register_fields']);
				$json = $this->validateFields($data['merged_fields']);

				if ($this->model_account_customer->getTotalCustomersByEmail(isset($this->request->post['email']) ? $this->request->post['email'] : '')) {
					$json['error']['email'] = $this->language->get('error_email_exists');
				}
			}

			if ($this->config->get('captcha_' . $this->config->get('config_captcha') . '_status') &&
				in_array('register', (array)$this->config->get('config_captcha_page'))) {
				$captcha = $this->load->controller('extension/captcha/' . $this->config->get('config_captcha') . '/validate');
				if ($captcha) {
					$json['error']['captcha'] = $captcha;
				}
			}

			if ($this->customer->isLogged()) {
				$json['error']['logged'] = $this->language->get('error_logged');
			}

			if (!$json) {
				$json = $this->checkAgree();
			}

			if (!$json) {
				$this->session->data['register_form_validated'] = true;

				$oct_sms_settings = $this->config->get('oct_sms_settings');
				$template_code = 'register_confirmation';

				if (isset($oct_sms_settings['templates'][$template_code]['status']) && $oct_sms_settings['templates'][$template_code]['status']) {
					$this->session->data['register_form_data'] = $this->request->post;
					$json = $this->sendSmsCodeInternal();
				} else {
					$json = $this->processRegistration();
				}
			}

			$this->response->addHeader('Content-Type: application/json');
			$this->response->setOutput(json_encode($json));
		} else {
			$this->response->redirect($this->url->link('error/not_found', '', true));
		}
	}

	public function sendSmsCode() {
		$json = [];
		$this->load->language('octemplates/module/oct_smart_register');
		$this->load->model('octemplates/module/oct_sms_notify');

		if (!$this->validateCsrf()) {
			$json['error'] = $this->language->get('error_csrf');
			$this->response->addHeader('Content-Type: application/json');
			$this->response->setOutput(json_encode($json));
			return;
		}

		if (!isset($this->session->data['register_form_validated']) || !$this->session->data['register_form_validated']) {
			$json['error'] = $this->language->get('error_unauthorized');
			$this->response->addHeader('Content-Type: application/json');
			$this->response->setOutput(json_encode($json));
			return;
		}

		if (!isset($this->session->data['validated_phone'])) {
			$json['error'] = $this->language->get('error_telephone');
			$this->response->addHeader('Content-Type: application/json');
			$this->response->setOutput(json_encode($json));
			return;
		}

		$posted_phone = isset($this->request->post['telephone']) ? trim($this->request->post['telephone']) : '';

		if ($posted_phone !== $this->session->data['validated_phone']) {
			$json['error'] = $this->language->get('error_telephone');
			$this->response->addHeader('Content-Type: application/json');
			$this->response->setOutput(json_encode($json));
			return;
		}

		$phone = $this->session->data['validated_phone'];
		$ip = $this->request->server['REMOTE_ADDR'];
		$current_time = time();

		$this->model_octemplates_module_oct_sms_notify->cleanupExpired();

		$existing = $this->model_octemplates_module_oct_sms_notify->getActiveVerificationByPhone($phone);

		if ($existing) {
			$wait_time = max(1, 60 - ($current_time - (int)$existing['created_at']));
			
			if ($wait_time > 1) {
				$this->session->data['sms_verification_id'] = (int)$existing['id'];
				
				$json['sms_required'] = true;
				$json['show_sms_modal'] = true;
				$json['timer'] = $wait_time;
				$json['message'] = $this->language->get('text_sms_sent');
				$this->response->addHeader('Content-Type: application/json');
				$this->response->setOutput(json_encode($json));
				return;
			}
		}

		$rate_limit_errors = $this->checkSmsRateLimit($phone, $ip);
		if ($rate_limit_errors) {
			$json['error'] = reset($rate_limit_errors);
			$this->response->addHeader('Content-Type: application/json');
			$this->response->setOutput(json_encode($json));
			return;
		}

		$code = sprintf('%04d', random_int(0, 9999));
		$sms_id = $this->model_octemplates_module_oct_sms_notify->createVerificationRecord($phone, $code, $ip);
		$this->session->data['sms_verification_id'] = $sms_id;

		$sms_result = $this->sendSms($phone, $code);

		if ($sms_result) {
			$this->logSmsRequest($phone, $ip);
			
			$json['sms_required'] = true;
			$json['show_sms_modal'] = true;
			$json['timer'] = 60;
			$json['message'] = $this->language->get('text_sms_sent');
		} else {
			$this->model_octemplates_module_oct_sms_notify->deleteVerification($sms_id);
			unset($this->session->data['sms_verification_id']);
			$json['error'] = $this->language->get('error_sms_send');
		}

		$this->response->addHeader('Content-Type: application/json');
		$this->response->setOutput(json_encode($json));
	}

	public function verifySmsCode() {
		$json = [];
		$this->load->language('octemplates/module/oct_smart_register');
		$this->load->model('octemplates/module/oct_sms_notify');

		if (!$this->validateCsrf()) {
			$json['error'] = $this->language->get('error_csrf');
			$this->response->addHeader('Content-Type: application/json');
			$this->response->setOutput(json_encode($json));
			return;
		}

		$sms_id = isset($this->session->data['sms_verification_id']) ? (int)$this->session->data['sms_verification_id'] : 0;

		if (!$sms_id) {
			$json['error'] = $this->language->get('error_sms_expired');
			$this->response->addHeader('Content-Type: application/json');
			$this->response->setOutput(json_encode($json));
			return;
		}

		$verification = $this->model_octemplates_module_oct_sms_notify->getActiveVerification($sms_id);

		if (!$verification) {
			unset($this->session->data['sms_verification_id']);
			$json['error'] = $this->language->get('error_sms_expired');
			$this->response->addHeader('Content-Type: application/json');
			$this->response->setOutput(json_encode($json));
			return;
		}

		$user_code = isset($this->request->post['sms_code']) ? trim($this->request->post['sms_code']) : '';

		if ((int)$verification['attempts'] >= 5) {
			$json['error'] = $this->language->get('error_sms_max_attempts');
			$json['attempts_exceeded'] = true;
			$json['retry_after'] = 60;
			$this->response->addHeader('Content-Type: application/json');
			$this->response->setOutput(json_encode($json));
			return;
		}

		if ($user_code !== $verification['code']) {
			$this->model_octemplates_module_oct_sms_notify->incrementAttempts($sms_id);
			
			$remaining = 5 - ((int)$verification['attempts'] + 1);

			if ($remaining > 0) {
				$json['error'] = sprintf($this->language->get('error_sms_wrong_code'), $remaining);
			} else {
				$json['error'] = $this->language->get('error_sms_max_attempts');
				$json['attempts_exceeded'] = true;
				$json['retry_after'] = 60;
			}

			$this->response->addHeader('Content-Type: application/json');
			$this->response->setOutput(json_encode($json));
			return;
		}

		$this->model_octemplates_module_oct_sms_notify->markAsVerified($sms_id);

		$this->request->post = $this->session->data['register_form_data'];

		$json = $this->processRegistration();

		if (!isset($json['error'])) {
			$phone = isset($this->session->data['validated_phone']) ? $this->session->data['validated_phone'] : '';
			if ($phone) {
				$this->db->query("DELETE FROM `" . DB_PREFIX . "oct_order_rate_limit` WHERE phone = '" . $this->db->escape($phone) . "'");
			}
		}

		unset($this->session->data['sms_verification_id']);
		unset($this->session->data['register_form_data']);
		unset($this->session->data['register_form_validated']);
		unset($this->session->data['validated_phone']);

		$this->response->addHeader('Content-Type: application/json');
		$this->response->setOutput(json_encode($json));
	}

	public function resendSmsCode() {
		$json = [];
		$this->load->language('octemplates/module/oct_smart_register');
		$this->load->model('octemplates/module/oct_sms_notify');

		if (!$this->validateCsrf()) {
			$json['error'] = $this->language->get('error_csrf');
			$this->response->addHeader('Content-Type: application/json');
			$this->response->setOutput(json_encode($json));
			return;
		}

		$sms_id = isset($this->session->data['sms_verification_id']) ? (int)$this->session->data['sms_verification_id'] : 0;
		
		if (!$sms_id) {
			$phone = $this->session->data['validated_phone'] ?? null;
			
			if ($phone) {
				$this->sendSmsCode();
				return;
			}
			
			$json['error'] = $this->language->get('error_sms_session_lost');
			$this->response->addHeader('Content-Type: application/json');
			$this->response->setOutput(json_encode($json));
			return;
		}

		$verification = $this->model_octemplates_module_oct_sms_notify->getActiveVerification($sms_id);

		if (!$verification) {
			unset($this->session->data['sms_verification_id']);
			
			$phone = $this->session->data['validated_phone'] ?? null;
			if ($phone) {
				$this->sendSmsCode();
				return;
			}
			
			$json['error'] = $this->language->get('error_sms_expired');
			$this->response->addHeader('Content-Type: application/json');
			$this->response->setOutput(json_encode($json));
			return;
		}

		$current_time = time();
		$time_since_last = $current_time - (int)$verification['created_at'];

		if ($time_since_last < 60) {
			$wait_time = 60 - $time_since_last;
			$json['error'] = sprintf($this->language->get('error_sms_rate_limit'), $wait_time);
			$this->response->addHeader('Content-Type: application/json');
			$this->response->setOutput(json_encode($json));
			return;
		}

		$code = sprintf('%04d', random_int(0, 9999));
		$this->model_octemplates_module_oct_sms_notify->updateVerificationCode($sms_id, $code);

		$sms_result = $this->sendSms($verification['phone'], $code);

		if ($sms_result) {
			$json['success'] = $this->language->get('text_sms_resent');
		} else {
			$json['error'] = $this->language->get('error_sms_send');
		}

		$this->response->addHeader('Content-Type: application/json');
		$this->response->setOutput(json_encode($json));
	}

	private function sendSmsCodeInternal() {
		$json = [];
		$this->load->language('octemplates/module/oct_smart_register');
		$this->load->model('octemplates/module/oct_sms_notify');

		if (!isset($this->session->data['validated_phone'])) {
			return ['error' => $this->language->get('error_telephone')];
		}

		$phone = $this->session->data['validated_phone'];
		$ip = $this->request->server['REMOTE_ADDR'];
		$current_time = time();

		$this->model_octemplates_module_oct_sms_notify->cleanupExpired();

		$existing = $this->model_octemplates_module_oct_sms_notify->getActiveVerificationByPhone($phone);

		if ($existing) {
			$wait_time = max(1, 60 - ($current_time - (int)$existing['created_at']));
			
			if ($wait_time > 1) {
				$this->session->data['sms_verification_id'] = (int)$existing['id'];
				
				return [
					'sms_required' => true,
					'show_sms_modal' => true,
					'timer' => $wait_time,
					'message' => $this->language->get('text_sms_sent')
				];
			}
		}

		$rate_limit_errors = $this->checkSmsRateLimit($phone, $ip);
		if ($rate_limit_errors) {
			return ['error' => ['rate_limit' => reset($rate_limit_errors)]];
		}

		$code = sprintf('%04d', random_int(0, 9999));
		$sms_id = $this->model_octemplates_module_oct_sms_notify->createVerificationRecord($phone, $code, $ip);
		$this->session->data['sms_verification_id'] = $sms_id;

		$sms_result = $this->sendSms($phone, $code);

		if ($sms_result) {
			$this->logSmsRequest($phone, $ip);
			
			$json['sms_required'] = true;
			$json['message'] = $this->language->get('text_sms_sent');
		} else {
			$this->model_octemplates_module_oct_sms_notify->deleteVerification($sms_id);
			unset($this->session->data['sms_verification_id']);
			$json['error'] = $this->language->get('error_sms_send');
		}

		return $json;
	}

	private function sendSms($telephone, $otp_code) {
		$this->load->language('octemplates/module/oct_smart_register');

		$oct_sms_settings = $this->config->get('oct_sms_settings');
		$template_code = 'register_confirmation';
		$language_id = (int)$this->config->get('config_language_id');

		if (!isset($oct_sms_settings['templates'][$template_code]['status']) || !$oct_sms_settings['templates'][$template_code]['status']) {
			return false;
		}

		if (isset($oct_sms_settings['templates'][$template_code]['message'][$language_id]) && !empty($oct_sms_settings['templates'][$template_code]['message'][$language_id]) && isset($oct_sms_settings['templates'][$template_code]['edit_localization'])) {
			$sms_template = $oct_sms_settings['templates'][$template_code]['message'][$language_id];
		} else {
			$sms_template = $this->language->get('sms_template_register_confirmation');
		}

		if (empty($sms_template)) {
			return false;
		}

		$replace = [
			'[code]' => $otp_code,
			'[store]' => $this->config->get('config_name')
		];

		$sms_message = str_replace(array_keys($replace), array_values($replace), $sms_template);

		$this->load->model('octemplates/module/oct_sms_notify');

		$result = $this->model_octemplates_module_oct_sms_notify->sendNotification([
			'phone' => $telephone,
			'message' => $sms_message,
			'template_code' => $template_code,
			'access_token' => $oct_sms_settings['oct_sms_token']
		]);

		return (bool)$result;
	}

	private function validateCsrf() {
		$token = isset($this->request->post['csrf_reg_token']) ? $this->request->post['csrf_reg_token'] : '';
		$session_token = isset($this->session->data['csrf_reg_token']) ? $this->session->data['csrf_reg_token'] : '';

		return !empty($token) && !empty($session_token) && hash_equals($session_token, $token);
	}

	private function sanitizeString($input, $max_length = 255) {
		if (!is_string($input)) return '';
		$input = trim($input);
		$input = strip_tags($input);

		$input = preg_replace([
			'/[^\p{L}\p{N}\s\-_\.@]+/u',
			'/\s+/u'
		], ['', ' '], $input);

		return mb_substr($input, 0, $max_length, 'UTF-8');
	}

	private function processRegistration() {
		$json = [];
		
		$this->request->post['firstname'] = $this->sanitizeString($this->request->post['firstname'], 32);
		$this->request->post['lastname'] = $this->sanitizeString($this->request->post['lastname'] ?? '', 32);
		$this->request->post['email'] = filter_var($this->request->post['email'], FILTER_SANITIZE_EMAIL);
		$this->request->post['telephone'] = $this->session->data['validated_phone'] ?? '';

		$oct_smart_register_data = $this->config->get('oct_smart_register_data');
		$customer_group_id = (int)$this->config->get('config_customer_group_id');

		$this->load->model('account/customer_group');

		if (!empty($oct_smart_register_data['customer_group'])) {
			$posted = isset($this->request->post['customer_group_id']) ? (int)$this->request->post['customer_group_id'] : 0;
			if ($posted && $this->model_account_customer_group->getCustomerGroup($posted)) {
				$customer_group_id = $posted;
			}
		}

		$this->request->post['customer_group_id'] = $customer_group_id;

		$display = (array)$this->config->get('config_customer_group_display');
		if (!in_array($customer_group_id, $display)) {
			$display[] = $customer_group_id;
			$this->config->set('config_customer_group_display', $display);
		}

		$this->session->data['account'] = 'register';
		$this->load->model('account/customer');

		$customer_id = $this->model_account_customer->addCustomer($this->request->post);
		$this->session->data['customer_id'] = $customer_id;
		$this->session->data['checkout_customer_id'] = true;

		$customer_group = $this->model_account_customer_group->getCustomerGroup($customer_group_id);
		
		if ($customer_group && !$customer_group['approval']) {
			$this->customer->login($this->request->post['email'], $this->request->post['password']);
			$json['redirect'] = $this->url->link('account/success');
		} else {
			$json['redirect'] = $this->url->link('account/success');
		}
		
		unset($this->session->data['guest']);
		unset($this->session->data['csrf_reg_token']);
		unset($this->session->data['validated_phone']);
		unset($this->session->data['sms_verification_id']);
		
		$cart = new Cart\Cart($this->registry);
		$this->registry->set('cart', $cart);
		
		return $json;
	}

	private function checkSmsRateLimit($phone, $ip) {
		$errors = [];
		$current_time = time();

		$phone_check = $this->db->query("SELECT timestamp FROM `" . DB_PREFIX . "oct_order_rate_limit` WHERE phone = '" . $this->db->escape($phone) . "' AND timestamp > " . (int)($current_time - 60) . " ORDER BY timestamp DESC LIMIT 1");

		if ($phone_check->num_rows) {
			$wait_time = 60 - ($current_time - (int)$phone_check->row['timestamp']);
			$errors['phone'] = sprintf($this->language->get('error_phone_limit'), $wait_time);
		}
		
		return $errors;
	}

	private function logSmsRequest($phone, $ip) {
		$this->db->query("DELETE FROM `" . DB_PREFIX . "oct_order_rate_limit` WHERE timestamp < " . (int)(time() - 3600));

		$this->db->query("INSERT INTO `" . DB_PREFIX . "oct_order_rate_limit` SET phone = '" . $this->db->escape($phone) . "', ip = '" . $this->db->escape($ip) . "', timestamp = " . (int)time());
	}

	private function validatePhone($phone) {
		$phone = preg_replace("/[^0-9()\-_\+ ]/", '', $phone);
		
		$oct_smart_register_data = $this->config->get('oct_smart_register_data');
		$mask = isset($oct_smart_register_data['phone_mask']) ? $oct_smart_register_data['phone_mask'] : '';
		$theme_data = $this->config->get('theme_oct_promo_data');
		$phone_regex = isset($theme_data['phone_regex']) ? $theme_data['phone_regex'] : '';
		
		$clean_telephone = utf8_strlen(str_replace(['_', '-', '(', ')', '+', ' '], '', $phone));
		
		if (!empty($mask)) {
			$phone_count = utf8_strlen(str_replace(['_', '-', '(', ')', '+', ' '], '', $mask));
			if ($clean_telephone < $phone_count) {
				return false;
			}
		} else {
			if ($clean_telephone > 15 || $clean_telephone < 3) {
				return false;
			}
		}
		
		if (!preg_match('/^\+?[0-9\s\-\(\)_]+$/', $phone)) {
			return false;
		}
		
		if (!empty($phone_regex) && @preg_match($phone_regex, '') !== false) {
			if (!preg_match($phone_regex, $phone)) {
				return false;
			}
		}
		
		return $phone;
	}

	private function getCustomerFields(&$data = []) {
		$this->load->language('octemplates/module/oct_smart_register');
		$this->load->model('account/address');

		$data['language_id'] = (int)$this->config->get('config_language_id');
		$data['fields'] = $this->getFieldList('customer', 'default');

		$data['register_fields'] = [
			'email' => [
				'required' => 1,
				'display' => 'all',
				'sort_order' => 1,
				'status' => 'on',
				'custom_field' => 0,
				'custom_field_id' => 0,
				'type' => 'text',
				'localization' => '',
				'merge_field' => '',
			],
			'telephone' => [
				'required' => 1,
				'display' => 'all',
				'sort_order' => 1,
				'status' => 'on',
				'custom_field' => 0,
				'custom_field_id' => 0,
				'type' => 'text',
				'localization' => '',
				'merge_field' => '',
			]
		];

		$data['fields'] = array_merge($data['fields'], $data['register_fields']);

		$oct_promo_data = $this->config->get('theme_oct_promo_data');
		$phone_masks = [];
		$allowed_countries = [];

		if (isset($oct_promo_data['phone_countries']) && is_array($oct_promo_data['phone_countries'])) {
			foreach ($oct_promo_data['phone_countries'] as $country) {
				if (!empty($country['country_code']) && !empty($country['mask'])) {
					$country_code_lower = strtolower($country['country_code']);
					$phone_masks[$country_code_lower] = [
						'mask' => $country['mask'],
						'regex' => !empty($country['regex']) ? $country['regex'] : ''
					];
					$allowed_countries[] = $country_code_lower;
				}
			}
		}
		$data['phone_masks'] = json_encode($phone_masks);
		$data['allowed_countries'] = json_encode($allowed_countries);

		$data['default_phone_country'] = !empty($oct_promo_data['default_phone_country'])
			? strtolower($oct_promo_data['default_phone_country'])
			: 'ua';

		return $this->load->view('account/oct_smart_register', $data);
	}

	private function getFieldList($fieldType, $FieldGroup = 'default') {
		$configData = $this->config->get('oct_smart_register_data');
		$fieldsSettings = isset($configData[$fieldType]['fields'][$FieldGroup])
			? $configData[$fieldType]['fields'][$FieldGroup]
			: $configData[$fieldType]['fields']['default'];

		$fieldList = [];
		$isLogged = $this->customer->isLogged();

		foreach ($fieldsSettings as $fieldName => $settings) {
			if (isset($settings['status'])) {
				if ($settings['display'] == 'all' ||
					($isLogged && $settings['display'] == 'registered') ||
					(!$isLogged && $settings['display'] == 'guests')) {

					$customFieldSettings = [];
					if (isset($settings['custom_field']) && $settings['custom_field']) {
						$fieldList[$fieldName] = array_merge($settings, $customFieldSettings);
					} else {
						$fieldList[$fieldName] = $settings;
					}
				}
			}
		}

		return $fieldList;
	}

	private function checkAgree() {
		$json = [];

		if ($this->config->get('config_checkout_id')) {
			$this->load->model('catalog/information');
			$this->load->language('octemplates/module/oct_smartcheckout');

			$information_info = $this->model_catalog_information->getInformation($this->config->get('config_checkout_id'));

			if ($information_info && !isset($this->request->post['agree'])) {
				$json['error']['warning_agree'] = sprintf($this->language->get('error_agree'), $information_info['title']);
			}
		}

		return $json;
	}

	private function validateFields($fields = []) {
		$json = [];
		$oct_smart_register_data = $this->config->get('oct_smart_register_data');

		foreach ($fields as $fieldName => $field_settings) {
			$value = isset($this->request->post[$fieldName]) ? $this->request->post[$fieldName] : '';

			if (!isset($field_settings['status'])) {
				continue;
			}

			if ($field_settings['required'] && empty($value)) {
				$json['error'][$fieldName] = $this->getErrorMessage($field_settings, $fieldName);
			}

			switch ($fieldName) {
				case 'firstname':
					if ($field_settings['required'] && (utf8_strlen($value) < 1 || utf8_strlen($value) > 32)) {
						$json['error']['firstname'] = $this->getErrorMessage($field_settings, $fieldName);
					}
					break;
				case 'lastname':
					if ($field_settings['required'] && (utf8_strlen($value) < 1 || utf8_strlen($value) > 32)) {
						$json['error']['lastname'] = $this->getErrorMessage($field_settings, $fieldName);
					}
					break;
				case 'email':
					if ($field_settings['required'] && !filter_var($value, FILTER_VALIDATE_EMAIL)) {
						$json['error']['email'] = $this->getErrorMessage($field_settings, $fieldName);
					}
					break;
				case 'telephone':
					if ($field_settings['required'] && isset($this->request->post['telephone'])) {
						$postedTelephone = $this->request->post['telephone'] ?? '';
						$oct_promo_data = $this->config->get('theme_oct_promo_data');
						
						$cleanPhoneDigits = preg_replace('/[^0-9]/', '', $postedTelephone);
						
						if (utf8_strlen($cleanPhoneDigits) < 3 || utf8_strlen($cleanPhoneDigits) > 15) {
							$json['error']['telephone'] = $this->getErrorMessage($field_settings, $fieldName);
						} else {
							$isValid = false;
							$hasAnyRegex = false;

							if (isset($oct_promo_data['phone_countries']) && is_array($oct_promo_data['phone_countries'])) {
								foreach ($oct_promo_data['phone_countries'] as $country) {
									if (!empty($country['regex'])) {
										$hasAnyRegex = true;
										$regex = $country['regex'];

										$regex = str_replace('\\+', '', $regex);

										if (substr($regex, 0, 1) !== '/') {
											$regex = '/' . $regex . '/';
										}

										if (@preg_match($regex, '') !== false) {
											if (preg_match($regex, $cleanPhoneDigits)) {
												$isValid = true;
												$this->session->data['validated_phone'] = $postedTelephone;
												break;
											}
										}
									}
								}
							}
							
							if (!$hasAnyRegex) {
								$isValid = true;
								$this->session->data['validated_phone'] = $postedTelephone;
							} elseif (!$isValid) {
								$json['error']['telephone'] = $this->language->get('error_telephone_mask');
							}
						}
					}
					break;
				case 'password':
					if ($field_settings['required'] && strlen($value) < 4) {
						$json['error']['password'] = $this->getErrorMessage($field_settings, $fieldName);
					}
					break;
			}
		}

		return $json;
	}

	private function getErrorMessage($field_settings, $field) {
		$language_id = (int)$this->config->get('config_language_id');
		if (!empty($field_settings['localization'][$language_id]['error_text'])) {
			return $field_settings['localization'][$language_id]['error_text'];
		} else {
			return $this->language->get('error_' . $field);
		}
	}
}