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

namespace Plesk\MigrationLibV8\SiteImport\Backend;

use Plesk\MigrationLibV8\Common\Backend\ToolUtils;
use Plesk\MigrationLibV8\Common\Exception\NonZeroExitCode;
use Plesk\MigrationLibV8\SiteImport\Backend\Files\Config;

/**
 * Wrapper around "plesk-migrator" utility (which is backend entry point) for site import.
 */
class Tool
{
    /**
     * Site migration: run get information about site on the source server by FTP/SSH.
     *
     * @param Config $config
     * @param bool $reloadSourceData whether to use cached data, or reload all data from source site again
     * @param bool $async whether to wait for execution results, or run the command in background
     */
    public function getSiteInfo(
        Config $config, $reloadSourceData=true, $async=false
    ) {
        $command = 'get-site-info';
        $args = [];
        if ($reloadSourceData) {
            $args[] = '--reload-source-data';
        }
        if ($async) {
            $args[] = '--async';
        }
        $this->_exec($command, $args, $config);
    }

    /**
     * Site migration: schedule site migration tasks.
     * Once the tasks are scheduled, start them with 'runSiteMigrationTasks' function.
     *
     * @param Config $config
     * @param string $migrationListFile full path to file with migration list
     */
    public function addSiteMigrationTasks(
        Config $config, $migrationListFile
    ) {
        $command = 'add-tasks';
        $args = [
            "--site-migration-list=$migrationListFile",
        ];
        $this->_exec($command, $args, $config);
    }

    /**
     * Site migration: cancel site migration tasks related to apps/folders/databases specified by migration list.
     *
     * @param Files\Config $config
     * @param string $migrationListFile full path to file with migration list
     */
    public function cancelSiteMigrationTasks(
        Config $config, $migrationListFile
    ) {
        $command = 'cancel-tasks';
        $args = [
            "--site-migration-list=$migrationListFile",
        ];
        $this->_exec($command, $args, $config);
    }

    /**
     * Site migration: run previously scheduled site migration tasks.
     * Schedule site migration tasks with 'addSiteMigrationTasks' function.
     *
     * On Unix this function returns once the task runner was started (asynchronously).
     * On Windows this function returns only once the task runner was finished (synchronously).
     * The difference is caused by difficulties implementing asynchronous command execution on Windows.
     *
     * @param Config $config
     */
    public function runSiteMigrationTasks(Config $config)
    {
        // Start task queue runner for site migration.
        // If it is already running, this call will simply do nothing - "run-tasks" will exit immediately.
        $this->_exec('run-tasks', ['--async'], $config);
    }

    /**
     * Get migration statistics, as JSON string
     *
     * @return string
     */
    public function getStatistics()
    {
        return $this->_exec('summarize-statistics', ['--quiet']);
    }

    /**
     * Execute backend action for site migration.
     *
     * Backend for site migration runs under superuser (root on Linux, SYSTEM on Windows)
     * with Plesk 17.0 SDK features, which are not available on Plesk 12.5. No Administrator password is
     * required on Windows comparing to regular migration, so site migration could be launched by customer,
     * not only by server administrator.
     *
     * Backend lowers privileges to system user of subscription when necessary, for example when transferring files
     * by FTP/SSH.
     *
     * @param string $action
     * @param string[] $args
     * @param Config $config
     * @return mixed
     * @throws \Exception
     */
    private function _exec($action, $args, Config $config=null)
    {
        // Execute the backend under superuser
        $env = ToolUtils::getEnvVarsByConfig($config);
        $params = [$action];
        if (!is_null($config)) {
            $params[] = $config->getPath();
        }
        $params = array_merge($params, $args);

        if (\pm_ProductInfo::isWindows()) {
            $sbinExecutable = 'plesk-migrator.bat';
        } else {
            $sbinExecutable = 'plesk-migrator';
        }

        $result = \pm_ApiCli::callSbin($sbinExecutable, $params, \pm_ApiCli::RESULT_FULL, $env);
        if ($result['code'] !== 0) {
            $commandParts = ['plesk-migrator'];
            $commandParts = array_merge($commandParts, $params);
            foreach ($commandParts as &$commandPart) {
                $commandPart = escapeshellarg($commandPart);
            }

            throw new NonZeroExitCode(
                implode(" ", $commandParts), $result['code'], $result['stdout'], $result['stderr']
            );
        }
        return $result['stdout'];
    }
}
