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

class ControllerOctemplatesModuleOctPriceTracker extends Controller {

	private $error = array();

	public function index() {
		if ($this->isValidRequest()) {
			$this->language->load('octemplates/module/oct_price_tracker');

			if (isset($this->request->post['product_id'])) {
				$product_id = (int) $this->request->post['product_id'];
			} else {
				$product_id = 0;
			}

			$this->load->model('catalog/product');

			$product_info = $this->model_catalog_product->getProduct($product_id);

			if ($product_info) {
				$data['oct_price_tracker_data'] = $oct_price_tracker_data = $this->config->get('oct_price_tracker_data');

				$this->load->model('tool/image');

				if ($product_info['image']) {
					$data['thumb'] = $this->model_tool_image->resize($product_info['image'], 150, 150);
				} else {
					$data['thumb'] = '';
				}

				if (($this->config->get('config_customer_price') && $this->customer->isLogged()) || !$this->config->get('config_customer_price')) {
					$data['price'] = $this->currency->format($this->tax->calculate($product_info['price'], $product_info['tax_class_id'], $this->config->get('config_tax')), $this->session->data['currency']);
				} else {
					$data['price'] = false;
				}

				if ((float) $product_info['special']) {
					$data['special'] = $this->currency->format($this->tax->calculate($product_info['special'], $product_info['tax_class_id'], $this->config->get('config_tax')), $this->session->data['currency']);
				} else {
					$data['special'] = false;
				}

				$current_price = $product_info['special'] ? $product_info['special'] : $product_info['price'];
				$data['current_price_value'] = $this->tax->calculate($current_price, $product_info['tax_class_id'], $this->config->get('config_tax'));

				$data['heading_title_product'] = $product_info['name'];
				$data['href'] = $this->url->link('product/product', 'product_id=' . $product_id, true);
				$data['product_id'] = (int) $product_id;
				$data['model'] = $product_info['model'];
				$data['customer_name'] = $this->customer->isLogged() ? $this->customer->getFirstName() . " " . $this->customer->getLastName() : '';
				
				$phone = $this->customer->isLogged() ? $this->customer->getTelephone() : '';
				if ($phone) {
					$phone = preg_replace('/[^0-9+]/', '', $phone);
				}
				$data['phone'] = $phone;
				
				$data['email'] = $this->customer->isLogged() ? $this->customer->getEmail() : '';

				$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';

				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_terms'] = sprintf($this->language->get('text_terms'), $this->url->link('information/information', 'information_id=' . $this->config->get('config_account_id'), true), $information_info['title'], $information_info['title']);
					} else {
						$data['text_terms'] = '';
					}
				} else {
					$data['text_terms'] = '';
				}

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

				$data['ajax_add_url'] = str_replace('&amp;', '&', $this->url->link('octemplates/module/oct_price_tracker/add'));

				$this->response->setOutput($this->load->view('octemplates/module/oct_price_tracker', $data));
			} else {
				$this->redirectToNotFound();
			}
		} else {
			$this->redirectToNotFound();
		}
	}

	public function add() {
		if ($this->isValidRequest()) {
			$this->processSubscription();
		} else {
			$this->redirectToNotFound();
		}
	}

	public function cron() {
		$config_data = $this->config->get('oct_price_tracker_data');

		if (!$this->config->get('oct_price_tracker_status') || empty($config_data['cron_secret'])) {
			$this->redirectToNotFound();
			return;
		}

		$this->load->model('octemplates/module/oct_abandoned_cart');
		$ip = isset($this->request->server['REMOTE_ADDR']) ? (string)$this->request->server['REMOTE_ADDR'] : 'unknown';

		if ($this->model_octemplates_module_oct_abandoned_cart->isRateLimited($ip, 'cron_price_tracker', 5, 300)) {
			$this->redirectToNotFound();
			return;
		}

		$secret_key = $config_data['cron_secret'];
		$request_key = isset($this->request->get['cron_secret']) ? (string)$this->request->get['cron_secret'] : '';

		if (!$request_key || $request_key !== $secret_key) {
			if (!$this->model_octemplates_module_oct_abandoned_cart->checkRateLimit($ip, 'cron_price_tracker', 5, 300)) {
				$this->log->write('[SECURITY] Multiple failed Cron key attempts (Price Tracker) from IP: ' . $ip);
			}

			$this->redirectToNotFound();
			return;
		}

		$product_id = isset($this->request->get['product_id']) ? (int)$this->request->get['product_id'] : null;
		$this->processPriceChanges($product_id);
	}

	private function isValidRequest() {
		return $this->config->get('oct_price_tracker_status') &&
			   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';
	}

	private function processSubscription() {
		$this->language->load('octemplates/module/oct_price_tracker');
		$this->load->model('catalog/product');

		$json = [];
		$data['oct_price_tracker_data'] = $this->config->get('oct_price_tracker_data');
		$product_info = $this->model_catalog_product->getProduct((int) $this->request->post['pid']);

		if ($this->validate() && $product_info) {
			$this->load->model('octemplates/module/oct_price_tracker');
			$data = $this->collectSubscriptionData($product_info);

			if (!$this->model_octemplates_module_oct_price_tracker->isAlreadySubscribed($data)) {
				$this->model_octemplates_module_oct_price_tracker->addRequest($data);
				$json['output'] = $this->language->get('text_success_send');
			} else {
				$json['error']['subscribed'] = $this->language->get('error_already_subscribed');
			}
		} else {
			$json['error'] = $this->error;
		}

		$this->respondJson($json);
	}

	private function collectSubscriptionData($product_info) {
		$current_price = $product_info['special'] ? $product_info['special'] : $product_info['price'];
		$current_price = $this->tax->calculate($current_price, $product_info['tax_class_id'], $this->config->get('config_tax'));

		$currency_code = $this->session->data['currency'] ?? $this->config->get('config_currency');
		$current_price = $this->currency->convert($current_price, $this->config->get('config_currency'), $currency_code);

		$this->load->model('localisation/currency');
		$currency_info = $this->model_localisation_currency->getCurrencyByCode($currency_code);
		$currency_id = $currency_info ? $currency_info['currency_id'] : 1;

		return [
			'customer_name' => $this->request->post['customer_name'] ?? null,
			'phone' => $this->request->post['phone'] ?? null,
			'email' => $this->request->post['email'] ?? null,
			'pid' => (int) $this->request->post['pid'],
			'customer_id' => $this->customer->isLogged() ? $this->customer->getId() : null,
			'current_price' => $current_price,
			'currency_id' => $currency_id,
			'language_id' => $this->config->get('config_language_id')
		];
	}

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

	private function processPriceChanges($product_id = null) {
		$this->load->model('octemplates/module/oct_price_tracker');
		$this->load->model('catalog/product');
		$this->load->model('localisation/currency');
		$this->language->load('octemplates/module/oct_price_tracker');

		echo "Cron started at " . date('Y-m-d H:i:s') . "<br>\n";
		
		if ($product_id) {
			echo "Processing only product ID: $product_id<br>\n";
		}

		$limit = 1000;
		$start = 0;
		$processed_count = 0;
		$email_sent_count = 0;
		$sms_sent_count = 0;

		do {
			$pending_requests = $this->model_octemplates_module_oct_price_tracker->getPendingRequests($limit, $start, $product_id);

			foreach ($pending_requests as $request) {
				$product_info = $this->model_catalog_product->getProduct($request['product_id']);

				if ($product_info && $product_info['status'] == '1') {
					$current_price = $product_info['special'] ? $product_info['special'] : $product_info['price'];
					$current_price = $this->tax->calculate($current_price, $product_info['tax_class_id'], $this->config->get('config_tax'));

					$current_price_in_request_currency = $this->currency->convert(
						$current_price, 
						$this->config->get('config_currency'),
						$request['currency_code']
					);

					$price_difference = abs((float)$current_price_in_request_currency - (float)$request['current_price']);
					$threshold = 0.01;

					if ($price_difference >= $threshold) {
						$notification_result = $this->notifySubscriber($request, $product_info, $current_price_in_request_currency);
						$email_sent_count += $notification_result['email_sent'] ? 1 : 0;
						$sms_sent_count += $notification_result['sms_sent'] ? 1 : 0;
						
						$this->model_octemplates_module_oct_price_tracker->updateRequestStatus($request['request_id'], 'sent');
						
						$this->model_octemplates_module_oct_price_tracker->addPriceLog(
							$request['request_id'], 
							$request['current_price'],           
							$current_price_in_request_currency,  
							'success'
						);
						$processed_count++;
					}
				}
			}

			$start += $limit;
		} while (count($pending_requests) == $limit);

		echo "Processed $processed_count price changes<br>\n";
		echo "Emails sent: $email_sent_count<br>\n";
		echo "SMS sent: $sms_sent_count<br>\n";
		echo "Cron finished at " . date('Y-m-d H:i:s') . "<br>\n";
	}

	private function notifySubscriber($request, $product_info, $new_price) {
		$email_sent = false;
		$sms_sent = false;

		if (!empty($request['email'])) {
			$mailData = $this->prepareMailData($request, $product_info, $new_price);
			$this->sendMail($request['email'], $mailData);
			$email_sent = true;
		}

		$sms_sent = $this->sendSmsNotification($request, $product_info, $new_price);

		return array(
			'email_sent' => $email_sent,
			'sms_sent' => $sms_sent
		);
	}

	private function sendSmsNotification($request, $product_info, $new_price) {
		if (empty($request['phone'])) {
			return false;
		}

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

		if (empty($oct_sms_settings['oct_sms_token'])) {
			return false;
		}

		$template_code = 'oct_price_tracker';
		$language_id = isset($request['language_id']) ? (int)$request['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']) {
			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('default_sms_template_customer');
			}

			if (!empty($sms_template)) {
				$this->load->model('localisation/currency');
				$currency_code = isset($request['currency_code']) ? $request['currency_code'] : $this->config->get('config_currency');
				$currency_info = $this->model_localisation_currency->getCurrencyByCode($currency_code);

				if ($currency_info) {
					$decimal_place = (int)$currency_info['decimal_place'];
					$symbol_left = strip_tags($currency_info['symbol_left']);
					$symbol_right = strip_tags($currency_info['symbol_right']);

					$old_price_formatted = $symbol_left . number_format($request['current_price'], $decimal_place) . $symbol_right;
					$new_price_formatted = $symbol_left . number_format($new_price, $decimal_place) . $symbol_right;
				} else {
					$old_price_formatted = number_format($request['current_price'], 2) . ' ' . $currency_code;
					$new_price_formatted = number_format($new_price, 2) . ' ' . $currency_code;
				}

				$customer_name = '';
				if (isset($request['customer_name'])) {
					$customer_name = strip_tags($request['customer_name']);
					$customer_name = mb_substr($customer_name, 0, 50);
				}

				$product_name = '';
				if (isset($product_info['name'])) {
					$product_name = strip_tags($product_info['name']);
					$product_name = mb_substr($product_name, 0, 100);
				}

				$product_id = isset($product_info['product_id']) ? (int)$product_info['product_id'] : 0;

				$replace = array(
					'[customer_name]' => $customer_name,
					'[product_name]' => $product_name,
					'[old_price]' => $old_price_formatted,
					'[new_price]' => $new_price_formatted,
					'[product_url]' => $this->url->link('product/product', 'product_id=' . $product_id, true),
					'[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');
				$this->model_octemplates_module_oct_sms_notify->sendNotification(array(
					'phone' => $request['phone'],
					'message' => $sms_message,
					'template_code' => $template_code,
					'access_token' => $oct_sms_settings['oct_sms_token']
				));

				return true;
			}
		}

		return false;
	}

	private function sendMail($toEmail, $mailData) {
		$mail                = new Mail($this->config->get('config_mail_engine'));
		$mail->protocol      = $this->config->get('config_mail_protocol');
		$mail->parameter     = $this->config->get('config_mail_parameter');
		$mail->smtp_hostname = $this->config->get('config_mail_smtp_hostname');
		$mail->smtp_username = $this->config->get('config_mail_smtp_username');
		$mail->smtp_password = html_entity_decode($this->config->get('config_mail_smtp_password'), ENT_QUOTES, 'UTF-8');
		$mail->smtp_port     = $this->config->get('config_mail_smtp_port');
		$mail->smtp_timeout  = $this->config->get('config_mail_smtp_timeout');

		$mail->setTo($toEmail);
		$mail->setFrom($this->config->get('config_email'));
		$mail->setSender(html_entity_decode($mailData['storeName'], ENT_QUOTES, 'UTF-8'));
		$mail->setSubject($mailData['subject']);

		if (!$mailData['type']) {
			$message = $mailData['body'];
		} else {
			$message = html_entity_decode($mailData['welcome'], ENT_QUOTES, 'UTF-8');
		}

		$mail->setHtml($message);
		$mail->send();
	}

	private function prepareMailData($request, $product_info, $new_price) {
		$module_data = $this->config->get('oct_price_tracker_data');
		$storeName = $this->config->get('config_name');
		$type = 0;

		$language_id = isset($request['language_id']) ? $request['language_id'] : $this->config->get('config_language_id');
		$language_data = $this->loadLanguageForEmail($language_id);

		$this->load->model('localisation/currency');
		$currency_code = $request['currency_code'] ?? $this->config->get('config_currency');
		$currency_info = $this->model_localisation_currency->getCurrencyByCode($currency_code);
		
		if ($currency_info) {
			$decimal_place = (int)$currency_info['decimal_place'];
			$symbol_left = $currency_info['symbol_left'];
			$symbol_right = $currency_info['symbol_right'];
			
			$old_price_formatted = $symbol_left . number_format($request['current_price'], $decimal_place) . $symbol_right;
			$new_price_formatted = $symbol_left . number_format($new_price, $decimal_place) . $symbol_right;
		} else {
			$old_price_formatted = number_format($request['current_price'], 2) . ' ' . $currency_code;
			$new_price_formatted = number_format($new_price, 2) . ' ' . $currency_code;
		}

		$email_data = array();
		$email_data['product_link'] = $this->url->link('product/product', 'product_id=' . $product_info['product_id'], true);
		$email_data['product_name'] = htmlspecialchars($product_info['name'], ENT_QUOTES, 'UTF-8');
		$email_data['store_name'] = htmlspecialchars($this->config->get('config_name'), ENT_QUOTES, 'UTF-8');
		$email_data['customer_name'] = htmlspecialchars($request['customer_name'], ENT_QUOTES, 'UTF-8');
		$email_data['old_price'] = $old_price_formatted;
		$email_data['new_price'] = $new_price_formatted;

		$email_data['subject'] = sprintf(
			isset($language_data['mail_subject']) ? $language_data['mail_subject'] : 'Price change - %s - %s',
			html_entity_decode($storeName, ENT_QUOTES, 'UTF-8'), 
			$email_data['product_name']
		);
		$email_data['welcome'] = sprintf(
			isset($language_data['mail_welcome']) ? $language_data['mail_welcome'] : 'Hello from %s!',
			html_entity_decode($storeName, ENT_QUOTES, 'UTF-8')
		);
		$email_data['body'] = sprintf(
			isset($language_data['mail_body']) ? $language_data['mail_body'] : 'Hello %s! Price of %s changed: was %s, now %s. Link: %s',
			$email_data['customer_name'], 
			$email_data['product_name'], 
			$old_price_formatted, 
			$new_price_formatted, 
			$email_data['product_link']
		);

		if (isset($module_data['custom_message']) && $module_data['custom_message']) {
			if (isset($module_data['subject'][$language_id]) && isset($module_data['message'][$language_id])) {
				$email_data['subject'] = $module_data['subject'][$language_id];
				$email_data['welcome'] = $module_data['message'][$language_id];
				$email_data['body'] = '';
				$type = 1;
				$this->convertHtmlMailData($email_data);
			}
		}

		return [
			'welcome' => $email_data['welcome'],
			'subject' => $email_data['subject'],
			'body' => $email_data['body'],
			'storeName' => $email_data['store_name'],
			'type' => $type
		];
	}

	private function loadLanguageForEmail($language_id) {
		$this->load->model('localisation/language');
		$language_info = $this->model_localisation_language->getLanguage($language_id);
		
		if ($language_info) {
			$language_code = $language_info['code'];
			$language_file = DIR_LANGUAGE . $language_code . '/octemplates/module/oct_price_tracker.php';
			
			if (file_exists($language_file)) {
				$_ = array();
				include $language_file;
				return $_;
			}
		}
		
		$current_language_file = DIR_LANGUAGE . $this->config->get('config_language') . '/octemplates/module/oct_price_tracker.php';
		if (file_exists($current_language_file)) {
			$_ = array();
			include $current_language_file;
			return $_;
		}
		
		return array();
	}

	private function convertHtmlMailData(&$data) {
		$replacements = array(
			'[customer_name]' => $data['customer_name'],
			'[store_name]'    => $data['store_name'],
			'[product_name]'  => $data['product_name'],
			'[old_price]'     => $data['old_price'],
			'[new_price]'     => $data['new_price'],
			'[product_url]'   => $data['product_link']
		);

		$data['welcome'] = str_replace(array_keys($replacements), array_values($replacements), $data['welcome']);
		$data['subject'] = str_replace(array_keys($replacements), array_values($replacements), $data['subject']);

		return $data;
	}

	private function redirectToNotFound() {
		$this->response->redirect($this->url->link('error/not_found', '', true));
	}

	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');
	}

	protected function validate() {
		$oct_price_tracker_data = $this->config->get('oct_price_tracker_data');

		if (isset($this->request->post['customer_name'])) {
			$this->request->post['customer_name'] = $this->sanitizeString($this->request->post['customer_name'], 32);
		}

		if (isset($oct_price_tracker_data['customer_name']) && $oct_price_tracker_data['customer_name'] && (utf8_strlen(trim($this->request->post['customer_name'])) < 1 || utf8_strlen(trim($this->request->post['customer_name'])) > 32)) {
			$this->error['customer_name'] = $this->language->get('error_customer_name');
		}

		if (isset($this->request->post['phone']) && isset($oct_price_tracker_data['phone']) && $oct_price_tracker_data['phone']) {
			$postedPhone = $this->request->post['phone'] ?? '';
			$oct_promo_data  = $this->config->get('theme_oct_promo_data');
			
			$cleanPhoneDigits = preg_replace('/[^0-9]/', '', $postedPhone);
			
			if (utf8_strlen($cleanPhoneDigits) < 3 || utf8_strlen($cleanPhoneDigits) > 15) {
				$this->error['phone'] = $this->language->get('error_phone');
			} 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;
									break;
								}
							}
						}
					}
				}
				
				if (!$hasAnyRegex) {
					$isValid = true;
				} elseif (!$isValid) {
					$this->error['phone'] = $this->language->get('error_phone_mask');
				}
			}
		}

		if (isset($this->request->post['email']) && !filter_var($this->request->post['email'], FILTER_VALIDATE_EMAIL)) {
			$this->error['email'] = $this->language->get('error_email');
		}

		if ($this->config->get('config_account_id') && !isset($this->request->post['agree'])) {
			$this->load->model('catalog/information');

			$information_info = $this->model_catalog_information->getInformation($this->config->get('config_account_id'));
			if (isset($information_info) && !empty($information_info)) {
				$this->error['agree'] = sprintf($this->language->get('error_agree'), $information_info['title']);
			}
		}

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

			if ($captcha) {
				$this->error['captcha'] = $captcha;
			}
		}

		return !$this->error;
	}
}