<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Flex\Command;

use Composer\Command\BaseCommand;
use Composer\Config;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Dotenv\Dotenv;
use Symfony\Flex\Options;

class DumpEnvCommand extends BaseCommand
{
    private $config;
    private $options;

    public function __construct(Config $config, Options $options)
    {
        $this->config = $config;
        $this->options = $options;

        parent::__construct();
    }

    protected function configure(): void
    {
        $this->setName('symfony:dump-env')
            ->setAliases(['dump-env'])
            ->setDescription('Compiles .env files to .env.local.php.')
            ->setDefinition([
                new InputArgument('env', InputArgument::OPTIONAL, 'The application environment to dump .env files for - e.g. "prod".'),
            ])
            ->addOption('empty', null, InputOption::VALUE_NONE, 'Ignore the content of .env files')
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $runtime = $this->options->get('runtime') ?? [];
        $envKey = $runtime['env_var_name'] ?? 'APP_ENV';

        if ($env = $input->getArgument('env') ?? $runtime['env'] ?? null) {
            $_SERVER[$envKey] = $env;
        }

        $path = $this->options->get('root-dir').'/'.($runtime['dotenv_path'] ?? '.env');
        $GLOBALS['SYMFONY_DOTENV_VARS'] = [];

        if (!$env || !$input->getOption('empty')) {
            $vars = $this->loadEnv($path, $env, $runtime);
            $env = $vars[$envKey];
        }

        if ($input->getOption('empty')) {
            $vars = [$envKey => $env];
        }

        $vars = var_export($vars, true);

        foreach ($GLOBALS['SYMFONY_DOTENV_VARS'] as $k => $v) {
            $k = var_export($k, true);
            $vars = str_replace($v, "'.(\$_ENV[{$k}] ?? ".(str_starts_with($k, "'HTTP_") ? '' : "\$_SERVER[{$k}] ?? ")."'').'", $vars);
        }
        unset($GLOBALS['SYMFONY_DOTENV_VARS']);
        $vars = strtr($vars, [
            "''.(" => '(',
            ").''.(" => ').(',
            ").''" => ')',
        ]);

        $vars = <<<EOF
            <?php

            // This file was generated by running "composer dump-env $env"

            return $vars;

            EOF;
        file_put_contents($path.'.local.php', $vars, \LOCK_EX);

        $this->getIO()->writeError('Successfully dumped .env files in <info>.env.local.php</>');

        return 0;
    }

    private function loadEnv(string $path, ?string $env, array $runtime): array
    {
        if (!file_exists($autoloadFile = $this->config->get('vendor-dir').'/autoload.php')) {
            throw new \RuntimeException(\sprintf('Please run "composer install" before running this command: "%s" not found.', $autoloadFile));
        }

        require $autoloadFile;

        if (!class_exists(Dotenv::class)) {
            throw new \RuntimeException('Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.');
        }

        $envKey = $runtime['env_var_name'] ?? 'APP_ENV';
        $globalsBackup = [$_SERVER, $_ENV];
        unset($_SERVER[$envKey]);
        $_ENV = [$envKey => $env];
        $_SERVER['SYMFONY_DOTENV_VARS'] = implode(',', array_keys($_SERVER));
        putenv('SYMFONY_DOTENV_VARS='.$_SERVER['SYMFONY_DOTENV_VARS']);

        try {
            $dotenv = new Dotenv();

            if (!$env && file_exists($p = "$path.local")) {
                $env = $_ENV[$envKey] = $dotenv->parse(file_get_contents($p), $p)[$envKey] ?? null;
            }

            if (!$env) {
                throw new \RuntimeException(\sprintf('Please provide the name of the environment either by passing it as command line argument or by defining the "%s" variable in the ".env.local" file.', $envKey));
            }

            $testEnvs = $runtime['test_envs'] ?? ['test'];

            $dotenv->loadEnv($path, $envKey, 'dev', $testEnvs);

            unset($_ENV['SYMFONY_DOTENV_VARS'], $_ENV['SYMFONY_DOTENV_PATH']);
            $env = $_ENV;
        } finally {
            [$_SERVER, $_ENV] = $globalsBackup;
        }

        return $env;
    }
}

namespace Symfony\Component\Dotenv;

function getenv(?string $name = null, bool $local_only = false): string|array|false
{
    if (null === $name) {
        return \getenv($name, $local_only);
    }

    return $GLOBALS['SYMFONY_DOTENV_VARS'][$name] ??= md5(random_bytes(10));
}
