<?php

declare(strict_types=1);

/*
 * This file is part of the TYPO3 CMS project.
 *
 * It is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, either version 2
 * of the License, or any later version.
 *
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

namespace TYPO3\CMS\Core\Command;

use Psr\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use TYPO3\CMS\Core\Attribute\AsNonSchedulableCommand;
use TYPO3\CMS\Core\Core\Bootstrap;
use TYPO3\CMS\Core\Package\Event\PackagesMayHaveChangedEvent;
use TYPO3\CMS\Core\Package\PackageManager;
use TYPO3\CMS\Core\Package\PackageSetup;

/**
 * Command for setting up all extensions via CLI.
 */
#[AsCommand('extension:setup', 'Set up extensions and perform database migrations.')]
#[AsNonSchedulableCommand]
class SetupExtensionsCommand extends Command
{
    public function __construct(
        private readonly PackageManager $packageManager,
        private readonly EventDispatcherInterface $eventDispatcher,
        private readonly PackageSetup $packageSetup,
    ) {
        parent::__construct();
    }

    /**
     * Defines the allowed options for this command
     */
    protected function configure(): void
    {
        $this
            ->setDescription('Set up extensions')
            ->setHelp(
                <<<'EOD'
Setup all extensions or the given extension by extension key. This must
be performed after new extensions are required via Composer.

The command performs all necessary setup operations, such as database
schema changes, static data import, distribution files import etc.

The given extension keys must be recognized by TYPO3 or will be ignored.
EOD
            )
            ->addOption(
                'extension',
                '-e',
                InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED,
                'Only set up extensions with given key'
            );
    }

    /**
     * Sets up one or all extensions
     */
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        Bootstrap::initializeBackendAuthentication();
        $this->eventDispatcher->dispatch(new PackagesMayHaveChangedEvent());

        $io = new SymfonyStyle($input, $output);
        $extensionKeys = $input->getOption('extension');
        $packagesToSetUp = $this->packageManager->getActivePackages();
        if (!empty($extensionKeys)) {
            $packagesToSetUp = array_filter(
                $packagesToSetUp,
                static function ($extKey) use ($extensionKeys) {
                    return in_array($extKey, $extensionKeys, true);
                },
                ARRAY_FILTER_USE_KEY
            );
        }
        if (empty($packagesToSetUp)) {
            $io->error('Given extension(s) "' . implode(', ', $extensionKeys) . '" not found in the system.');
            return Command::FAILURE;
        }
        $messages = $this->packageSetup->setup($packagesToSetUp);
        foreach ($messages as $message) {
            $io->warning($message->getMessage());
        }
        $io->success('Extension(s) "' . implode(', ', array_keys($packagesToSetUp)) . '" successfully set up.');

        return Command::SUCCESS;
    }
}
