<?php
// Copyright 1999-2017. Plesk International GmbH. All rights reserved.

namespace Plesk\MigrationLibV8\SiteImport\Backend\Files;

use Plesk\MigrationLibV8\Utils\ArrayUtils;
use Plesk\MigrationLibV8\Utils\FileUtils;

class SiteInfo
{
    const KEY_APPLICATIONS = 'applications';
    const KEY_FILES = 'files';
    const KEY_ROOT_PATH = 'root_path';
    const KEY_FILESYSTEM_ROOT_PATH = 'filesystem_root_path';
    const KEY_DOCUMENT_ROOT = 'document_root';
    const KEY_SITE_NAME = 'site_name';
    const KEY_TARGET_DOCUMENT_ROOT = 'target-document-root';

    private $_siteName;
    private $_siteInfoFilePath;
    private $_data;

    public function __construct($siteName, $siteInfoFilePath)
    {
        $this->_siteName = $siteName;
        $this->_siteInfoFilePath = $siteInfoFilePath;
        $this->_data = [];

        if (FileUtils::fileExists($this->_siteInfoFilePath)) {
            $allSitesData = json_decode(
                FileUtils::fileGetContents($this->_siteInfoFilePath), true
            );
            if (!is_null($allSitesData) && array_key_exists($this->_siteName, $allSitesData)) {
                $this->_data = $allSitesData[$this->_siteName];
            }
        }
    }

    public function getApplications()
    {
        $applications = [];

        $siteInfoApplications = ArrayUtils::getElementDefaultArray(
            $this->_data, self::KEY_APPLICATIONS
        );

        foreach ($siteInfoApplications as $applicationName => $paths) {
            foreach ($paths as $path) {
                $applications[] = [
                    'type' => $applicationName,
                    'source_path' => $path,
                    // 'target_path' contains path relative to target document root path
                    // Site info storage doesn't contain target path, consider it is the same as on source
                    'target_path' => $this->getSourceRelativePath($path)
                ];
            }
        }

        return $applications;
    }

    public function getFiles()
    {
        return ArrayUtils::getElementDefaultArray(
            $this->_data, self::KEY_FILES
        );
    }

    public function getRootPath()
    {
        return ArrayUtils::getElementDefaultNull($this->_data, self::KEY_ROOT_PATH);
    }

    public function getFilesystemRootPath()
    {
        $filesystemRootPath = ArrayUtils::getElementDefaultNull($this->_data, self::KEY_FILESYSTEM_ROOT_PATH);
        if ($filesystemRootPath === null) {
            // for compatibility with old versions of migrator
            $filesystemRootPath = $this->getRootPath();
        }
        return $filesystemRootPath;
    }

    public function getSourceDocumentRoot()
    {
        return ArrayUtils::getElementDefaultNull($this->_data, self::KEY_DOCUMENT_ROOT);
    }

    public function getTargetDocumentRoot()
    {
        return ArrayUtils::getElementDefaultNull($this->_data, self::KEY_TARGET_DOCUMENT_ROOT);
    }

    public function getSiteName()
    {
        return ArrayUtils::getElementDefaultNull($this->_data, self::KEY_SITE_NAME);
    }

    /**
     * Whether site info file contains complete set of data, so migration could be started.
     *
     * @return bool
     */
    public function isComplete()
    {
        foreach ($this->_getAllRequiredKeys() as $key) {
            if (!array_key_exists($key, $this->_data)) {
                return false;
            }
        }

        return true;
    }

    /**
     * Returns path relative to target document root
     *
     * Example:
     *  - path - /var/www/vhosts/site.com/httpdocs/subfolder
     *  - target document root - /var/www/vhosts/site.com/httpdocs
     *  - result subfolder
     *
     * @param string $path
     * @return string
     */
    public function getTargetRelativePath($path)
    {
        return $this->_getRelativePath($this->getTargetDocumentRoot(), $path);
    }

    /**
     * Returns path relative to source document root
     *
     * Example:
     *  - path - /var/www/vhosts/site.com/httpdocs/subfolder
     *  - source document root - /var/www/vhosts/site.com/httpdocs
     *  - result subfolder
     *
     * @param string $path
     * @return string
     */
    public function getSourceRelativePath($path)
    {
        if ($this->_isWindowsAbsolutePath($path)) {
            $fullSourceDocrootPath = (
                trim($this->getFilesystemRootPath(), '\\') . '\\' . trim($this->getSourceDocumentRoot(), '/') . '\\'
            );
        } else {
            $rootPathTrimmed = trim($this->getFilesystemRootPath(), '/');
            if ($rootPathTrimmed == '') {
                $rootPathNormalized = '/';
            } else {
                $rootPathNormalized = '/' . $rootPathTrimmed . '/' ;
            }
            $fullSourceDocrootPath = $rootPathNormalized . trim($this->getSourceDocumentRoot(), '/') . '/';
        }
        return $this->_getRelativePath($fullSourceDocrootPath, $path);
    }

    private function _getRelativePath($docrootPath, $path)
    {
        if ($this->_isWindowsAbsolutePath($path)) {
            $docrootPath = rtrim($docrootPath, '\\')  . '\\';
            $path = trim($path, '\\') . '\\';
            if (strpos($path, $docrootPath) === 0) {
                return rtrim(
                    str_replace('\\', '/', substr($path, strlen($docrootPath))), '/'
                );
            }
        } else {
            $docrootPath = rtrim($docrootPath, '/')  . '/';
            $path = '/' . trim($path, '/') . '/';
            if (strpos($path, $docrootPath) === 0) {
                return rtrim(substr($path, strlen($docrootPath)), '/');
            }
        }
        return '';
    }

    /**
     * Whether provided path looks like a Windows path.
     *
     * @param string $path
     * @return bool
     */
    private function _isWindowsAbsolutePath($path)
    {
        // Path starts with something like "C:\"
        return (bool)preg_match('/^[a-zA-Z]:\\\\/', $path);
    }

    /**
     * Data keys in site info file which are required to start migration.
     *
     * @return array
     */
    private function _getAllRequiredKeys()
    {
        return [
            self::KEY_SITE_NAME,
            self::KEY_ROOT_PATH,
            self::KEY_DOCUMENT_ROOT,
            self::KEY_APPLICATIONS,
            self::KEY_FILES,
        ];
    }
}
