<?php

/**
 * @package     Joomla.Administrator
 * @subpackage  com_joomlaupdate
 *
 * @copyright   (C) 2012 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

namespace Joomla\Component\Joomlaupdate\Administrator\View\Joomlaupdate;

use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Toolbar\ToolbarHelper;
use Joomla\CMS\Version;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Joomla! Update's Default View
 *
 * @since  2.5.4
 */
class HtmlView extends BaseHtmlView
{
    /**
     * An array with the Joomla! update information.
     *
     * @var    array
     *
     * @since  3.6.0
     */
    protected $updateInfo = null;

    /**
     * PHP options.
     *
     * @var   array  Array of PHP config options
     *
     * @since 3.10.0
     */
    protected $phpOptions = null;

    /**
     * PHP settings.
     *
     * @var   array  Array of PHP settings
     *
     * @since 3.10.0
     */
    protected $phpSettings = null;

    /**
     * Non Core Extensions.
     *
     * @var   array  Array of Non-Core-Extensions
     *
     * @since 3.10.0
     */
    protected $nonCoreExtensions = null;

    /**
     * The model state
     *
     * @var    \Joomla\CMS\Object\CMSObject
     *
     * @since  4.0.0
     */
    protected $state;

    /**
     * Flag if the update component itself has to be updated
     *
     * @var boolean  True when update is available otherwise false
     *
     * @since 4.0.0
     */
    protected $selfUpdateAvailable = false;

    /**
     * The default admin template for the major version of Joomla that should be used when
     * upgrading to the next major version of Joomla
     *
     * @var string
     *
     * @since 4.0.0
     */
    protected $defaultBackendTemplate = 'atum';

    /**
     * Flag if default backend template is being used
     *
     * @var boolean  True when default backend template is being used
     *
     * @since 4.0.0
     */
    protected $isDefaultBackendTemplate = false;

    /**
     * A special prefix used for the emptystate layout variable
     *
     * @var string  The prefix
     *
     * @since 4.0.0
     */
    protected $messagePrefix = '';

    /**
     * A special text used for the emptystate layout to explain why there is no download
     *
     * @var string  The message
     *
     * @since 4.4.0
     */
    protected $reasonNoDownload = '';

    /**
     * Details on failed PHP or DB version requirements to be shown in the emptystate layout when there is no download
     *
     * @var \stdClass  PHP and database requirements from the update manifest
     *
     * @since 4.4.2
     */
    protected $detailsNoDownload;

    /**
     * List of non core critical plugins
     *
     * @var    \stdClass[]
     * @since  4.0.0
     */
    protected $nonCoreCriticalPlugins = [];

    /**
     * Should I disable the confirmation checkbox for pre-update extension version checks?
     *
     * @var   boolean
     * @since 4.2.0
     */
    protected $noVersionCheck = false;

    /**
     * Should I disable the confirmation checkbox for taking a backup before updating?
     *
     * @var   boolean
     * @since 4.2.0
     */
    protected $noBackupCheck = false;

