<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

namespace Magento\Framework\App;

use Magento\Framework\Setup\BackendFrontnameGenerator;

/**
 * A model for determining information about setup application
 */
class SetupInfo
{
    /**#@+
     * Initialization parameters for redirecting if the application is not installed
     */
    const PARAM_NOT_INSTALLED_URL_PATH = 'MAGE_NOT_INSTALLED_URL_PATH';
    const PARAM_NOT_INSTALLED_URL = 'MAGE_NOT_INSTALLED_URL';
    /**#@-*/

    /**
     * Default path relative to the project root
     */
    const DEFAULT_PATH = 'setup';

    /**
     * Environment variables
     *
     * @var array
     */
    private $server;

    /**
     * Current document root directory
     *
     * @var string
     */
    private $docRoot;

    /**
     * Project root directory
     *
     * @var string
     */
    private $projectRoot;

    /**
     * Constructor
     *
     * @param array $server
     * @param string $projectRoot
     * @throws \InvalidArgumentException
     */
    public function __construct($server, $projectRoot = '')
    {
        $this->server = $server;
        if (empty($server['DOCUMENT_ROOT'])) {
            throw new \InvalidArgumentException('DOCUMENT_ROOT variable is unavailable.');
        }
        $this->docRoot = rtrim(str_replace('\\', '/', $server['DOCUMENT_ROOT']), '/');
        $this->projectRoot = $projectRoot ?: $this->detectProjectRoot();
        $this->projectRoot = str_replace('\\', '/', $this->projectRoot);
    }

    /**
     * Automatically detects project root from current environment
     *
     * Assumptions:
     * if the current setup application relative path is at the end of script path, then it is setup application
     * otherwise it is the "main" application
     *
     * @return mixed
     * @throws \InvalidArgumentException
     */
    private function detectProjectRoot()
    {
        if (empty($this->server['SCRIPT_FILENAME'])) {
            throw new \InvalidArgumentException('Project root cannot be automatically detected.');
        }
        $haystack = str_replace('\\', '/', dirname($this->server['SCRIPT_FILENAME']));
        $needle = '/' . $this->getPath();
        $isSetupApp = preg_match('/^(.+?)' . preg_quote($needle, '/') . '$/', $haystack, $matches);
        if ($isSetupApp) {
            return $matches[1];
        }
        return $haystack;
    }

    /**
     * Gets setup application URL
     *
     * @return string
     */
    public function getUrl()
    {
        if (isset($this->server[self::PARAM_NOT_INSTALLED_URL])) {
            return $this->server[self::PARAM_NOT_INSTALLED_URL];
        }
        return Request\Http::getDistroBaseUrlPath($this->server) . $this->getPath() . '/';
    }

    /**
     * Gets the "main" application URL
     *
     * @return string
     */
    public function getProjectUrl()
    {
        $isProjectInDocRoot = false !== strpos($this->projectRoot . '/', $this->docRoot . '/');
        if (empty($this->server['HTTP_HOST'])) {
            return '';
        } elseif (!$isProjectInDocRoot) {
            return 'http://' . $this->server['HTTP_HOST'] . '/';
        }
        return 'http://' . $this->server['HTTP_HOST'] . substr($this->projectRoot . '/', strlen($this->docRoot));
    }

    /**
     * Get the admin area path
     *
     * @return string
     */
    public function getProjectAdminPath()
    {
        return BackendFrontnameGenerator::generate();
    }

    /**
     * Gets setup application directory path in the filesystem
     *
     * @param string $projectRoot
     * @return string
     */
    public function getDir($projectRoot)
    {
        return rtrim($projectRoot, '/') . '/' . $this->getPath();
    }

    /**
     * Checks if the setup application is available in current document root
     *
     * @return bool
     */
    public function isAvailable()
    {
        $setupDir = $this->getDir($this->projectRoot);
        $isSubDir = false !== strpos($setupDir . '/', $this->docRoot . '/');
        // Setup is not accessible from pub folder
        $setupDir = rtrim($setupDir, '/');
        $lastOccurrence = strrpos($setupDir, '/pub/setup');

        if (false !== $lastOccurrence) {
            $setupDir = substr_replace($setupDir, '/setup', $lastOccurrence, strlen('/pub/setup'));
        }

        return $isSubDir && realpath($setupDir);
    }

    /**
     * Gets relative path to setup application
     *
     * @return string
     */
    private function getPath()
    {
        if (isset($this->server[self::PARAM_NOT_INSTALLED_URL_PATH])) {
            return trim($this->server[self::PARAM_NOT_INSTALLED_URL_PATH], '/');
        }
        return self::DEFAULT_PATH;
    }
}
