<?php
/**
 * @package     jAtomS Package
 * @subpackage  com_jatoms
 * @version     __DEPLOY_VERSION__
 * @author      Atom-S - atom-s.com
 * @copyright   Copyright (c) 2017 - 2021 Atom-S LLC. All rights reserved.
 * @license     GNU/GPL license: https://www.gnu.org/copyleft/gpl.html
 * @link        https://atom-s.com/
 */

/**
 * jAtomS Synchronization CLI.
 *
 * This is a command-line script synchronization Atom-S tour to jAtomS component.
 *
 * Called with no arguments: php jatoms_synchronization.php
 *                           Performs tours synchronization with the display of the dynamic status of each step.
 *
 * Called with --status=hide   `php jatoms_synchronization.php --status=hide`
 *          or --status=false   or `php jatoms_synchronization.php --status=false`
 *                              Performs tours synchronization with displaying only step titles.
 *
 */

// We are a valid entry point
const _JEXEC = 1;

// Load system defines
if (file_exists(dirname(__DIR__) . '/defines.php'))
{
	require_once dirname(__DIR__) . '/defines.php';
}
if (!defined('_JDEFINES'))
{
	define('JPATH_BASE', dirname(__DIR__));
	require_once JPATH_BASE . '/includes/defines.php';
}

define('JPATH_COMPONENT', JPATH_ADMINISTRATOR . '/components/com_jatoms');
define('JPATH_COMPONENT_ADMINISTRATOR', JPATH_ADMINISTRATOR . '/components/com_jatoms');

// Get the framework
require_once JPATH_LIBRARIES . '/import.legacy.php';

// Bootstrap the CMS libraries
require_once JPATH_LIBRARIES . '/cms.php';

// Import the configuration
require_once JPATH_CONFIGURATION . '/configuration.php';

// System configuration
$config = new JConfig;
define('JDEBUG', $config->debug);

// Configure error reporting to maximum for CLI output.
error_reporting(E_ALL);
ini_set('display_errors', 1);

use Joomla\CMS\Access\Access;
use Joomla\CMS\Application\CliApplication;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
use Joomla\CMS\Table\Table;
use Joomla\Utilities\ArrayHelper;

class jAtomsSynchronizationCli extends CliApplication
{
	/**
	 * Minimum PHP version required to install the extension.
	 *
	 * @var  string
	 *
	 * @since  2.0.0
	 */
	protected $minimumPhp = '7.0';

	/**
	 * Steps string max length.
	 *
	 * @var  integer.
	 *
	 * @since  2.0.0
	 */
	protected $_stepTextMaxSize = 0;

	/**
	 * Show status or show only step title.
	 *
	 * @var  boolean.
	 *
	 * @since  2.0.0
	 */
	protected $showStatus = true;

	/**
	 * Line space separator.
	 *
	 * @var  string.
	 *
	 * @since  2.0.0
	 */
	protected $separator = '.';

