<?php

/**
 * @package SP Page Builder
 * @author JoomShaper http://www.joomshaper.com
 * @copyright Copyright (c) 2010 - 2025 JoomShaper
 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPLv2 or later
 */

//no direct access
defined('_JEXEC') or die('Restricted access');

use Joomla\CMS\Factory;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\Http\Http;
use Joomla\CMS\Table\Table;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Filesystem\File;
use Joomla\CMS\Session\Session;
use Joomla\CMS\Filter\OutputFilter;
use Joomla\CMS\Response\JsonResponse;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
use Joomla\CMS\MVC\Controller\FormController;
use Joomla\Registry\Registry;
use Joomla\Filesystem\File as FilesystemFile;
use JoomShaper\SPPageBuilder\DynamicContent\Constants\CollectionIds;
use JoomShaper\SPPageBuilder\DynamicContent\Models\Collection;
use JoomShaper\SPPageBuilder\DynamicContent\Models\Page;
use JoomShaper\SPPageBuilder\DynamicContent\Supports\Arr;
use JoomShaper\SPPageBuilder\DynamicContent\Supports\Str;

require_once __DIR__ . '/traits/PageVersionsTrait.php';

class SppagebuilderControllerPage extends FormController
{
	use PageVersionsTrait;

	public function __construct($config = array())
	{
		parent::__construct($config);

		if (!Session::checkToken())
		{
			$response = [
				'status' => false,
				'message' => Text::_('JLIB_ENVIRONMENT_SESSION_EXPIRED')
			];

			echo json_encode($response);
			die();
		}

		// check have access
		$user = Factory::getUser();
		$canEdit = $user->authorise('core.edit', 'com_sppagebuilder');
		$canEditOwn = $user->authorise('core.edit.own', 'com_sppagebuilder');

		$pageCreatorId = null;

		if(!$canEdit && $canEditOwn) {
			$input = Factory::getApplication()->input;
			$pageId = $input->getInt('id', 0);

			$model = $this->getModel('Form');
			$pageCreatorId = $model->getPageCreatorId($pageId);
		}

		$canEditPage = $canEdit || ($canEditOwn && $user->id === $pageCreatorId);

		if (!$canEditPage)
		{
			$response = [
				'status' => false,
				'message' => Text::_('JERROR_ALERTNOAUTHOR')
			];

			echo json_encode($response);
			die();
		}
	}

	/**
	 * Get the page model
	 *
	 * @param string $name
	 * @param string $prefix
	 * @param array $config
	 * @return SppagebuilderModelPage
	 */
	public function getModel($name = 'form', $prefix = '', $config = array('ignore_request' => true))
	{
		$model = parent::getModel($name, $prefix, $config);

		return $model;
	}

	/**
	 * Exit from the edit page.
	 *
	 * @return 	void
	 * @since 	4.0.0
	 */
	public function exitExitPage()
	{
		$app 		= Factory::getApplication('site');
		$input 		= $app->input;
		$user		= Factory::getUser();
		$model 		= $this->getModel();
		$pageId = $input->get('id', 0, 'INT');

		$root = Uri::base();
		$root = new Uri($root);

		$response = ['response' => Text::_("COM_SPPAGEBUILDER_ERROR_MSG"), 'status' => false, 'code' => 500];

		if (!$pageId)
		{
			$response['response'] = 'Invalid page ID!';
			$response['status'] = false;
			$response['code'] = 404;
		}

		$table = $model->getTable();

		try
		{
			if ($table->checkIn($pageId))
			{
				$response['response'] = Uri::base() . 'index.php?option=com_sppagebuilder&view=dashboard&tmpl=component';
				$response['status'] = true;
				$response['code'] = 200;
			}
			else
			{
				$response['response'] = 'Cannot checking out the user!';
				$response['status'] = false;
				$response['code'] = 500;
			}
		}
		catch (Exception $e)
		{
			$response['response'] = $e->getMessage();
			$response['status'] = false;
			$response['code'] = 500;
		}

		$statusCode = $response['code'];
		unset($response['code']);

		$app->setHeader('status', $statusCode, true);
		$app->sendHeaders();
		echo new JsonResponse($response);
		$app->close();
	}

	public function checkOutPage(){
		$input = Factory::getApplication()->input;
		$id = $input->get('id', 0, 'INT');
		$model = $this->getModel('Page');
		$response = $model->checkOutPage($id);
		
		response()->json($response);
	}


