<?php
// Copyright (c) Pickware GmbH. All rights reserved.
// This file is part of software that is released under a proprietary license.
// You must not copy, modify, distribute, make publicly available, or execute
// its contents or parts thereof without express permission by the copyright
// holder, unless otherwise permitted by law.

namespace Shopware\Plugins\ViisonCommon\Components;

use Shopware\Plugins\ViisonCommon\Classes\Util\Util;
use Shopware\Plugins\ViisonCommon\Structs;

/**
 * Implements a simple plugin compatibility check:
 * Plugins implementing the getPluginCompatibilityConstraints method in their
 * bootstrap class can define compatibility constraints, which will first be
 * collected using the custom event "ViisonCommon_CompatibilityCheck". These
 * constraints will then be checked and unsolved constraints will be added to
 * a list of compatibility issues. This list will be requested when the
 * backend is loaded and displayed to the user as growl messages.
 */
class ViisonCommonCompatibilityCheck extends \Enlight_Class
{
    /**
     * @var Shopware\Plugins\ViisonCommon\Structs\Collection
     */
    private $compatibilityIssues = null;

    /**
     * Checks wether a given compatibility constraint is solved. if not
     * it will be added to a list of compatibility issues.
     *
     * @param  string  $plugin  Internal name of the plugin, which defined the constraint
     * @param  Structs\PluginCompatibilityConstraint $pluginConstraint  Constraint to be checked
     */
    public function checkConstraint($plugin, Structs\PluginCompatibilityConstraint $pluginConstraint)
    {
        $versionOfCheckedPlugin = $this->getVersionIfPluginIsInstalledAndActive($pluginConstraint);
        /*
         * Performs constraint check. If the constraint has a custom compatibility check, only this check is executed.
         * Otherwise plugin names an versions are compared.
         */
        if (!$pluginConstraint->passesCustomCheck()
            || ($versionOfCheckedPlugin !== null && version_compare($versionOfCheckedPlugin, $pluginConstraint->getMinimumVersion()) == -1)
            || ($versionOfCheckedPlugin === null && $pluginConstraint->isPluginRequired())
        ) {
            $this->compatibilityIssues[] = new Structs\PluginCompatibilityIssue(
                $plugin,
                $pluginConstraint
            );
        }
    }

    /**
     * Returns the current list of compatibility issues.
     *
     * @return Shopware\Plugins\ViisonCommon\Structs\Collection
     */
    public function getCompatibilityIssues()
    {
        // Lazy load compatibility issues
        if ($this->compatibilityIssues === null) {
            $this->compatibilityIssues = new Structs\Collection();
            // Collect plugin compatibility issues
            Shopware()->Events()->notify('ViisonCommon_CheckCompatibilityConstraints');
        }

        return $this->compatibilityIssues;
    }

    /**
     * Performs the compatibility check and returns a message for each issue
     * containing a description of what the user should do to transform his
     * system into a consistent state. Upon request the messages will be returned
     * as one concatenated string.
     *
     * @param  boolean $concatenateMessages  if true the messages will concatenated
     *                        into one string using the $glue parameter
     * @param  string  $glue  string, which will automatically be inserted between
     *                        two consecutive messages, if the concatenate messages
     *                        flag is set
     * @return null|string    Null if no compatibility issues have been identified,
     *                        the message as a string otherwise.
     */
    public function getCompatibilityIssuesAsMessage($concatenateMessages = true, $glue = '<br/><br/>')
    {
        /** @var \Shopware\Plugins\ViisonCommon\Structs\PluginCompatibilityIssue[] $compatibilityIssues */
        $compatibilityIssues = $this->getCompatibilityIssues();
        if (count($compatibilityIssues) === 0) {
            return null;
        }

        $growlMessages = [];
        foreach ($compatibilityIssues as $compatibilityIssue) {
            $growlMessages[] = $compatibilityIssue->getGrowlMessageInformation();
        }

        // If messages should be concatenated to a single growlmessage, we cannot use button for additional information.
        // Only a single growl message information is sent with message text and no link.
        if ($concatenateMessages) {
            $messages = array_map(
                function ($growlInformation) {
                    return $growlInformation['message'];
                },
                $growlMessages
            );

            return [
                [
                    'message' => implode($glue, $messages),
                ],
            ];
        }

        return $growlMessages;
    }

    /**
     * Checks whether the plugin defined in the given constrain is installed and active and, if it is, returns its
     * version. Otherwise null is returned.
     *
     * @param Structs\PluginCompatibilityConstraint $pluginConstraint
     * @return string|null plugin version or null if plugin is not installed or not active
     */
    public function getVersionIfPluginIsInstalledAndActive(Structs\PluginCompatibilityConstraint $pluginConstraint)
    {
        $pluginName = $pluginConstraint->getPlugin();
        $plugin = Util::getPlugin($pluginName);

        return ($plugin !== null && $plugin->getActive()) ? $plugin->getVersion() : null;
    }
}