	/**
	 * Entry point for jAtomS Synchronization CLI script.
	 *
	 * @throws  Exception
	 *
	 * @since  2.0.0
	 */
	public function doExecute()
	{
		// Check PHP version
		if (!(version_compare(PHP_VERSION, $this->minimumPhp) >= 0))
		{
			$this->out('This script is compatible only with PHP ' . $this->minimumPhp . ' and later');

			return;
		}

		// Remove the script time limit
		@set_time_limit(0);

		// Fool the system into thinking we are running as control panel
		$_SERVER['HTTP_HOST'] = 'domain.com';
		Factory::getApplication('administrator');
		Factory::getLanguage()->load('com_jatoms', JPATH_ADMINISTRATOR, 'en-GB');

		// Set super admin user
		$db     = Factory::getDbo();
		$query  = $db->getQuery(true)
			->select(array('id', 'parent_id'))
			->from($db->quoteName('#__usergroups'));
		$rows   = $db->setQuery($query)->loadAssocList('id', 'parent_id');
		$rules  = Access::getAssetRules(1);
		$groups = array();
		foreach ($rows as $id => $parent_id)
		{
			$ids        = array($id);
			$parent_key = $parent_id;
			while (!empty($parent_key) && isset($rows[$parent_key]))
			{
				$ids[]      = $parent_key;
				$parent_key = $rows[$parent_key];
			}
			$ids = array_reverse($ids);

			if ($rules->allow('core.admin', $ids))
			{
				$groups[] = $id;
			}
		}
		$groups = ArrayHelper::toInteger($groups);
		$query  = $db->getQuery(true)
			->select('id')
			->from($db->quoteName('#__users', 'u'))
			->leftJoin($db->quoteName('#__user_usergroup_map', 'm') . ' ON m.user_id = u.id')
			->where($db->quoteName('m.group_id') . ' IN (' . implode(',', $groups) . ')')
			->group(array('u.id'));
		$userId = $db->setQuery($query)->loadResult();
		Factory::getUser()->load($userId);
		Factory::getUser()->set('isRoot', true);

		// Print a blank line
		$this->out(Text::_('COM_JATOMS') . ': ' . Text::_('COM_JATOMS_SYNCHRONIZATION'));
		$this->out('============================');

		// Get model
		JLoader::register('jAtomSHelper', JPATH_COMPONENT . '/helpers/jatoms.php');
		JLoader::register('jAtomSHelperApi', JPATH_COMPONENT . '/helpers/api.php');
		JLoader::register('jAtomSHelperPrice', JPATH_COMPONENT . '/helpers/price.php');
		Table::addIncludePath(JPATH_COMPONENT . '/tables');
		BaseDatabaseModel::addIncludePath(JPATH_COMPONENT . '/models');
		/* @var jAtomSModelSynchronization $model */
		if (!$model = BaseDatabaseModel::getInstance('Synchronization', 'jAtomSModel', array('ignore_request' => true)))
		{
			$this->out(Text::_('COM_JATOMS_ERROR_MODEL_NOT_FOUND'));

			return;
		}

		// Set show status
		$status           = $this->input->getString('status', 'show');
		$this->showStatus = ($status !== 'hide' && $status !== 'false');

		// Start synchronization
		$this->outStatus('COM_JATOMS_SYNCHRONIZATION_STEP_1', 'progress');
		if (!$filename = $model->startSynchronization())
		{
			$this->updateStatus('error');
			$this->out();
			foreach ($model->getErrors() as $error)
			{
				$this->out(($error instanceof Exception) ? $error->getMessage() : $error);
			}

			return;
		}
		$this->updateStatus('success');
		$this->out();

		// Get tours list
		$this->outStatus('COM_JATOMS_SYNCHRONIZATION_STEP_2', 'progress');
		if (!$result = $model->getToursList($filename, array()))
		{
			$this->updateStatus('error');
			$this->out();
			foreach ($model->getErrors() as $error)
			{
				$this->out(($error instanceof Exception) ? $error->getMessage() : $error);
			}

			return;
		}
		$this->updateStatus('success');
		$this->out();

		// Synchronize tours
		$errors = array();
		$total  = $result['total'];
		$this->outStatus('COM_JATOMS_SYNCHRONIZATION_STEP_3', '[0/' . $total . ']');
		for ($i = 0; $i < $total; $i++)
		{
			if (!$model->synchronizeTour($filename, $i))
			{
				foreach ($model->getErrors() as $error)
				{
					$errors[] = $this->out(($error instanceof Exception) ? $error->getMessage() : $error);
				}
			}
			$this->updateStatus('[' . ($i + 1) . '/' . $total . ']');
		}
		$this->updateStatus('success');
		$this->out();

		// Cleanup tours
		$this->outStatus('COM_JATOMS_SYNCHRONIZATION_STEP_4', 'progress');
		if (!$model->cleanupTours($filename))
		{
			$this->updateStatus('error');
			$this->out();
			foreach ($model->getErrors() as $error)
			{
				$this->out(($error instanceof Exception) ? $error->getMessage() : $error);
			}

			return;
		}
		$this->updateStatus('success');
		$this->out();

		// Finish synchronization
		$this->outStatus('COM_JATOMS_SYNCHRONIZATION_STEP_5', 'progress');
		if (!$result = $model->finishSynchronization($filename))
		{
			$this->updateStatus('error');
			$this->out();
			foreach ($model->getErrors() as $error)
			{
				$this->out(($error instanceof Exception) ? $error->getMessage() : $error);
			}

			return;
		}
		$this->updateStatus('success');
		$this->out();

		// Show result
		$this->out('_________');
		$this->out(Text::_('COM_JATOMS_SYNCHRONIZATION_MESSAGE_COMPLETE'));
		if ($result->success)
		{
			$this->out(Text::sprintf('COM_JATOMS_SYNCHRONIZATION_MESSAGE_SUCCESS', count($result->success)));
		}
		if ($result->archive)
		{
			$this->out(Text::sprintf('COM_JATOMS_SYNCHRONIZATION_MESSAGE_ARCHIVE', count($result->archive)));
		}
		if ($result->error)
		{
			$this->out(Text::sprintf('COM_JATOMS_SYNCHRONIZATION_MESSAGE_ERROR', count($result->error),
				implode(',', $result->error)));
		}
		if ($result->error_archive)
		{
			$this->out(Text::sprintf('COM_JATOMS_SYNCHRONIZATION_MESSAGE_ERROR_ARCHIVE',
				count($result->error_archive), implode(',', $result->error_archive)));
		}
		$this->out();
	}