	public function save($key = null, $urlVar = null)
	{

		$user = Factory::getUser();
		$app      = Factory::getApplication();
		$input = $app->input;
		$Itemid = $input->get('Itemid', 0, 'INT');

		$root = Uri::base();
		$root = new Uri($root);
		$link = $root->getScheme() . '://' . $root->getHost();

		$model    = $this->getModel('Form');
		$data     = $this->input->post->get('jform', array(), 'array');
		$task     = $this->getTask();
		$context  = 'com_sppagebuilder.edit.page';
		$recordId = $data['id'];
		$output = array();

		//Authorized
		if (empty($recordId))
		{
			$authorised = $user->authorise('core.create', 'com_sppagebuilder') || (count((array) $user->getAuthorisedCategories('com_sppagebuilder', 'core.create')));
		}
		else
		{
			$authorised = $user->authorise('core.edit', 'com_sppagebuilder') || $user->authorise('core.edit', 'com_sppagebuilder.page.' . $recordId) || ($user->authorise('core.edit.own', 'com_sppagebuilder.page.' . $recordId) && $data['created_by'] == $user->id);
		}

		if ($authorised !== true)
		{
			$output['status'] = false;
			$output['message'] = Text::_('JERROR_ALERTNOAUTHOR');
			echo json_encode($output);
			die();
		}

		// Check for request forgeries.
		$output['status'] = false;
		$output['message'] = Text::_('JINVALID_TOKEN');
		Session::checkToken() or die(json_encode($output));

		$output['status'] = true;

		// Check for validation errors.
		if ($data === false)
		{
			// Get the validation messages.
			$errors = $model->getErrors();

			$output['status'] = false;
			$output['message'] = '';

			// Push up to three validation messages out to the user.
			for ($i = 0, $n = count((array) $errors); $i < $n && $i < 3; $i++)
			{
				if ($errors[$i] instanceof Exception)
				{
					$output['message'] .= $errors[$i]->getMessage();
				}
				else
				{
					$output['message'] .= $errors[$i];
				}
			}

			// Save the data in the session.
			$app->setUserState('com_sppagebuilder.edit.page.data', $data);

			// Redirect back to the edit screen.
			$url = $link . 'index.php?option=com_sppagebuilder&view=form&layout=edit&tmpl=component&id=' . $recordId . '&Itemid=' . $Itemid;
			$output['redirect'] = $url;
			echo json_encode($output);
			die();
		}

		if ($itemOld = $model->getPageItem($recordId))
		{
			if ($itemOld->extension == 'com_content' && $itemOld->extension_view == 'article' && $itemOld->view_id)
			{
				$data['catid'] = $itemOld->catid;
			}
		}

		// Attempt to save the data.
		if (!$model->save($data))
		{
			// Save the data in the session.
			$app->setUserState('com_sppagebuilder.edit.page.data', $data);

			// Redirect back to the edit screen.
			$output['status'] = false;
			$output['message'] = Text::sprintf('JLIB_APPLICATION_ERROR_SAVE_FAILED', $model->getError());
			$output['redirect'] = $link . 'index.php?option=com_sppagebuilder&view=form&layout=edit&tmpl=component&id=' . $recordId . '&Itemid=' . $Itemid;
			echo json_encode($output);
			die();
		}

		// Save succeeded, check-in the row.
		if ($model->checkin($data['id']) === false)
		{

			// Check-in failed, go back to the row and display a notice.
			$output['status'] = false;
			$output['message'] = Text::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED', $model->getError());
			$output['redirect'] = $link . 'index.php?option=com_sppagebuilder&view=form&layout=edit&tmpl=component&id=' . $recordId . '&Itemid=' . $Itemid;
			echo json_encode($output);
			die();
		}

		$output['status'] = true;
		$output['message'] = Text::_('COM_SPPAGEBUILDER_PAGE_SAVE_SUCCESS');

		// Redirect the user and adjust session state based on the chosen task.
		switch ($task)
		{
			case 'apply':
				// Set the row data in the session.
				$this->holdEditId($context, $recordId);
				$app->setUserState('com_sppagebuilder.edit.page.data', null);

				// Convert json to readable article text
				$oldPage = $model->getItem($recordId);

				if ($oldPage->extension == 'com_content' && $oldPage->extension_view == 'article')
				{
					$model->addArticleFullText($oldPage->view_id, $oldPage->content);
				}

				// Delete generated CSS file
				$css_folder_path = JPATH_ROOT . '/media/com_sppagebuilder/css';
				$css_file_path = $css_folder_path . '/page-' . $recordId . '.css';
				if (file_exists($css_file_path))
				{
					FilesystemFile::delete($css_file_path);
				}

				// Redirect back to the edit screen.
				$output['redirect'] = $link . 'index.php?option=com_sppagebuilder&view=form&layout=edit&tmpl=component&id=' . $recordId . '&Itemid=' . $Itemid;
				$output['id'] = $recordId;
				break;

			default:
				// Clear the row id and data in the session.
				$this->releaseEditId($context, $recordId);
				$app->setUserState('com_sppagebuilder.edit.page.data', null);

				// Redirect to the list screen.
				$output['redirect'] = $link . 'index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend();
				break;
		}

		echo json_encode($output);
		die();
	}

	public function getMySections()
	{
		$model = $this->getModel();
		die($model->getMySections());
	}

	public function deleteSection()
	{
		$model = $this->getModel();
		$app = Factory::getApplication();
		$input = $app->input;

		$id = $input->get('id', '', 'INT');

		die($model->deleteSection($id));
	}

	public function saveSection()
	{
		$model = $this->getModel();
		$app = Factory::getApplication();
		$input = $app->input;

		$title = htmlspecialchars($input->get('title', '', 'STRING'));
		$section = $input->get('section', '', 'RAW');

		if ($title && $section)
		{
			$section_id = $model->saveSection($title, $section);
			echo $section_id;
			die();
		}
		else
		{
			die('Failed');
		}
	}

	public function getMyAddons()
	{
		$model = $this->getModel();
		die($model->getMyAddons());
	}

	/**
	 * Save Addon method.
	 *
	 * @return	void
	 * @since	1.0.0
	 */
	public function saveCode()
	{
		$input = Factory::getApplication()->input;
		$title = $input->json->get('title', '', 'STRING');
		$code = $input->json->get('code', [], 'ARRAY');
		$codeCategory = $input->json->get('category', '', 'STRING');

		$response = [
			'status' => false,
			'data' => 'Something went wrong! Please try again later.'
		];

		if (empty($title) || empty($code) || empty($codeCategory))
		{
			$response['data'] = 'Information missing.';
			echo json_encode($response);
			die();
		}

		if (is_array($code))
		{
			$code = json_encode($code);
		}

		$data = new stdClass;
		$data->title = $title;

		if ($codeCategory === 'section')
		{
			$data->section = $code;
			$table = '#__sppagebuilder_sections';
		}
		elseif ($codeCategory === 'addon')
		{
			$data->code = $code;
			$table = '#__sppagebuilder_addons';
		}

		$data->created = Factory::getDate()->toSql();
		$data->created_by = Factory::getUser()->id;

		try
		{
			$db = Factory::getDbo();
			$db->insertObject($table, $data, 'id');

			$response = [
				'status' => true,
				'data' => ucfirst($codeCategory) . ' saved successfully!'
			];

			echo json_encode($response);
			die();
		}
		catch (Exception $e)
		{
			$response = [
				'status' => false,
				'data' => $e->getMessage()
			];
			echo json_encode($response);
			die();
		}

		echo json_encode($response);
		die();
	}

