<?php
/*
 * @package     RadicalMart Package
 * @subpackage  com_radicalmart
 * @version     __DEPLOY_VERSION__
 * @author      Delo Design - delo-design.ru
 * @copyright   Copyright (c) 2021 Delo Design. All rights reserved.
 * @license     GNU/GPL license: https://www.gnu.org/copyleft/gpl.html
 * @link        https://delo-design.ru/
 */

/**
 * RadicalMart  CLI.
 *
 * This is a command-line script for RadicalMart component.
 *
 * Called with no arguments: php radicalmart.php
 *                           Performs task with the display of the dynamic status of each step.
 *
 * Called with --status=hide   `php radicalmart.php --status=hide`
 *          or --status=false   or `php radicalmart.php --status=false`
 *                              Performs cli with displaying only step titles.
 *
 *  Called with --task=X   `php radicalmart.php --task=X`
 *                              Performs task by name.
 *
 */

// 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_ROOT . '/components/com_radicalmart');
define('JPATH_COMPONENT_ADMINISTRATOR', JPATH_ADMINISTRATOR . '/components/com_radicalmart');


// 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\AdministratorApplication;
use Joomla\CMS\Application\CliApplication;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Filesystem\Folder;
use Joomla\CMS\Filesystem\Path;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Table\Table;
use Joomla\CMS\User\User;
use Joomla\Registry\Registry;
use Joomla\Utilities\ArrayHelper;
use Joomla\CMS\Helper\MediaHelper;

class RadicalMartCli extends CliApplication
{
	/**
	 * Minimum PHP version required to install the extension.
	 *
	 * @var  string
	 *
	 * @since  __DEPLOY_VERSION__
	 */
	protected $minimumPhp = '7.0';

	/**
	 * Steps string max length.
	 *
	 * @var  int.
	 *
	 * @since  __DEPLOY_VERSION__
	 */
	protected $_stepTextMaxSize = 40;

	/**
	 * Show status or show only step title.
	 *
	 * @var  boolean.
	 *
	 * @since  __DEPLOY_VERSION__
	 */
	protected $showStatus = true;

	/**
	 * Line space separator.
	 *
	 * @var  string.
	 *
	 * @since  __DEPLOY_VERSION__
	 */
	protected $separator = '.';

	/**
	 * Application object.
	 *
	 * @var  AdministratorApplication
	 *
	 * @since  __DEPLOY_VERSION__
	 */
	protected $app = null;

	/**
	 * User object.
	 *
	 * @var  User
	 *
	 * @since  __DEPLOY_VERSION__
	 */
	protected $user = null;

	/**
	 * Component params.
	 *
	 * @var  Registry
	 *
	 * @since  __DEPLOY_VERSION__
	 */
	protected $component = null;

	/**
	 * Entry point for DachaDacha CLI script.
	 *
	 * @throws  Exception
	 *
	 * @since  __DEPLOY_VERSION__
	 */
	public function doExecute()
	{
		// Load language
		Factory::getLanguage()->load('com_radicalmart', JPATH_ADMINISTRATOR, 'en-GB');

		// Check PHP version
		if (!(version_compare(PHP_VERSION, $this->minimumPhp) >= 0))
		{
			$this->out(Text::sprintf('COM_RADICALMART_CLI_ERROR_PHP_COMPATIBLE'));

			return;
		}

		// Remove the script time limit
		@set_time_limit(0);

		// Set show status
		$status           = $this->input->getString('status', 'show');
		$this->showStatus = ($status !== 'hide' && $status !== 'false');

		$task = $this->input->getString('task', '');
		if (empty($task) || !method_exists($this, $task))
		{
			$this->out(Text::sprintf('COM_RADICALMART_CLI_ERROR_TASK_NOT_FOUND'));

			return;
		}

		// Check method is public
		$reflection = new ReflectionMethod($this, $task);
		if (!$reflection->isPublic())
		{
			$this->out(Text::sprintf('COM_RADICALMART_CLI_ERROR_TASK_NOT_FOUND'));

			return;
		}

		// Print a blank line
		$this->out(Text::sprintf('COM_RADICALMART_CLI', $task));
		$this->out('============================');
		$this->$task();

		$this->out();
	}

