<?php
/**
 * Console Class
 *
 * @todo        Nach Foundation/Console verlagern.
 *
 * @version     1.0.$Revision:$
 * @version     SVN: $Id:$
 * @package     bplan-base/globals
 * @subpackage  Console
 * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
 * @copyright   Copyright (C) 2025, 2026 Wassilios Meletiadis <https://www.bplan-solutions.de/>
 * /Δ\
 */

namespace BplanBase\Globals\Console;


use Closure;
use Illuminate\Console\Command;
use InvalidArgumentException;
use RuntimeException;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Terminal;


/**
 * Console Class
 *
 * @see         https://laravel.com/docs/12.x/artisan#writing-output
 * @see         https://laravel.com/docs/12.x/prompts
 * @see         https://symfony.com/doc/current/components/console/helpers/index.html
 *
 * @todo        Icons ✅⚠️ ❌ →
 *
 * @version     2.1.0 / 2026-04-19
 * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
 */
class Output
{


/* +++ TRAITS +++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */


/* +++ CLASS CONSTANTS +++ ++++++++++++++++++++++++++++++++++++++++++++++++++ */


/* +++ OBJECT MEMBERS +++ +++++++++++++++++++++++++++++++++++++++++++++++++++ */


    /**
     * @var     Command|null
     */
    protected ?Command $_Command;


    /**
     * @var     ConsoleOutput $_ConsoleOutput
     */
    private ConsoleOutput $_ConsoleOutput;


/* +++ CLASS MEMBERS +++ ++++++++++++++++++++++++++++++++++++++++++++++++++++ */


    /**
     * @var     int
     */
    private static int $_defaultIndent = 1;


    /**
     * Farbedefinitionen für die Ausgabe
     *
     * Mögliche Farbbezeichnungen:
     *  black
     *  blue, bright-blue,
     *  cyan, bright-cyan
     *  gray
     *  green, bright-green
     *  magenta, bright-magenta
     *  red, bright-red
     *  white, bright-white
     *  yellow, bright-yellow
     *
     * @var     array
     * @version 2.0.0 / 2025-07-22
     */
    private static array $_outputColors = [
        'caption-level-1' => 'bright-blue',
        'caption-level-2' => 'cyan',
        'caption-level-3' => 'bright-cyan',
        'error'           => 'red',
        'highlight'       => 'yellow',
        'success'         => 'green',
        'title'           => 'bright-green',
    ];


    /**
     * @var     array
     */
    private static array $_statusColors = [];


    /**
     * @var     array
     */
    private static array $_validColors = [
        'black',
        'blue',
        'bright-blue',
        'bright-cyan',
        'bright-green',
        'bright-magenta',
        'bright-red',
        'bright-white',
        'bright-yellow',
        'cyan',
        'gray',
        'green',
        'magenta',
        'red',
        'white',
        'yellow',
    ];


/* +++ OBJECT METHODS +++ +++++++++++++++++++++++++++++++++++++++++++++++++++ */