	public function deleteAddon()
	{
		$model = $this->getModel();
		$app = Factory::getApplication();
		$input = $app->input;

		$id = $input->get('id', '', 'INT');

		die($model->deleteAddon($id));
	}

	public function myPages()
	{
		$model = $this->getModel('Page');
		$model->getMyPages();
		die();
	}

	public function cancel($key = 'id')
	{
		parent::cancel($key);
		$return_url = Factory::getApplication()->input->get('return_page', null, 'base64');

		$this->setRedirect(base64_decode($return_url));
	}

	public function addToMenu()
	{
		$output = array();

		$data = $this->input->post->get('jform', array(), 'array');
		$pageId = (int) $this->input->get->get('pageId', 0, 'INT');

		BaseDatabaseModel::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_menus/models');
		Table::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_menus/tables');
		$formModel = $this->getModel('Form');
		$model = $this->getModel('Item', 'MenusModel');

		//Check menu
		$menuId = (isset($data['menuid']) && $data['menuid']) ? $data['menuid'] : 0;
		$menutitle = (isset($data['menutitle']) && $data['menutitle']) ? $data['menutitle'] : '';
		$menualias = (isset($data['menualias']) && $data['menualias']) ? $data['menualias'] : OutputFilter::stringURLSafe($menutitle);
		$menutype = (isset($data['menutype']) && $data['menutype']) ? $data['menutype'] : '';
		$menuparent_id = (isset($data['menuparent_id']) && $data['menuparent_id']) ? $data['menuparent_id'] : 0;
		$menuordering = (isset($data['menuordering']) && $data['menuordering']) ? $data['menuordering'] : -2;
		$link = 'index.php?option=com_sppagebuilder&view=page&id=' . (int) $pageId;
		$component_id = ComponentHelper::getComponent('com_sppagebuilder')->id;

		$menu = $formModel->getMenuById($menuId);
		$home = (isset($menu->home) && $menu->home) ? $menu->home : 0;

		$menuData = array(
			'id' => (int) $menuId,
			'link' => $link,
			'parent_id' => (int) $menuparent_id,
			'menutype' => htmlspecialchars($menutype),
			'title' => htmlspecialchars($menutitle),
			'alias' => htmlspecialchars($menualias),
			'type' => 'component',
			'published' => 1,
			'language' => '*',
			'component_id' => $component_id,
			'menuordering' => (int) $menuordering,
			'home' => (int) $home
		);

		$message = ($menuId) ? 'Menu successfully updated' : 'Added to a new menu';

		if ($model->save($menuData))
		{
			$menu = $formModel->getMenuByAlias($menualias);
			$menuId = $menu->id;
			$output['status'] = true;
			$output['alias'] = $menualias;
			$output['menuid'] = $menuId;
			$output['success'] = $message;

			$Itemid = $formModel->getMenuByPageId($pageId);
			$menuItemId = 0;
			if (isset($Itemid->id) && $Itemid->id)
			{
				$menuItemId = '&Itemid=' . $Itemid->id;
			}
			$root = Uri::base();
			$root = new Uri($root);
			$output['redirect'] = Uri::base() . 'index.php?option=com_sppagebuilder&view=form&layout=edit&tmpl=component&id=' . $pageId . $menuItemId;
		}
		else
		{
			$output['status'] = false;
			$output['error'] = $model->getError();
		}

		die(json_encode($output));
	}

	public function getMenuParentItem()
	{

		BaseDatabaseModel::addIncludePath(JPATH_SITE . '/administrator/components/com_menus/models');
		$app = Factory::getApplication();

		$results  = array();
		$menutype = $this->input->get->get('menutype', '');
		$parent_id = $this->input->get->get('parent_id', 0);

		if ($menutype)
		{
			$model = $this->getModel('Items', 'MenusModel', array());
			$model->getState();
			$model->setState('filter.menutype', $menutype);
			$model->setState('list.select', 'a.id, a.title, a.level');
			$model->setState('list.start', '0');
			$model->setState('list.limit', '0');

			$results = $model->getItems();

			for ($i = 0, $n = count($results); $i < $n; $i++)
			{
				$results[$i]->title = str_repeat(' - ', $results[$i]->level) . $results[$i]->title;
			}
		}

		echo json_encode($results);

		$app->close();
	}

	public function createNewPage()
	{
		$output = array();
		$app = Factory::getApplication();

		$user = Factory::getUser();
		$authorised = $user->authorise('core.create', 'com_sppagebuilder');

		if (!$authorised)
		{
			$output['status'] = false;
			$output['message'] = Text::_('JERROR_ALERTNOAUTHOR');
			die(json_encode($output));
		}

		$title = trim(htmlspecialchars($this->input->post->get('title', '', 'STRING')));
		$model = $this->getModel('Form');
		$id = $model->createNewPage($title);

		$root = Uri::base();
		$root = new Uri($root);
		$redirect = Uri::base() . 'index.php?option=com_sppagebuilder&view=form&layout=edit&tmpl=component&id=' . $id;

		$output['status'] = true;
		$output['message'] = Text::_('Page created successfully.');
		$output['redirect'] = $redirect;
		die(json_encode($output));
	}