	/**
	 * Method to output step status.
	 *
	 * @param   string  $constant  Step language constant.
	 * @param   string  $status    Status text.
	 *
	 * @since  2.0.0
	 */
	protected function outStatus($constant = '', $status = '')
	{
		if ($this->showStatus)
		{
			$this->out($this->getStepText($constant) . $this->getStatusText($status), false);
		}
		else
		{
			$this->out(' - ' . $this->getStepText($constant), false);
		}
	}

	/**
	 * Method to update step status text.
	 *
	 * @param   string  $status  Status text.
	 *
	 * @since  2.0.0
	 */
	protected function updateStatus($status = '')
	{
		if ($this->showStatus)
		{
			echo "\033[20D";
			echo str_pad($this->getStatusText($status), 20, ' ', STR_PAD_LEFT);
		}
	}

	/**
	 * Method to get step text with separators.
	 *
	 * @param   string  $constant  Step language constant.
	 *
	 * @return  string Step text with separator
	 *
	 * @since  2.0.0
	 */
	protected function getStepText($constant = '')
	{
		if ($this->_stepTextMaxSize == 0)
		{
			for ($i = 1; $i <= 5; $i++)
			{
				$size = strlen(Text::_('COM_JATOMS_SYNCHRONIZATION_STEP_' . $i));
				if ($size > $this->_stepTextMaxSize)
				{
					$this->_stepTextMaxSize = $size;
				}
			}
		}

		$text = Text::_($constant);
		$size = strlen($text);
		if ($this->showStatus && $size < $this->_stepTextMaxSize)
		{
			$text .= str_repeat($this->separator, $this->_stepTextMaxSize - $size);
		}

		return $text;
	}

	/**
	 * Method to get status text with separators.
	 *
	 * @param   string  $status  Status text.
	 *
	 * @return  string Status text with separator
	 *
	 * @since  2.0.0
	 */
	protected function getStatusText($status)
	{
		if ($this->showStatus)
		{
			$size = strlen($status);
			if ($size < 20)
			{
				$status = str_repeat($this->separator, 20 - $size) . $status;
			}
		}

		return $status;
	}

}

// Instantiate the application object.
CliApplication::getInstance('jAtomsSynchronizationCli')->execute();