    /**
     * Renders the view
     *
     * @param   string  $tpl  Template name
     *
     * @return void
     *
     * @since  2.5.4
     */
    public function display($tpl = null)
    {
        $this->updateInfo          = $this->get('UpdateInformation');
        $this->selfUpdateAvailable = $this->get('CheckForSelfUpdate');

        // Get results of pre update check evaluations
        $model                          = $this->getModel();
        $this->phpOptions               = $this->get('PhpOptions');
        $this->phpSettings              = $this->get('PhpSettings');
        $this->nonCoreExtensions        = $this->get('NonCoreExtensions');
        $this->isDefaultBackendTemplate = (bool) $model->isTemplateActive($this->defaultBackendTemplate);
        $nextMajorVersion               = Version::MAJOR_VERSION + 1;

        // The critical plugins check is only available for major updates.
        if (version_compare($this->updateInfo['latest'], (string) $nextMajorVersion, '>=')) {
            $this->nonCoreCriticalPlugins = $this->get('NonCorePlugins');
        }

        // Set to true if a required PHP option is not ok
        $isCritical = false;

        foreach ($this->phpOptions as $option) {
            if (!$option->state) {
                $isCritical = true;
                break;
            }
        }

        $this->state = $this->get('State');

        $hasUpdate   = !empty($this->updateInfo['hasUpdate']);
        $hasDownload = isset($this->updateInfo['object']->downloadurl->_data);

        // Fresh update, show it
        if ($this->getLayout() == 'complete') {
            // Complete message, nothing to do here
        } elseif ($this->selfUpdateAvailable) {
            // There is an update for the updater itself. So we have to update it first
            $this->setLayout('selfupdate');
        } elseif (!$hasDownload || !$hasUpdate) {
            // Could be that we have a download file but no update, so we offer a re-install
            if ($hasDownload) {
                // We can reinstall if we have a URL but no update
                $this->setLayout('reinstall');
            } else {
                // No download available
                if ($hasUpdate) {
                    $this->messagePrefix     = '_NODOWNLOAD';
                    $this->reasonNoDownload  = 'COM_JOOMLAUPDATE_NODOWNLOAD_EMPTYSTATE_REASON';
                    $this->detailsNoDownload = $this->updateInfo['object']->get('otherUpdateInfo');
                }

                $this->setLayout('noupdate');
            }
        } elseif ($this->getLayout() != 'update' && ($isCritical || $this->shouldDisplayPreUpdateCheck())) {
            // Here we have now two options: preupdatecheck or update
            $this->setLayout('preupdatecheck');
        } else {
            $this->setLayout('update');
        }

        if (in_array($this->getLayout(), ['preupdatecheck', 'update', 'upload'])) {
            $language = $this->getLanguage();
            $language->load('com_installer', JPATH_ADMINISTRATOR, 'en-GB', false, true);
            $language->load('com_installer', JPATH_ADMINISTRATOR, null, true);

            Factory::getApplication()->enqueueMessage(Text::_('COM_JOOMLAUPDATE_VIEW_DEFAULT_UPDATE_NOTICE'), 'warning');
        }

        $params = ComponentHelper::getParams('com_joomlaupdate');

        switch ($params->get('updatesource', 'default')) {
            // "Minor & Patch Release for Current version AND Next Major Release".
            case 'next':
                $this->langKey         = 'COM_JOOMLAUPDATE_VIEW_DEFAULT_UPDATES_INFO_NEXT';
                $this->updateSourceKey = Text::_('COM_JOOMLAUPDATE_CONFIG_UPDATESOURCE_NEXT');
                break;

            // "Testing"
            case 'testing':
                $this->langKey         = 'COM_JOOMLAUPDATE_VIEW_DEFAULT_UPDATES_INFO_TESTING';
                $this->updateSourceKey = Text::_('COM_JOOMLAUPDATE_CONFIG_UPDATESOURCE_TESTING');
                break;

            // "Custom"
            case 'custom':
                $this->langKey         = 'COM_JOOMLAUPDATE_VIEW_DEFAULT_UPDATES_INFO_CUSTOM';
                $this->updateSourceKey = Text::_('COM_JOOMLAUPDATE_CONFIG_UPDATESOURCE_CUSTOM');
                break;

            /**
             * "Minor & Patch Release for Current version (recommended and default)".
             * The commented "case" below are for documenting where 'default' and legacy options falls
             * case 'default':
             * case 'sts':
             * case 'lts':
             * case 'nochange':
             */
            default:
                $this->langKey         = 'COM_JOOMLAUPDATE_VIEW_DEFAULT_UPDATES_INFO_DEFAULT';
                $this->updateSourceKey = Text::_('COM_JOOMLAUPDATE_CONFIG_UPDATESOURCE_DEFAULT');
        }

        $this->noVersionCheck = $params->get('versioncheck', 1) == 0;
        $this->noBackupCheck  = $params->get('backupcheck', 1) == 0;

        // Remove temporary files
        $this->getModel()->removePackageFiles();

        $this->addToolbar();

        // Render the view.
        parent::display($tpl);
    }

    /**
     * Add the page title and toolbar.
     *
     * @return  void
     *
     * @since   4.0.0
     */
    protected function addToolbar()
    {
        // Set the toolbar information.
        ToolbarHelper::title(Text::_('COM_JOOMLAUPDATE_OVERVIEW'), 'joomla install');

        if (in_array($this->getLayout(), ['update', 'complete'])) {
            $arrow = $this->getLanguage()->isRtl() ? 'arrow-right' : 'arrow-left';

            ToolbarHelper::link('index.php?option=com_joomlaupdate', 'JTOOLBAR_BACK', $arrow);

            ToolbarHelper::title(Text::_('COM_JOOMLAUPDATE_VIEW_DEFAULT_TAB_UPLOAD'), 'joomla install');
        } elseif (!$this->selfUpdateAvailable) {
            ToolbarHelper::custom('update.purge', 'loop', '', 'COM_JOOMLAUPDATE_TOOLBAR_CHECK', false);
        }

        // Add toolbar buttons.
        $currentUser = version_compare(JVERSION, '4.2.0', 'ge')
            ? $this->getCurrentUser()
            : Factory::getApplication()->getIdentity();

        if ($currentUser->authorise('core.admin')) {
            ToolbarHelper::preferences('com_joomlaupdate');
        }

        ToolbarHelper::divider();
        ToolbarHelper::help('Joomla_Update');
    }

    /**
     * Returns true, if the pre update check should be displayed.
     *
     * @return boolean
     *
     * @since 3.10.0
     */
    public function shouldDisplayPreUpdateCheck()
    {
        // When the download URL is not found there is no core upgrade path
        if (!isset($this->updateInfo['object']->downloadurl->_data)) {
            return false;
        }

        $nextMinor = Version::MAJOR_VERSION . '.' . (Version::MINOR_VERSION + 1);

        // Show only when we found a download URL, we have an update and when we update to the next minor or greater.
        return $this->updateInfo['hasUpdate']
            && version_compare($this->updateInfo['latest'], $nextMinor, '>=');
    }
}