	public function deletePage()
	{
		$output = array();
		$app = Factory::getApplication();

		$user = Factory::getUser();
		$authorised = $user->authorise('core.delete', 'com_sppagebuilder');

		if (!$authorised)
		{
			$output['status'] = false;
			$output['message'] = Text::_('JERROR_ALERTNOAUTHOR');
			die(json_encode($output));
		}

		$pageid = (int) $this->input->post->get('pageid', '', 'INT');

		$model = $this->getModel('Form');
		$result = $model->deletePage($pageid);

		if (!$result)
		{
			$output['message'] = Text::_('Unable to delete this page.');
		}

		$output['status'] = $result;
		die(json_encode($output));
	}

	private function getFields($fieldset, $xml)
	{
		// Make sure there is a valid JForm XML document.
		if (!($xml instanceof \SimpleXMLElement))
		{
			return false;
		}

		/*
		 * Get an array of <field /> elements that are underneath a <fieldset /> element
		 * with the appropriate name attribute, and also any <field /> elements with
		 * the appropriate fieldset attribute. To allow repeatable elements only fields
		 * which are not descendants of other fields are selected.
		 */
		$fields = $xml->xpath('(//fieldset[@name="' . $fieldset . '"]//field | //field[@fieldset="' . $fieldset . '"])[not(ancestor::field)]');

		return (array) $fields;
	}

	/**
	 * Export com_content.article Schema.org tab as JSON for the frontend (reactjs) editor.
	 *
	 * @return  void
	 *
	 * @since   5.5.0
	 */
	public function getContentArticleSchemaForm()
	{
		$app = Factory::getApplication();

		if (strtoupper($app->input->getMethod()) !== 'GET')
		{
			echo json_encode(['available' => false, 'message' => 'Method not allowed']);
			die();
		}

		if (!\interface_exists(\Joomla\CMS\Schemaorg\SchemaorgServiceInterface::class))
		{
			echo json_encode([
				'available' => false,
				'message'   => Text::_('COM_SPPAGEBUILDER_EDITOR_SCHEMA_FORM_JOOMLA_VERSION'),
			]);
			die();
		}

		$helper = JPATH_ADMINISTRATOR . '/components/com_sppagebuilder/editor/helpers/ContentArticleSchemaFormExporter.php';

		if (!\is_file($helper))
		{
			echo json_encode(['available' => false, 'message' => 'Exporter missing']);
			die();
		}

		require_once $helper;

		$preparerPath = JPATH_ADMINISTRATOR . '/components/com_sppagebuilder/editor/helpers/ContentArticleSchemaFormPreparer.php';

		if (\is_file($preparerPath))
		{
			require_once $preparerPath;
		}

		try
		{
			$component = $app->bootComponent('com_content');

			if (!$component instanceof \Joomla\CMS\Schemaorg\SchemaorgServiceInterface)
			{
				echo json_encode([
					'available' => false,
					'message'   => Text::_('COM_SPPAGEBUILDER_EDITOR_SCHEMA_FORM_COM_CONTENT'),
				]);
				die();
			}

			$model = $component->getMVCFactory()->createModel('Article', 'Administrator', ['ignore_request' => true]);
			$base  = JPATH_ADMINISTRATOR . '/components/com_content';

			if (!\is_dir($base))
			{
				throw new \RuntimeException('com_content administrator files not found');
			}

			$formFactory = Factory::getContainer()->get(\Joomla\CMS\Form\FormFactoryInterface::class);
			$form        = $formFactory->createForm(
				'com_content.article',
				['control' => 'jform', 'load_data' => false]
			);

			if ($form instanceof \Joomla\CMS\User\CurrentUserInterface)
			{
				$form->setCurrentUser($app->getIdentity());
			}

			\Joomla\CMS\Form\Form::addFormPath($base . '/forms');
			\Joomla\CMS\Form\Form::addFormPath($base . '/models/forms');
			\Joomla\CMS\Form\Form::addFieldPath($base . '/models/fields');
			\Joomla\CMS\Form\Form::addFormPath($base . '/model/form');
			\Joomla\CMS\Form\Form::addFieldPath($base . '/model/field');

			if (!$form->loadFile('article', false))
			{
				throw new \RuntimeException(Text::_('COM_SPPAGEBUILDER_EDITOR_SCHEMA_FORM_LOAD_FAILED'));
			}

			$data = [];
			$prep = new \ReflectionMethod($model, 'preprocessForm');
			$prep->setAccessible(true);
			$prep->invoke($model, $form, $data, 'content');
			$form->bind($data);

			if (\class_exists('ContentArticleSchemaFormPreparer'))
			{
				ContentArticleSchemaFormPreparer::ensureSchemaorgTab($form);
			}

			$exporter = new ContentArticleSchemaFormExporter();
			$payload  = $exporter->export($form);
			$plugin = PluginHelper::getPlugin('system', 'schemaorg');
			$link = null;

			if ($plugin)
			{
				$params = new Registry($plugin->params);
				if (!$params->get('baseType'))
				{
					$link = Uri::root() . 'administrator/index.php?option=com_plugins&task=plugin.edit&extension_id=' . (int) $plugin->id;
				}
			}

			$payload['link'] = $link;
			$payload['form_empty'] = !empty($link) || empty($payload['fields']);

			echo json_encode($payload);
		}
		catch (\Throwable $e)
		{
			echo json_encode([
				'available' => false,
				'message'   => $e->getMessage(),
			]);
		}

		die();
	}