	/**
	 * Method to create test products.
	 *
	 * @since  __DEPLOY_VERSION__
	 */
	public function createTestData()
	{
		try
		{
			// Fool the system into thinking we are running as control panel
			$_SERVER['HTTP_HOST'] = 'domain.com';
			Factory::getApplication('administrator');
			$this->initialiseSuperUser();

			ini_set('memory_limit', '256M');

			// Prepare images
			$images = array();
			$this->outStatus('Prepare images', 'progress');
			$folder      = Path::clean(JPATH_ROOT . '/images');
			$mediaHelper = new MediaHelper();
			if (Folder::exists($folder))
			{
				$files = Folder::files($folder, null, true, true);
				$total = count($files);
				foreach ($files as $f => $file)
				{
					$this->updateStatus('[' . $f . '/' . $total . ']');
					if ($mediaHelper->isImage($file))
					{
						$images[] = str_replace(Path::clean(JPATH_ROOT . '/'), '', $file);
					}
				}
			}
			$imagesTotal = count($images);
			$imagesCount = ($imagesTotal > 5) ? 5 : $imagesTotal;
			$this->updateStatus('success');
			$this->out();

			// Prepare categories
			$mainCategory        = array();
			$additionsCategories = array();
			$this->outStatus('Prepare categories', 'progress');

			BaseDatabaseModel::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_radicalmart/models/');
			Table::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_radicalmart/tables/');
			$firstLevel = false;
			$db         = Factory::getDbo();
			$c          = 0;
			foreach (
				array(
					'test-first-level'  => array(
						'title'     => 'First Level',
						'alias'     => 'test-first-level',
						'parent_id' => 1,
						'language'  => '*',
						'state'     => 1,
						'show'      => 1,
						'media'     => array(
							'image' => ($images) ? $images[rand(0, count($images) - 1)] : null,
							'icon'  => ($images) ? $images[rand(0, count($images) - 1)] : null
						),
					),
					'test-second-level' => array(
						'title'     => 'Second Level',
						'alias'     => 'test-second-level',
						'parent_id' => 'firstLevel',
						'language'  => '*',
						'state'     => 1,
						'show'      => 1,
						'media'     => array(
							'image' => ($images) ? $images[rand(0, count($images) - 1)] : null,
							'icon'  => ($images) ? $images[rand(0, count($images) - 1)] : null
						),
					),
					'test-hit'          => array(
						'title'     => 'Hit',
						'alias'     => 'test-hit',
						'parent_id' => 1,
						'language'  => '*',
						'state'     => 1,
						'show'      => 1,
						'media'     => array(
							'image' => ($images) ? $images[rand(0, count($images) - 1)] : null,
							'icon'  => ($images) ? $images[rand(0, count($images) - 1)] : null
						),

					),
					'test-new'          => array(
						'title'     => 'New',
						'alias'     => 'test-new',
						'parent_id' => 1,
						'language'  => '*',
						'state'     => 1,
						'show'      => 1,
						'media'     => array(
							'image' => ($images) ? $images[rand(0, count($images) - 1)] : null,
							'icon'  => ($images) ? $images[rand(0, count($images) - 1)] : null
						),

					),
					'test-promotion'    => array(
						'title'     => 'Promotion',
						'alias'     => 'test-promotion',
						'parent_id' => 1,
						'language'  => '*',
						'state'     => 1,
						'show'      => 1,
						'media'     => array(
							'image' => ($images) ? $images[rand(0, count($images) - 1)] : null,
							'icon'  => ($images) ? $images[rand(0, count($images) - 1)] : null
						),

					),
				) as $key => $data
			)
			{
				$this->updateStatus('[' . $c . '/' . 5 . ']');

				$query = $db->getQuery(true)
					->select('id')
					->from($db->quoteName('#__radicalmart_categories'))
					->where($db->quoteName('alias') . ' = ' . $db->quote($key));
				if (!$id = $db->setQuery($query)->loadResult())
				{
					if ($data['parent_id'] === 'firstLevel') $data['parent_id'] = $firstLevel;
					/* @var RadicalMartModelCategory $model */
					$model = BaseDatabaseModel::getInstance('Category', 'RadicalMartModel');
					$id    = $model->save($data);
				}

				if ($key === 'test-first-level') $firstLevel = $id;
				elseif ($key === 'test-second-level') $mainCategory = $id;
				else $additionsCategories[] = $id;

				$c++;
			}
			$this->updateStatus('success');
			$this->out();

			// Prepare introtext
			$this->outStatus('Prepare introtext', 'progress');
			$introtext = '<p>';
			for ($i = 1; $i <= 10; $i++)
			{
				$introtext .= 'Test Product {index} introtext. ';
			}
			$introtext .= '</p>';
			$this->updateStatus('success');
			$this->out();

			// Prepare fulltext
			$this->outStatus('Prepare fulltext', 'progress');
			$fulltext = '<p>';
			for ($i = 1; $i <= 10; $i++)
			{
				$fulltext .= 'Test Product {index} fulltext. ';
			}
			$fulltext .= '</p>';
			$fulltext = str_repeat($fulltext, 2);
			$this->updateStatus('success');
			$this->out();

			$this->outStatus('Create products', 'progress');
			JLoader::register('RadicalMartHelperPrice', JPATH_ADMINISTRATOR . '/components/com_radicalmart/helpers/price.php');
			$currency = RadicalMartHelperPrice::getDefaultCurrency();

			$total = $this->input->getInt('count', 500);
			$query = $db->getQuery(true)
				->clear()
				->select('COUNT(id)')
				->from($db->quoteName('#__radicalmart_products'));
			$index = (int) $db->setQuery($query)->loadResult();
			for ($p = 1; $p <= $total; $p++)
			{
				$this->updateStatus('[' . ($p - 1) . '/' . $total . ']');
				if ($p % 1000 == 0) sleep(1);
				$index++;

				$dataGallery = array();
				if (!empty($images))
				{
					while (count($dataGallery) <= $imagesCount)
					{
						$src = $images[rand(0, $imagesTotal - 1)];
						if (!in_array($src, $dataGallery)) $dataGallery[] = $src;
					}
				}

				$data = array(
					'id'                    => 0,
					'title'                 => 'TEST PRODUCT [' . $index . ']',
					'code'                  => 'test_' . $index,
					'alias'                 => 'test' . $index,
					'introtext'             => str_replace('{index}', $index, $introtext),
					'fulltext'              => str_replace('{index}', $index, $fulltext),
					'category'              => $mainCategory,
					'additional_categories' => array($additionsCategories[rand(0, 2)]),
					'language'              => '*',
					'state'                 => 1,
					'media'                 => array('gallery' => $dataGallery),
					'params'                => array(
						'seo_product_title'       => '',
						'seo_product_description' => '',
						'seo_product_image'       => '',
						'seo_product_robots'      => '',
						'seo_product_h1'          => '',
						'ordering'                => null,
					),
					'prices'                => array(
						$currency['group'] => array(
							'currency'        => $currency['code'],
							'purchase_enable' => 0,
							'purchase'        => null,
							'extra'           => null,
							'base'            => rand(1, 1000000),
							'discount_enable' => 0,
							'discount'        => null,
							'discount_end'    => null,
							'final'           => 0,
						)
					),
					'shipping'              => array(
						'enable' => 1,
					)
				);

				/* @var RadicalMartModelProduct $model */
				$model  = BaseDatabaseModel::getInstance('Product', 'RadicalMartModel');
				$result = $model->save($data);
				if ($result === false)
				{
					$message = array();
					foreach ($model->getErrors() as $error)
					{
						$message[] = ($error instanceof Exception) ? $error->getMessage() : $error;
					}
					throw new Exception(implode(PHP_EOL, $message));
				}
			}
			$this->updateStatus('success');
			$this->out();
		}
		catch (Exception $e)
		{
			$this->updateStatus('error');
			$this->out();
			$this->out($e->getMessage());
		}
	}

