<?php

use Psy\Configuration;
use Psy\Shell;
use Psy\VersionUpdater\GitHubChecker;
use Psy\VersionUpdater\Installer;
use Psy\VersionUpdater\SelfUpdate;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;

if (!isset($_SERVER['PSYSH_IGNORE_ENV']) || !$_SERVER['PSYSH_IGNORE_ENV']) {
    if (\defined('HHVM_VERSION_ID')) {
        \fwrite(\STDERR, 'PsySH v0.11 and higher does not support HHVM. Install an older version, or set the environment variable PSYSH_IGNORE_ENV=1 to override this restriction and proceed anyway.' . \PHP_EOL);
        exit(1);
    }

    if (\PHP_VERSION_ID < 70000) {
        \fwrite(\STDERR, 'PHP 7.0.0 or higher is required. You can set the environment variable PSYSH_IGNORE_ENV=1 to override this restriction and proceed anyway.' . \PHP_EOL);
        exit(1);
    }

    if (\PHP_VERSION_ID > 89999) {
        \fwrite(\STDERR, 'PHP 9 or higher is not supported. You can set the environment variable PSYSH_IGNORE_ENV=1 to override this restriction and proceed anyway.' . \PHP_EOL);
        exit(1);
    }

    if (!\function_exists('json_encode')) {
        \fwrite(\STDERR, 'The JSON extension is required. Please install it. You can set the environment variable PSYSH_IGNORE_ENV=1 to override this restriction and proceed anyway.' . \PHP_EOL);
        exit(1);
    }

    if (!\function_exists('token_get_all')) {
        \fwrite(\STDERR, 'The Tokenizer extension is required. Please install it. You can set the environment variable PSYSH_IGNORE_ENV=1 to override this restriction and proceed anyway.' . \PHP_EOL);
        exit(1);
    }
}

$usageException = null;
$shellIsPhar = Shell::isPhar();

$input = new ArgvInput();
try {
    $input->bind(new InputDefinition(\array_merge(Configuration::getInputOptions(), [
        new InputOption('help', 'h', InputOption::VALUE_NONE),
        new InputOption('version', 'V', InputOption::VALUE_NONE),
        new InputOption('self-update', 'u', InputOption::VALUE_NONE),

        new InputArgument('include', InputArgument::IS_ARRAY),
    ])));
} catch (\RuntimeException $e) {
    $usageException = $e;
}

try {
    $config = Configuration::fromInput($input);
} catch (\InvalidArgumentException $e) {
    $usageException = $e;
}

// Handle --help
if ($usageException !== null || $input->getOption('help')) {
    if ($usageException !== null) {
        echo $usageException->getMessage() . \PHP_EOL . \PHP_EOL;
    }

    $version = Shell::getVersionHeader(false);
    $argv = isset($_SERVER['argv']) ? $_SERVER['argv'] : [];
    $name = $argv ? \basename(\reset($argv)) : 'psysh';

    echo <<<EOL
$version

Usage:
$name [--version] [--help] [files...]

Options:
-h, --help            Display this help message.
-c, --config FILE     Use an alternate PsySH config file location.
--cwd PATH        Use an alternate working directory.
-V, --version         Display the PsySH version.

EOL;
    if ($shellIsPhar) {
        echo <<<EOL
-u, --self-update     Install a newer version if available.

EOL;
    }
    echo <<<EOL
--color           Force colors in output.
--no-color        Disable colors in output.
-i, --interactive     Force PsySH to run in interactive mode.
-n, --no-interactive  Run PsySH without interactive input. Requires input from stdin.
-r, --raw-output      Print var_export-style return values (for non-interactive input)
--compact         Run PsySH with compact output.
-q, --quiet           Shhhhhh.
-v|vv|vvv, --verbose  Increase the verbosity of messages.
--yolo            Run PsySH without input validation. You don't want this.

EOL;

    exit($usageException === null ? 0 : 1);
}

// Handle --version
if ($input->getOption('version')) {
    echo Shell::getVersionHeader($config->useUnicode()) . \PHP_EOL;
    exit(0);
}

// Handle --self-update
if ($input->getOption('self-update')) {
    if (!$shellIsPhar) {
        \fwrite(\STDERR, 'The --self-update option can only be used with with a phar based install.' . \PHP_EOL);
        exit(1);
    }
    $selfUpdate = new SelfUpdate(new GitHubChecker(), new Installer());
    $result = $selfUpdate->run($input, $config->getOutput());
    exit($result);
}

$shell = new Shell($config);

try {
    // And go!
    $shell->run();
} catch (\Throwable $e) {
    \fwrite(\STDERR, $e->getMessage() . \PHP_EOL);

    // @todo this triggers the "exited unexpectedly" logic in the
    // ForkingLoop, so we can't exit(1) after starting the shell...
    // fix this :)

    // exit(1);
}