	/**
	 * Read the page.xml form and extract information from it for rendering
	 * into reactJS.
	 *
	 * @return	void
	 * @since	4.0.0
	 */
	public function getPageForm()
	{
		$model = $this->getModel();
		$input = Factory::getApplication()->input;
		$id = $input->getInt('id', 0);
		$model->setState('page.id', $id);

		$data = [];
		$form = $model->getForm();
		$formXml = $form->getXml();
		$groups = $form->getFieldsets();

		foreach ($groups as $name => $group)
		{
			$fields = $this->getFields($name, $formXml);

			foreach ($fields as $field)
			{
				if (version_compare(PHP_VERSION, '7.4.0', '>='))
				{
					$simpleXMLObj = get_mangled_object_vars($field->attributes());
					$fieldArray = (new ArrayIterator($simpleXMLObj))->current();
				}
				else
				{
					$fieldArray = current($field->attributes());
				}


				$fieldName = $fieldArray['name'];

				$fieldArray['label'] = isset($fieldArray['label']) ? Text::_($fieldArray['label']) : '';
				$fieldArray['desc'] = isset($fieldArray['desc']) ? Text::_($fieldArray['desc']) : '';
				$fieldArray['placeholder'] = isset($fieldArray['placeholder']) ? Text::_($fieldArray['placeholder']) : '';

				$value = $form->getValue($fieldName);
				$fieldArray['value'] = $value;

				if($fieldName === 'language') {
					$fieldArray['options'] = $model->getLanguages();
					$data[$name][] = $fieldArray;

					continue;
				}

				$formField = $form->getField($fieldName);
				$options = $formField->options;

				if (isset($options))
				{
					foreach ($options as &$option)
					{
						$option->text = Text::_($option->text);
					}

					unset($option);

					$fieldArray['options'] = $options;
				}

				$data[$name][] = $fieldArray;
			}
		}

		$response = ['status' => true, 'data' => $data];

		echo json_encode($response);
		die();
	}

	/**
	 * Save the page data.
	 *
	 * @return	void
	 * @since	4.0.0
	 */
	public function saveData()
	{
		$input = Factory::getApplication()->input;
		$id = $input->get('id', 0, 'INT');
		$data = $input->json->get('data', [], 'ARRAY');

		if ($data['published'] === '')
		{
			$data['published'] = '0';
		}

		$model = $this->getModel();

		$response = $model->saveData($data, $id);

		$model->checkin($id);

		echo json_encode($response);
		die();
	}

	/**
	 * @return void
	 * @since 5.4.0
	 */
	public function savePopupSettings()
	{
		$input = Factory::getApplication()->input;
		$id = $input->get('id', 0, 'INT');
		$data = $input->json->get('data', [], 'ARRAY');

		$model = $this->getModel();

		$response = $model->savePopupSettingsData($data, $id);

		$model->checkin($id);

		echo json_encode($response);
		die();
	}

	public function getVersioningState(){
		$params = ComponentHelper::getParams('com_sppagebuilder');
		$versioningEnabled = $params->get('enable_page_versioning', '1');

		$response = [
			'status' => true,
			'data' => [
				'versioning_enabled' => $versioningEnabled
			]
		];
		
		echo json_encode($response);
		die();
	}

	/**
	 * Get the page data.
	 *
	 * @return	void
	 * @since	5.4.0
	 */
	public function getData()
	{
		$input = Factory::getApplication()->input;
		$id = $input->get('id', 0, 'INT');

		$model = $this->getModel();

		$response = $model->getData($id);

		$model->checkin($id);

		echo json_encode($response);
		die();
	}

	/**
	 * Get importing layout page information from JoomShaper site.
	 *
	 * @return	void
	 * @since	4.0.0
	 */
	public function importLayout()
	{
		$http = new Http;
		$input = Factory::getApplication()->input;
		$params = ComponentHelper::getParams('com_sppagebuilder');

		$id = $input->get('id', 0, 'INT');
		$type = $input->get('type', 'template', 'STRING');

		$email = $params->get('joomshaper_email');
		$apiKey = $params->get('joomshaper_license_key');

		$response = [
			'status' => false,
			'data' => 'Page not found!'
		];

		if (empty($id))
		{
			$response = [
				'status' => false,
				'data' => 'Invalid layout ID given!'
			];

			echo json_encode($response);
			die();
		}

		if (empty($email) || empty($apiKey))
		{
			$response = [
				'status' => false,
				'data' => 'Invalid email and license key!'
			];

			echo json_encode($response);
			die();
		}

		$apiURL = 'https://www.joomshaper.com/index.php?option=com_layouts&task=' . $type . '.download&support=4beyond&id=' . $id . '&email=' . urlencode($email) . '&api_key=' . $apiKey;
		$pageResponse = $http->get($apiURL);
		$pageData = $pageResponse->body;

		if ($pageResponse->code !== 200)
		{
			$response = ['status' => false, 'data' => $pageData->error->message];
		}

		if (!empty($templatesData))
		{
			File::write($cache_file, $templatesData);
		}

		if (!empty($pageData))
		{
			$pageData = json_decode($pageData);
			$pageDataContent = $pageData->content;
			$content = (object) ['template' => '', 'css' => ''];

			if (!isset($pageDataContent->template))
			{
				$content->template = json_encode($pageDataContent);
			}
			else
			{
				$pageDataContent->template = !\is_string($pageDataContent->template)
					? json_encode($pageDataContent->template)
					: $pageDataContent->template;

				$content = $pageDataContent;
			}

			if (!empty($pageData->status))
			{
				require_once JPATH_COMPONENT_SITE . '/builder/classes/addon.php';
				$content->template = ApplicationHelper::sanitizePageText($content->template);
				$content->template = json_encode($content->template);
				$content->template = json_decode(SppagebuilderHelperSite::sanitizeImportJSON($content->template));

				$response = [
					'status' => true,
					'data' => $content
				];

				echo json_encode($response);
				die();
			}
			elseif ($pageData->authorised)
			{
				$response = [
					'status' => false,
					'data' => $pageData->authorised
				];

				echo json_encode($response);
				die();
			}
		}

		echo json_encode($response);
		die();
	}