	/**
	 * Method to set categories filter.
	 *
	 * @since  __DEPLOY_VERSION__
	 */
	public function setCategoriesFilters()
	{
		try
		{
			// Get Categories types
			$this->outStatus('Get Categories types', 'progress');
			$db         = Factory::getDbo();
			$query      = $db->getQuery(true)
				->from($db->quoteName('#__radicalmart_categories'))
				->where($db->quoteName('alias') . '<>' . $db->quote('root'))
				->select(array('id', 'JSON_VALUE(params, ' . $db->quote('$.display_product') . ') as type'));
			$categories = $db->setQuery($query)->loadAssocList('id', 'type');
			$total      = count($categories);
			$c          = 0;

			foreach ($categories as $cid => $type)
			{
				$this->updateStatus('[' . $c . '/' . $total . ']');
				$c++;
				if (empty($type)) $categories[$cid] = 'categories';

			}
			$this->updateStatus('success');
			$this->out();

			// Set categories
			JLoader::register('RadicalMartHelperSQL', JPATH_ADMINISTRATOR . '/components/com_radicalmart/helpers/sql.php');
			$this->outStatus('Set categories', 'progress');
			$s = 0;
			foreach ($categories as $current_id => $current_type)
			{
				$current_id = (int) $current_id;

				$this->updateStatus('[' . $s . '/' . $total . ']');
				$s++;
				if ($current_type === 'hidden') continue;

				$filter_categories   = array();
				$filter_manufactures = array();
				$filter_badges       = array();

				// Get all products categories
				$query = $db->getQuery(true)
					->select('p.categories')
					->from($db->quoteName('#__radicalmart_products', 'p'));

				$sql = array('FIND_IN_SET(' . $current_id . ', p.categories)');
				foreach (RadicalMartHelperSQL::getSubCategories($current_id) as $catid)
				{
					$sql[] = 'FIND_IN_SET(' . $catid . ', p.categories)';
				}
				$query->where('(' . implode(' OR ', $sql) . ')');

				$productsCategories = $db->setQuery($query)->loadColumn();
				$productsCategories = explode(',', implode(',', $productsCategories));
				$productsCategories = array_values(ArrayHelper::toInteger(array_unique($productsCategories)));

				// Set new params
				foreach ($productsCategories as $pc)
				{
					if (!isset($categories[$pc])) continue;
					$productsCategory = $categories[$pc];

					if ($pc == $current_id) continue;
					elseif ($productsCategory === 'categories') $filter_categories[] = $pc;
					elseif ($productsCategory === 'manufacturers') $filter_manufactures[] = $pc;
					elseif ($productsCategory === 'badges') $filter_badges[] = $pc;
				}

				// Clear data by current type
				if ($current_type === 'categories') $filter_categories = array();
				elseif ($current_type === 'manufacturers') $filter_manufactures = array();
				elseif ($current_type === 'badges') $filter_badges = array();

				// Get old params
				$query          = $db->getQuery(true)
					->select(array('id', 'params'))
					->from($db->quoteName('#__radicalmart_categories'))
					->where('id =' . (int) $current_id);
				$update         = $db->setQuery($query, 0, 1)->loadObject();
				$update->params = new Registry($update->params);
				$update->params->set('filter_categories', $filter_categories);
				$update->params->set('filter_manufactures', $filter_manufactures);
				$update->params->set('filter_badges', $filter_badges);
				$update->params = $update->params->toString('json');
				$db->updateObject('#__radicalmart_categories', $update, 'id');
			}

			$this->updateStatus('success');
			$this->out();
		}
		catch (Exception $e)
		{
			$this->updateStatus('error');
			$this->out();
			$this->out($e->getMessage());
		}
	}

