<?php

namespace Google\Web_Stories_Dependencies\AmpProject\Cli;

use Google\Web_Stories_Dependencies\AmpProject\Exception\AmpCliException;
use Google\Web_Stories_Dependencies\AmpProject\Exception\Cli\InvalidSapi;
use Exception;
/**
 * This file is adapted from the splitbrain\php-cli library, which is authored by Andreas Gohr <andi@splitbrain.org> and
 * licensed under the MIT license.
 *
 * Source: https://github.com/splitbrain/php-cli/blob/fb4f888866d090b10e3e68292d197ca274cea626/src/CLI.php
 */
/**
 * Your commandline script should inherit from this class and implement the abstract methods.
 *
 * @package ampproject/amp-toolbox
 */
abstract class Executable
{
    /**
     * Instance of the Colors helper object.
     *
     * @var Colors
     */
    public $colors;
    /**
     * The executable script itself.
     *
     * @var string
     */
    protected $bin;
    /**
     * Instance of the options parser to use.
     *
     * @var Options
     */
    protected $options;
    /**
     * PSR-3 compatible log levels and their prefix, color, output channel.
     *
     * @var array<array>
     */
    protected $loglevels = [LogLevel::DEBUG => ['', Colors::C_RESET, \STDOUT], LogLevel::INFO => ['ℹ ', Colors::C_CYAN, \STDOUT], LogLevel::NOTICE => ['☛ ', Colors::C_CYAN, \STDOUT], LogLevel::SUCCESS => ['✓ ', Colors::C_GREEN, \STDOUT], LogLevel::WARNING => ['⚠ ', Colors::C_BROWN, \STDERR], LogLevel::ERROR => ['✗ ', Colors::C_RED, \STDERR], LogLevel::CRITICAL => ['☠ ', Colors::C_LIGHTRED, \STDERR], LogLevel::ALERT => ['✖ ', Colors::C_LIGHTRED, \STDERR], LogLevel::EMERGENCY => ['✘ ', Colors::C_LIGHTRED, \STDERR]];
    /**
     * Default log level.
     *
     * @var string
     */
    protected $loglevel = 'info';
    /**
     * Constructor.
     *
     * Initialize the arguments, set up helper classes and set up the CLI environment.
     *
     * @param bool         $autocatch Optional. Whether exceptions should be caught and handled automatically. Defaults
     *                                to true.
     * @param Options|null $options   Optional. Instance of the Options object to use. Defaults to null to instantiate a
     *                                new one.
     * @param Colors|null  $colors    Optional. Instance of the Colors object to use. Defaults to null to instantiate a
     *                                new one.
     */
    public function __construct($autocatch = \true, Options $options = null, Colors $colors = null)
    {
        if ($autocatch) {
            \set_exception_handler([$this, 'fatal']);
        }
        $this->colors = $colors instanceof Colors ? $colors : new Colors();
        $this->options = $options instanceof Options ? $options : new Options($this->colors);
    }
    /**
     * Execute the CLI program.
     *
     * Executes the setup() routine, adds default options, initiate the options parsing and argument checking
     * and finally executes main() - Each part is split into their own protected function below, so behaviour
     * can easily be overwritten.
     *
     * @param bool $exitOnCompletion Optional. Whether to exit on completion. Defaults to true.
     * @throws InvalidSapi If a SAPI other than 'cli' is detected.
     */
    public function run($exitOnCompletion = \true)
    {
        $sapi = \php_sapi_name();
        if ('cli' !== $sapi) {
            throw InvalidSapi::forSapi($sapi);
        }
        $this->setup($this->options);
        $this->registerDefaultOptions();
        $this->parseOptions();
        $this->handleDefaultOptions();
        $this->setupLogging();
        $this->checkArguments();
        $this->execute();
        if ($exitOnCompletion) {
            exit(0);
        }
    }
    /**
     * Exits the program on a fatal error.
     *
     * @param Exception|string $error   Either an exception or an error message.
     * @param array            $context Optional. Associative array of contextual information. Defaults to an empty
     *                                  array.
     */
    public function fatal($error, array $context = [])
    {
        $code = 0;
        if ($error instanceof Exception) {
            $this->debug(\get_class($error) . ' caught in ' . $error->getFile() . ':' . $error->getLine());
            $this->debug($error->getTraceAsString());
            $code = $error->getCode();
            $error = $error->getMessage();
        }
        if (!$code) {
            $code = AmpCliException::E_ANY;
        }
        $this->critical($error, $context);
        exit($code);
    }
    /**
     * System is unusable.
     *
     * @param string $message Log message.
     * @param array  $context Optional. Contextual information. Defaults to an empty array.
     * @return void
     */
    public function emergency($message, array $context = [])
    {
        $this->log(LogLevel::EMERGENCY, $message, $context);
    }
    /**
     * Action must be taken immediately.
     *
     * Example: Entire website down, database unavailable, etc. This should trigger the SMS alerts and wake you up.
     *
     * @param string $message Log message.
     * @param array  $context Optional. Contextual information. Defaults to an empty array.
     * @return void
     */
    public function alert($message, array $context = [])
    {
        $this->log(LogLevel::ALERT, $message, $context);
    }
    /**
     * Critical conditions.
     *
     * Example: Application component unavailable, unexpected exception.
     *
     * @param string $message Log message.
     * @param array  $context Optional. Contextual information. Defaults to an empty array.
     * @return void
     */
    public function critical($message, array $context = [])
    {
        $this->log(LogLevel::CRITICAL, $message, $context);
    }
    /**
     * Runtime errors that do not require immediate action but should typically be logged and monitored.
     *
     * @param string $message Log message.
     * @param array  $context Optional. Contextual information. Defaults to an empty array.
     * @return void
     */
    public function error($message, array $context = [])
    {
        $this->log(LogLevel::ERROR, $message, $context);
    }
    /**
     * Exceptional occurrences that are not errors.
     *
     * Example: Use of deprecated APIs, poor use of an API, undesirable things that are not necessarily wrong.
     *
     * @param string $message Log message.
     * @param array  $context Optional. Contextual information. Defaults to an empty array.
     * @return void
     */
    public function warning($message, array $context = [])
    {
        $this->log(LogLevel::WARNING, $message, $context);
    }
    /**
     * Normal, positive outcome.
     *
     * @param string $string  Log message.
     * @param array  $context Optional. Contextual information. Defaults to an empty array.
     * @return void
     */
    public function success($string, array $context = [])
    {
        $this->log(LogLevel::SUCCESS, $string, $context);
    }
    /**
     * Normal but significant events.
     *
     * @param string $message Log message.
     * @param array  $context Optional. Contextual information. Defaults to an empty array.
     * @return void
     */
    public function notice($message, array $context = [])
    {
        $this->log(LogLevel::NOTICE, $message, $context);
    }
    /**
     * Interesting events.
     *
     * Example: User logs in, SQL logs.
     *
     * @param string $message Log message.
     * @param array  $context Optional. Contextual information. Defaults to an empty array.
     * @return void
     */
    public function info($message, array $context = [])
    {
        $this->log(LogLevel::INFO, $message, $context);
    }
    /**
     * Detailed debug information.
     *
     * @param string $message Log message.
     * @param array  $context Optional. Contextual information. Defaults to an empty array.
     * @return void
     */
    public function debug($message, array $context = [])
    {
        $this->log(LogLevel::DEBUG, $message, $context);
    }
    /**
     * Log a message of a given log level to the logs.
     *
     * @param string $level   Log level to use.
     * @param string $message Log message.
     * @param array  $context Optional. Contextual information. Defaults to an empty array.
     * @return void
     */
    public function log($level, $message, array $context = [])
    {
        if (!LogLevel::matches($level, $this->options->getOption('loglevel', $this->loglevel))) {
            return;
        }
        list($prefix, $color, $channel) = $this->loglevels[$level];
        if (!$this->colors->isEnabled()) {
            $prefix = '';
        }
        $message = $this->interpolate($message, $context);
        $this->colors->line($prefix . $message, $color, $channel);
    }
    /**
     * Interpolates context values into the message placeholders.
     *
     * @param string $message Message to interpolate.
     * @param array  $context Optional. Contextual information. Defaults to an empty array.
     * @return string Interpolated string.
     */
    protected function interpolate($message, array $context = [])
    {
        // Build a replacement array with braces around the context keys.
        $replace = [];
        foreach ($context as $key => $val) {
            // Check that the value can be cast to string.
            if (!\is_array($val) && (!\is_object($val) || \method_exists($val, '__toString'))) {
                $replace['{' . $key . '}'] = $val;
            }
        }
        // Interpolate replacement values into the message and return.
        return \strtr($message, $replace);
    }
    /**
     * Add the default help, color and log options.
     */
    protected function registerDefaultOptions()
    {
        $this->options->registerOption('help', 'Display this help screen and exit immediately.', 'h');
        $this->options->registerOption('no-colors', 'Do not use any colors in output. Useful when piping output to other tools or files.');
        $this->options->registerOption('loglevel', "Minimum level of messages to display. Default is {$this->colors->wrap($this->loglevel, Colors::C_CYAN)}." . ' Valid levels are: debug, info, notice, success, warning, error, critical, alert, emergency.', null, 'level');
    }
    /**
     * Handle the default options.
     */
    protected function handleDefaultOptions()
    {
        if ($this->options->getOption('no-colors')) {
            $this->colors->disable();
        }
        if ($this->options->getOption('help')) {
            echo $this->options->help();
            exit(0);
        }
    }
    /**
     * Handle the logging options.
     */
    protected function setupLogging()
    {
        $this->loglevel = $this->options->getOption('loglevel', $this->loglevel);
        if (!\in_array($this->loglevel, LogLevel::ORDER)) {
            $this->fatal('Unknown log level');
        }
    }
    /**
     * Wrapper around the option parsing.
     */
    protected function parseOptions()
    {
        $this->options->parseOptions();
    }
    /**
     * Wrapper around the argument checking.
     */
    protected function checkArguments()
    {
        $this->options->checkArguments();
    }
    /**
     * Wrapper around main.
     */
    protected function execute()
    {
        $this->main($this->options);
    }
    /**
     * Register options and arguments on the given $options object.
     *
     * @param Options $options Options instance to register the commands with.
     * @return void
     */
    protected abstract function setup(Options $options);
    /**
     * Main program routine.
     *
     * Arguments and options have been parsed when this is run.
     *
     * @param Options $options Options instance to register the commands with.
     * @return void
     */
    protected abstract function main(Options $options);
}