	/**
	 * Save page information to the database.
	 *
	 * @return	void
	 * @since	4.0.0
	 */
	public function savePage()
	{
		$input = Factory::getApplication()->input;
		$id = $input->getInt('id', 0);
		$data = $input->json->get('data', [], 'ARRAY');

		// Get version_name and version_note from request if provided
		$versionName = $input->json->getString('version_name', '');
		$versionNote = $input->json->getString('version_note', '');

		$response = [
			'status' => false,
			'message' => 'Something went wrong! Page not saved.'
		];

		if (empty($id))
		{
			$response['message'] = 'Invalid page id given!';
			echo json_encode($response);
			die();
		}

		if (!empty($data))
		{
			foreach ($data as &$row)
			{
				foreach ($row['columns'] as &$column)
				{
					foreach ($column['addons'] as &$addon)
					{
						if (isset($addon['type']) && ($addon['type'] === 'sp_row' || $addon['type'] === 'inner_row'))
						{
							foreach ($addon['columns'] as &$innerColumn)
							{
								foreach ($innerColumn['addons'] as &$innerAddon)
								{
									if (isset($innerAddon['htmlContent']))
									{
										unset($innerAddon['htmlContent']);
									}

									if (isset($innerAddon['assets']))
									{
										unset($innerAddon['assets']);
									}
								}

								unset($innerAddon);
							}

							unset($innerColumn);
						}
						else
						{
							if (isset($addon['htmlContent']))
							{
								unset($addon['htmlContent']);
							}

							if (isset($addon['assets']))
							{
								unset($addon['assets']);
							}
						}
					}

					unset($addon);
				}

				unset($column);
			}

			unset($row);
		}

		$version = SppagebuilderHelper::getVersion();
		$pageData 			   = new stdClass;
		$pageData->id 		   = $id;
		$pageData->content 	   = json_encode($data);
		// $pageData->content 	   = EditorUtils::cleanXSS($data);
		$pageData->modified    = Factory::getDate()->toSql();
		$pageData->modified_by = Factory::getUser()->get('id');
		$pageData->version	   = $version;

		/** @var SppagebuilderModelForm */
		$model     = $this->getModel('Form');
		$pageModel = $this->getModel();
		$oldPage   = $model->getItem($id);
		$oldPage = ApplicationHelper::preparePageData($oldPage);

		if ($oldPage->extension == 'com_content' && $oldPage->extension_view == 'article')
		{
			$model->addArticleFullText($oldPage->view_id, json_encode($oldPage->text));
		}

		try
		{
			// $db = Factory::getDbo();
			// $db->updateObject('#__sppagebuilder', $pageData, 'id', true);

			if (!$model->savePage((array) $pageData))
			{
				$response = [
					'status' => false,
					'message' => 'Error saving the page'
				];

				echo json_encode($response);
				die();
			}

			$params = ComponentHelper::getParams('com_sppagebuilder');
            $pageVersioningEnabled = $params->get('enable_page_versioning', 1);
            $versioningOnSaveDisabled = !$params->get('enable_versioning_on_save', 1);
			$is_triggered_from_save = !$input->json->getBool('is_triggered_from_versioning', false);
			$is_pro_version = !$input->json->getBool('is_free_version', false);

			// Create version snapshot after successful save
			// Use custom name and note if provided
			if ($is_pro_version && $pageVersioningEnabled) {
				if(!($is_triggered_from_save && $versioningOnSaveDisabled)) {
					$this->createVersionSnapshot($id, !empty($versionName) ? $versionName : null, !empty($versionNote) ? $versionNote : null);
				}
			}

			$pageModel->checkin($id);

			$response = [
				'status' => true,
				'message' => 'Page data saved successfully!'
			];

			echo json_encode($response);
			die();
		}
		catch (Exception $e)
		{
			$response = [
				'status' => false,
				'message' => $e->getMessage()
			];

			echo json_encode($response);
			die();
		}

		echo json_encode($response);
		die();
	}