	/**
	 * Method to initialise SuperAdmin user.
	 *
	 * @throws Exception
	 *
	 * @since  __DEPLOY_VERSION__
	 */
	protected function initialiseSuperUser()
	{
		if ($this->user === null)
		{
			// 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);

			$this->user = Factory::getUser();
		}
	}

	/**
	 * Method to initialise component.
	 *
	 * @throws Exception
	 *
	 * @since  __DEPLOY_VERSION__
	 */
	protected function initialiseComponent()
	{
		if ($this->component === null)
		{
			$this->initialiseApplication();
			$this->initialiseSuperUser();

			PluginHelper::importPlugin('system');
			PluginHelper::importPlugin('content');
			PluginHelper::importPlugin('radicalmart');

			// Load component
			JLoader::register('RadicalMartHelperIntegration', JPATH_ADMINISTRATOR
				. '/components/com_radicalmart/helpers/integration.php');
			RadicalMartHelperIntegration::initializeAdmin();

			// Load models
			BaseDatabaseModel::addIncludePath(RadicalMartHelperIntegration::$adminPath . '/models/',
				'RadicalMartModel');
			Table::addIncludePath(RadicalMartHelperIntegration::$adminPath . '/tables/');
			Form::addFormPath(RadicalMartHelperIntegration::$adminPath . '/models/forms');
			Form::addFieldPath(RadicalMartHelperIntegration::$adminPath . '/models/fields');
			Form::addRulePath(RadicalMartHelperIntegration::$adminPath . '/models/rules');

			$this->component = ComponentHelper::getParams('com_radicalmart');
		}
	}

	/**
	 * Method to initialise application.
	 *
	 * @throws Exception
	 *
	 * @since  __DEPLOY_VERSION__
	 */
	protected function initialiseApplication()
	{
		if ($this->app === null)
		{
			$_SERVER['HTTP_HOST'] = 'domain.com';
			$this->app            = Factory::getApplication('administrator');
			Factory::getConfig()->set('debug', 0);
			PluginHelper::importPlugin('system');
		}
	}

	/**
	 * Method to output step status.
	 *
	 * @param   string  $constant  Step language constant.
	 * @param   string  $status    Status text.
	 *
	 * @since  __DEPLOY_VERSION__
	 */
	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  __DEPLOY_VERSION__
	 */
	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  __DEPLOY_VERSION__
	 */
	protected function getStepText($constant = '')
	{
		$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  __DEPLOY_VERSION__
	 */
	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('RadicalMartCli')->execute();