    /**
     *
     * @param       Command|null $Command
     *              Required only for methods that delegate to the Command object
     *              (error(), table(), withProgressBar()). May be omitted when only
     *              print-based output methods are needed.
     *
     * @version     2.0.0 / 2026-04-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function __construct(?Command $Command = null)
    {
        $this->_Command = $Command;

        $this->_ConsoleOutput = new ConsoleOutput();

        if ($Command === null) {
            $this->_ConsoleOutput->setDecorated(true);
        }
    } // __construct()


    /**
     *
     * @param       string $text
     *
     * @return 	    void
     *
     * @version     1.0.0 / 2025-06-21
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function bulletMessage(string $text): void
    {
        $this->print($text, bullet: true);

    } // bulletMessage()


    /**
     *
     * @param       string $text
     *
     * @param       int|string $level
     *
     * @return 	    void
     *
     * @version     2.0.0 / 2025-07-22
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function caption(string $text, int|string $level = 1): void
    {
        if (is_int($level)) {
            $color = self::$_outputColors['caption-level-'.$level];
        } else {
            $color = $level;
        }
        $this->print($text, appendNewline: 1, color: $color, prependNewline: 1);

    } // caption()


    /**
     *
     * @param       string $text
     *
     * @return 	    void
     *
     * @version     1.0.0 / 2025-06-21
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function error(string $text): void
    {
        $this->_Command->error($text);

    } // error()


    /**
     *
     * @param       int $count
     *
     * @param       bool $print
     *
     * @return 	    string
     *
     * @version     1.0.0 / 2025-06-21
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function newline(int $count = 1, bool $print = true): string
    {
        $string = '';

        if ($count > 0) {
            $string .= str_repeat("\n", $count);
        }
        if ($print === true) {
            $this->_ConsoleOutput->write($string);
        }
        return $string;

    } // newline()


    /**
     *
     * @param       string $text
     *
     * @param       string $append
     *
     * @param       int|null $appendNewline
     *
     * @param       bool $bullet
     *
     * @param       null|string $color
     *              Can be either a valid color name or a predefined named color.
     *
     * @param       int $indent
     *
     * @param       int|null $prependNewline
     *
     * @return 	    void
     *
     * @version     1.0.0 / 2025-06-21
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function print(string $text, null|string $append = null, int $appendNewline = 0, bool $bullet = false, null|string $color = null, int $indent = 0, bool $noNewline = false, int $prependNewline = 0): void
    {
        if ($indent === 0) {
            $indent = self::$_defaultIndent;
        }
        $outputString = ($prependNewline > 0 ? self::newline($prependNewline, false) : '')
            .($indent > 0 ? str_repeat(' ', $indent) : '')
            .($bullet === true ? '• ' : '')
                .$text
            // .($append !== null ? ' <fg=gray>'.str_repeat('.', 30 - strlen($text)).'</> '.$append : '').''
            .($appendNewline > 0 ? self::newline($appendNewline, false) : '');

        if ($color !== null) {
            $outputString = self::colorize($outputString, $color);
        }
        if ($noNewline === true) {
            $this->_ConsoleOutput->write($outputString);
        } else {
            $this->_ConsoleOutput->writeln($outputString);
        }
    } // print()


    /**
     *
     * @param       string $text
     *
     * @param       string $status
     *
     * @return 	    void
     *
     * @version     1.1.0 / 2026-04-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function statusMessage(string $text, string $status): void
    {
        if (!isset(self::$_statusColors[$status])) {
            throw new RuntimeException('Undefined color for status ['.$status.']');
        }
        $width = min((new Terminal)->getWidth(), 150);
        $dots = max($width - mb_strlen($text) - mb_strlen($status) - 7, 2);
        $output = $text.' '.str_repeat('.', $dots).' '.self::colorize($status, self::$_statusColors[$status]);

        $this->print($output, bullet: true);

    } // statusMessage()


    /**
     *
     * @param       string $text
     *
     * @return 	    void
     *
     * @version     1.0.0 / 2025-06-21
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function table(array $captions, array $data): void
    {
        $this->_Command->error($captions, $data);

    } // error()


    /**
     *
     * @param       string $text
     *
     * @return 	    void
     *
     * @version     1.0.0 / 2025-06-21
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function title(string $text): void
    {
        $this->print($text, color: 'title', indent: 3, prependNewline: 2);

    } // title()


    /**
     *
     * @param       $data
     *
     * @param       Closure $Closure
     *
     * @return
     *
     * @version     1.0.0 / 2025-06-21
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function withProgressBar($data, Closure $Closure)
    {
        return $this->_Command->withProgressBar($data, $Closure);

    } // withProgressBar()


/* +++ CLASS METHODS +++ ++++++++++++++++++++++++++++++++++++++++++++++++++++ */


    /**
     *
     * @param       string $string
     *
     * @param       string $color
     *
     * @return      string
     *
     * @version     1.0.0 / 2025-06-21
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public static function colorize(string $string, string $color): string
    {
        $color = self::$_outputColors[$color] ?? $color;

        if (!in_array($color, self::$_validColors)) {
            throw new RuntimeException('Invalid color ['.$color.'].');
        }
        return '<fg='.$color.'>'.$string.'</>';

    } // colorize()


    /**
     *
     * @return      array
     *
     * @version     1.0.0 / 2025-07-22
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public static function getValidColors(): array
    {
        return self::$_validColors;

    } // getValidColors()

    /**
     *
     * @param       string $name
     *
     * @param       mixed $color
     *
     * @return      void
     *
     * @version     1.0.0 / 2025-06-21
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public static function registerOutputColor(string $name, string $color): void
    {
        if (!in_array($color, self::$_validColors)) {
            throw new InvalidArgumentException('Invalid color ['.$color.'].');
        }
        self::$_outputColors[$name] = $color;

    } // registerOutputColor()


    /**
     *
     * @param       string $name
     *
     * @param       mixed $color
     *
     * @return      void
     *
     * @version     1.0.0 / 2025-06-21
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public static function registerStatusColor(string $name, string $color): void
    {
        if (!in_array($color, self::$_validColors)) {
            throw new InvalidArgumentException('Invalid color ['.$color.'].');
        }
        self::$_statusColors[$name] = $color;

    } // registerStatusColor()


} // class Output {}