	public function loadPagesList()
	{
		$db = Factory::getDbo();
		$query = $db->getQuery(true);

		$pageTypes = array_map(
			[$db, 'quote'],
			[
				Page::PAGE_TYPE_REGULAR,
				Page::PAGE_TYPE_DYNAMIC_CONTENT_INDEX,
				Page::PAGE_TYPE_DYNAMIC_CONTENT_DETAIL
			]
		);

		$query->select(['id', 'title', 'extension_view', 'view_id'])
			->from($db->quoteName('#__sppagebuilder'))
			->where($db->quoteName('extension_view') . 'IN (' . implode(',', $pageTypes) . ')')
			->where($db->quoteName('published') . ' = 1')
			->order($db->quoteName('title') . ' ASC');

		$db->setQuery($query);

		try
		{
			$pages = $db->loadObjectList();

			$pages = Arr::make($pages);
			$pages = $pages->reduce(function($carry, $current) {
				if (in_array($current->extension_view, [Page::PAGE_TYPE_DYNAMIC_CONTENT_INDEX, Page::PAGE_TYPE_DYNAMIC_CONTENT_DETAIL]) && $current->view_id === CollectionIds::ARTICLES_COLLECTION_ID) {
					$carry['articles'] ??= [
						'label' => Text::_('COM_SPPAGEBUILDER_PAGE_TYPE_ARTICLES'),
						'icon' => 'articles',
						'options' => []
					];
					$carry['articles']['options'][] = $current;
				} elseif (in_array($current->extension_view, [Page::PAGE_TYPE_DYNAMIC_CONTENT_INDEX, Page::PAGE_TYPE_DYNAMIC_CONTENT_DETAIL])) {
					$carry['cms'] ??= [
						'label' => Text::_('COM_SPPAGEBUILDER_PAGE_TYPE_DYNAMIC_CONTENT'),
						'icon' => 'dynamicContent',
						'options' => []
					];
					$carry['cms']['options'][] = $current;
				} elseif ($current->extension_view === Page::PAGE_TYPE_REGULAR) {
					$carry['page'] ??= [
						'label' => Text::_('COM_SPPAGEBUILDER_PAGE_TYPE_PAGES'),
						'icon' => 'pages',
						'options' => []
					];
					$carry['page']['options'][] = $current;
				} else {
					$carry[$current->extension_view] ??= [
						'label' => ucfirst($current->extension_view),
						'options' => []
					];
					$carry[$current->extension_view]['options'][] = $current;
				}
				return $carry;
			}, []);

			$pages = $pages->map(function ($page) {
				$items = Arr::make($page['options']);
				$page['options'] = $items->map(function ($item) {
					if ($item->extension_view === Page::PAGE_TYPE_DYNAMIC_CONTENT_INDEX) {
						$collection = ($item->view_id === CollectionIds::ARTICLES_COLLECTION_ID || $item->view_id === CollectionIds::TAGS_COLLECTION_ID) ? null : Collection::find($item->view_id);
						$item->legend = '/' . ($collection ? $collection->alias : ($item->view_id === CollectionIds::TAGS_COLLECTION_ID ? 'tags' : 'articles'));
					} elseif ($item->extension_view === Page::PAGE_TYPE_DYNAMIC_CONTENT_DETAIL) {
						$collection = ($item->view_id === CollectionIds::ARTICLES_COLLECTION_ID || $item->view_id === CollectionIds::TAGS_COLLECTION_ID) ? null : Collection::find($item->view_id);
						$item->legend = '/' . ($collection ? $collection->alias : ($item->view_id === CollectionIds::TAGS_COLLECTION_ID ? 'tags' : 'articles')) . '/:slug';
					}
					return (object) [
						'label' => $item->title,
						'value' => $item->id,
						'legend' => $item->legend ?? ''
					];
				})->toArray();
				return $page;
			});

			$pages = $pages->toArray();

			$isAppendArticleDetails = true;
			$articlesIndex = -1;

			if (!empty($pages))
			{
				foreach ($pages as $index => $page)
				{
					if (isset($page['label']))
					{
						if ($page['label'] === Text::_('COM_SPPAGEBUILDER_PAGE_TYPE_ARTICLES'))
						{
							$articlesIndex = $index;
						}
					}

					if (isset($page['options']) && is_array($page['options']))
					{
						foreach ($page['options'] as $option)
						{
							if (isset($option->legend) && $option->legend === '/articles/:slug')
							{
								$isAppendArticleDetails = false;
								break;
							}
						}
					}
				}
			}

			if ($isAppendArticleDetails)
			{
				if ($articlesIndex === -1)
				{
					$pages[] = [
						'label' => Text::_('COM_SPPAGEBUILDER_PAGE_TYPE_ARTICLES'),
						'icon' => 'articles',
						'options' => [
							(object) [
								'label' => 'Article Details',
								'value' => CollectionIds::ARTICLES_COLLECTION_ID,
								'legend' => '/articles/:slug',
							]
						]
					];
				}
				else
				{	
					array_unshift($pages[$articlesIndex]['options'], (object) [
						'label' => 'Article Details',
						'value' => CollectionIds::ARTICLES_COLLECTION_ID,
						'legend' => '/articles/:slug',
					]);
				}
				
			}

			echo json_encode($pages);
			exit();
		}
		catch (\Exception $e)
		{
			echo json_encode([]);
			exit();
		}
	}

	public function loadPopupsList(){
		$result = [];

		$db 	= Factory::getDbo();
		$query 	= $db->getQuery(true);
		$query->select('id AS value, title AS label')
			->from($db->quoteName('#__sppagebuilder'))
			->where($db->quoteName('extension_view') . '=' . $db->quote(Page::PAGE_TYPE_POPUP))
			->where($db->quoteName('published') . ' = 1');
		$query->order($db->quoteName('ordering') . ' ASC');
		$db->setQuery($query);

		try
		{
			$result = $db->loadObjectList();
		}
		catch (Exception $e)
		{
			echo json_encode([]);
			die;
		}

		echo \json_encode($result);
		die;
	}

	public function loadSiteMenus()
	{
		$result = [];

		$db 	= Factory::getDbo();
		$query 	= $db->getQuery(true);
		$query->select('id, title, link, menutype')
			->from($db->quoteName('#__menu'))
			->where($db->quoteName('id') . ' > 1')
			->where($db->quoteName('published') . ' = 1')
			->where($db->quoteName('client_id') . ' = 0');
		$query->order($db->quoteName('lft') . ' ASC');
		$db->setQuery($query);

		try
		{
			$result = $db->loadObjectList();
		}
		catch (Exception $e)
		{
			echo json_encode([]);
			die;
		}

		if (!empty($result))
		{
			$result = array_map(function ($item)
			{
				$obj = new \stdClass;
				$obj->value = $item->link . '&Itemid=' . $item->id;
				$obj->label = Text::_($item->title);

				return $obj;
			}, $result);
		}

		echo \json_encode($result);
		die;
	}

