<?php

/*
 * This file is part of the Behat Testwork.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Behat\Testwork\Output;

use Behat\Testwork\Output\Exception\FormatterNotFoundException;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * Manages formatters and their configuration.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
final class OutputManager
{
    /**
     * @var Formatter[]
     */
    private $formatters = [];

    /**
     * Initializes manager.
     */
    public function __construct(
        private readonly EventDispatcherInterface $eventDispatcher,
    ) {
    }

    /**
     * Registers formatter.
     */
    public function registerFormatter(Formatter $formatter)
    {
        if (isset($this->formatters[$formatter->getName()])) {
            $this->disableFormatter($formatter->getName());
        }

        $this->formatters[$formatter->getName()] = $formatter;
    }

    /**
     * Checks if formatter is registered.
     *
     * @param string $name
     *
     * @return bool
     */
    public function isFormatterRegistered($name)
    {
        return isset($this->formatters[$name]);
    }

    /**
     * Returns formatter by name provided.
     *
     * @param string $name
     *
     * @return Formatter
     *
     * @throws FormatterNotFoundException
     */
    public function getFormatter($name)
    {
        if (!$this->isFormatterRegistered($name)) {
            throw new FormatterNotFoundException(sprintf(
                '`%s` formatter is not found or has not been properly registered. Registered formatters: `%s`.',
                $name,
                implode('`, `', array_keys($this->formatters))
            ), $name);
        }

        return $this->formatters[$name];
    }

    /**
     * Returns all registered formatters.
     *
     * @return Formatter[]
     */
    public function getFormatters()
    {
        return $this->formatters;
    }

    /**
     * Enable formatter by name provided.
     *
     * @param string $formatter
     */
    public function enableFormatter($formatter)
    {
        if (!$this->isFormatterRegistered($formatter) && class_exists($formatter)) {
            $formatterInstance = new $formatter();
            $formatter = $formatterInstance->getName();

            if (!$this->isFormatterRegistered($formatter)) {
                $this->registerFormatter($formatterInstance);
            }
        }

        $this->eventDispatcher->addSubscriber($this->getFormatter($formatter));
    }

    /**
     * Disable formatter by name provided.
     *
     * @param string $formatter
     */
    public function disableFormatter($formatter)
    {
        $this->eventDispatcher->removeSubscriber($this->getFormatter($formatter));
    }

    /**
     * Disable all registered formatters.
     */
    public function disableAllFormatters()
    {
        array_map($this->disableFormatter(...), array_keys($this->formatters));
    }

    /**
     * Sets provided parameter to said formatter.
     *
     * @param string $formatter
     * @param string $parameterName
     */
    public function setFormatterParameter($formatter, $parameterName, $parameterValue)
    {
        $formatter = $this->getFormatter($formatter);
        $printer = $formatter->getOutputPrinter();

        switch ($parameterName) {
            case 'output_verbosity':
                $printer->setOutputVerbosity($parameterValue);

                return;
            case 'output_path':
                $printer->setOutputPath($parameterValue);

                return;
            case 'output_decorate':
                $printer->setOutputDecorated($parameterValue);

                return;
            case 'output_styles':
                $printer->setOutputStyles($parameterValue);

                return;
        }

        $formatter->setParameter($parameterName, $parameterValue);
    }

    /**
     * Sets provided formatter parameters.
     *
     * @param string $formatter
     */
    public function setFormatterParameters($formatter, array $parameters)
    {
        foreach ($parameters as $key => $val) {
            $this->setFormatterParameter($formatter, $key, $val);
        }
    }

    /**
     * Sets provided parameter to all registered formatters.
     *
     * @param string $parameterName
     */
    public function setFormattersParameter($parameterName, $parameterValue)
    {
        foreach (array_keys($this->formatters) as $formatter) {
            $this->setFormatterParameter($formatter, $parameterName, $parameterValue);
        }
    }

    /**
     * Sets provided parameters to all registered formatters.
     */
    public function setFormattersParameters(array $parameters)
    {
        foreach ($parameters as $key => $val) {
            $this->setFormattersParameter($key, $val);
        }
    }
}