	/**
	 * Create a version snapshot of the current page
	 *
	 * @param   int     $pageId      The page ID
	 * @param   string  $customName  Optional custom name for the version
	 * @param   string  $customNote  Optional custom note for the version
	 *
	 * @return  void
	 * @since   6.2.4
	 */
	private function createVersionSnapshot($pageId, $customName = null, $customNote = null)
	{
		try
		{
			// Get current page data after saving
			$db = Factory::getDbo();
			$query = $db->getQuery(true);
			$query->select([
				$db->quoteName('content'),
				$db->quoteName('css'),
				$db->quoteName('attribs'),
				$db->quoteName('og_title'),
				$db->quoteName('og_image'),
				$db->quoteName('og_description')
			])
			->from($db->quoteName('#__sppagebuilder'))
			->where($db->quoteName('id') . ' = ' . (int) $pageId);

			$db->setQuery($query);
			$pageData = $db->loadObject();

			if (!$pageData)
			{
				return; // Page doesn't exist, skip version creation
			}

			// If a custom note is provided, always create a snapshot (manual note addition)
			// Otherwise, compare with the last active version to see if settings changed
			if (empty($customNote))
			{
				// Get the last active version to compare
				$versionQuery = $db->getQuery(true);
				$versionQuery->select([
					$db->quoteName('content'),
					$db->quoteName('css'),
					$db->quoteName('attribs'),
					$db->quoteName('og_title'),
					$db->quoteName('og_image'),
					$db->quoteName('og_description')
				])
				->from($db->quoteName('#__sppagebuilder_versions'))
				->where($db->quoteName('page_id') . ' = ' . (int) $pageId)
				->where($db->quoteName('active') . ' = 1')
				->order($db->quoteName('created_on') . ' DESC')
				->setLimit(1);

				$db->setQuery($versionQuery);
				$activeVersion = $db->loadObject();

				// If there's an active version, compare settings
				if ($activeVersion)
				{
					$hasChanges = false;

					// Normalize and compare content
					$currentContent = $pageData->content ?? '';
					$activeContent = $activeVersion->content ?? '';
					if ($currentContent !== $activeContent)
					{
						$hasChanges = true;
					}

					// Compare CSS
					if (!$hasChanges)
					{
						$currentCss = $pageData->css ?? '';
						$activeCss = $activeVersion->css ?? '';
						if ($currentCss !== $activeCss)
						{
							$hasChanges = true;
						}
					}

					// Compare attribs
					if (!$hasChanges)
					{
						$currentAttribs = $pageData->attribs ?? '[]';
						$activeAttribs = $activeVersion->attribs ?? '[]';
						if ($currentAttribs !== $activeAttribs)
						{
							$hasChanges = true;
						}
					}

					// Compare OG fields
					if (!$hasChanges)
					{
						$currentOgTitle = $pageData->og_title ?? '';
						$activeOgTitle = $activeVersion->og_title ?? '';
						$currentOgImage = $pageData->og_image ?? '';
						$activeOgImage = $activeVersion->og_image ?? '';
						$currentOgDescription = $pageData->og_description ?? '';
						$activeOgDescription = $activeVersion->og_description ?? '';

						if ($currentOgTitle !== $activeOgTitle || 
							$currentOgImage !== $activeOgImage || 
							$currentOgDescription !== $activeOgDescription)
						{
							$hasChanges = true;
						}
					}

					// If no changes detected, skip version creation
					if (!$hasChanges)
					{
						return;
					}
				}
			}

			// Use custom name if provided, otherwise format date/time for version name: "Feb 13, 2026, 7:06:30 AM"
			if (!empty($customName))
			{
				$versionName = $customName;
			}
			else
			{
				$date = Factory::getDate();
				$versionName = $date->format('M j, Y, g:i:s A', true);
			}

			$maxVersionsForPage = (int) ComponentHelper::getParams('com_sppagebuilder')->get('max_versions_for_page', 10);
			$maxVersionsForPage = max(3, $maxVersionsForPage);

			$countQuery = $db->getQuery(true);
			$countQuery->select('COUNT(*)')
				->from($db->quoteName('#__sppagebuilder_versions'))
				->where($db->quoteName('page_id') . ' = ' . (int) $pageId);
			$db->setQuery($countQuery);
			$currentVersionCount = (int) $db->loadResult();

			if ($currentVersionCount >= $maxVersionsForPage)
			{
				$oldestVersionQuery = $db->getQuery(true);
				$oldestVersionQuery->select($db->quoteName('id'))
					->from($db->quoteName('#__sppagebuilder_versions'))
					->where($db->quoteName('page_id') . ' = ' . (int) $pageId)
					->order($db->quoteName('created_on') . ' ASC')
					->setLimit(1);
				$db->setQuery($oldestVersionQuery);
				$oldestVersionId = (int) $db->loadResult();

				if ($oldestVersionId > 0)
				{
					$deleteQuery = $db->getQuery(true);
					$deleteQuery->delete($db->quoteName('#__sppagebuilder_versions'))
						->where($db->quoteName('id') . ' = ' . $oldestVersionId);
					$db->setQuery($deleteQuery);
					$db->execute();
				}
			}

			// Set all other versions of this page to inactive
			$updateQuery = $db->getQuery(true);
			$updateQuery->update($db->quoteName('#__sppagebuilder_versions'))
				->set($db->quoteName('active') . ' = 0')
				->where($db->quoteName('page_id') . ' = ' . (int) $pageId);
			$db->setQuery($updateQuery);
			$db->execute();

			Table::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_sppagebuilder/tables');
			$versionTable = Table::getInstance('Version', 'SppagebuilderTable');

			$versionData = [
				'page_id' => $pageId,
				'name' => $versionName,
				'content' => $pageData->content ?? '',
				'css' => $pageData->css ?? '',
				'attribs' => $pageData->attribs ?? '[]',
				'og_title' => $pageData->og_title ?? '',
				'og_image' => $pageData->og_image ?? '',
				'og_description' => $pageData->og_description ?? '',
				'active' => 1,
				'created_on' => Factory::getDate()->toSql(),
				'created_by' => Factory::getUser()->get('id'),
			];

			// Add custom note if provided
			if (!empty($customNote))
			{
				$versionData['note'] = $customNote;
			}

			$versionTable->bind($versionData);
			$versionTable->store();
		}
		catch (Exception $e)
		{
			// Silently fail - versioning is not critical for saving
			// Log error if needed
		}
	}

}
