<?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 bplan-solutions GmbH & Co. KG <https://www.bplan-solutions.de/>
 * /Δ\
 */

namespace BplanBase\Globals\Console;


use Closure;
use Illuminate\Console\Command;
use Symfony\Component\Console\Output\ConsoleOutput;


/**
 * Console Class
 *
 * @see         https://laravel.com/docs/12.x/artisan#prompting-for-input
 * @see         https://laravel.com/docs/12.x/prompts
 *
 * @version     1.0.0 / 2025-06-21
 * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
 */
class Prompt
{


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


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


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


    /**
     * @var         Command $_Command
     */
    protected Command $_Command;


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


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


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


    /**
     *
     * @param       Command $command
     *
     * @version     1.0.0 / 2025-06-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function __construct(Command $Command)
    {
        $this->_Command = $Command;

    } // __construct()


    /**
     * The anticipate method can be used to provide auto-completion for possible choices
     *
     * The user can still provide any answer, regardless of the auto-completion hints.
     *
     * @param       string $question
     *
     * @param       array|Closure $suggestions
     *              An array with the choices. Alternatively, you may pass a closure as
     * *            the second argument to the anticipate method. The closure will be
     *              called each time the user types an input character. The closure
     *              should accept a string parameter containing the user's input so far,
     *              and return an array of options for auto-completion.
     *              Example:
     *
     *              $name = $this->anticipate('What is your address?', function (string $input) {
     *                  return Address::whereLike('name', "{$input}%")
     *                      ->limit(5)
     *                      ->pluck('name')
     *                      ->all();
     *              });
     *
     * @param       null|string $default
     *
     * @return      string
     *
     * @version     1.0.0 / 2025-06-21
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function anticipate(string $question, array|Closure $suggestions, null|string $default = null): string
    {
        return $this->_Command->anticipate("\n".' '.$question, $suggestions, $default);

    } // anticipate()


    /**
     * Ask the user to provide input during the execution of your command
     *
     * @param       string $question
     *
     * @param       null|string $default
     *              Specifies the default value that should be returned if no user input is provided.
     *
     * @param       null|Closure $validator
     *
     * @return      string
     *
     * @version     1.0.0 / 2025-06-21
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function ask(string $question, null|string $default = null, null|Closure $validator = null): string
    {
        $answer = $this->_Command->ask($question, $default);

        if ($validator !== null) {
            while (!$validator($answer)) {
                $this->_Command->warn('   '.__('globals::console.error.invalid-input'));

                $answer = $this->_Command->ask($question, $default);
            }
        }
        return $answer;

    } // ask()


    /**
     * Give the user a predefined set of choices when asking a question
     *
     * @param       string $question
     *
     * @param       array $choices
     *
     * @param       int|string|null $default
     *              You may set the array index of the default value to be returned if
     *              no option is chosen.
     *
     * @param       null|int $attempts
     *              Determining the maximum number of attempts to select a valid response.
     *
     * @param       bool $multiple
     *              Decide whether multiple selections are permitted.
     *
     * @return      string
     *
     * @version     1.0.0 / 2025-06-21
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function choice(string $question, array $choices, int|string|null $default = null, $attempts = null, $multiple = false): string
    {
        $choice = $this->_Command->choice("\n".' '.$question, $choices, $default, $attempts, $multiple);

        return array_search($choice,  $choices);

    } // choice()


    /**
     * Ask the user for a simple "yes or no" confirmation
     *
     * @param       string $question
     *
     * @param       bool $default
     *              By default, this method will return false. However, if the user
     *              enters y or yes in response to the prompt, the method will return
     *              true. If necessary, you may specify that the confirmation prompt
     *              should return true by default by passing true for this argument.
     *
     * @return      bool
     *
     * @version     1.0.0 / 2025-06-21
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function confirm(string $question, bool $default = true): bool
    {
        return $this->_Command->confirm("\n".' '.$question, $default);

    } // confirm()


    /**
     * Is similar to ask, but the user's input will not be visible to them as they type in the console
     *
     * This method is useful when asking for sensitive information such as passwords.
     *
     * @param       string $question
     *
     * @param       $fallback
     *
     * @return      string
     *
     * @version     1.0.0 / 2025-06-21
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function secret(string $question, $fallback = true): string
    {
        return $this->_Command->secret("\n".' '.$question, $fallback);

    } // secret()


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


    /**
     *
     * @param       int $count
     *
     * @return 	    void
     *
     * @version     1.0.0 / 2025-06-21
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function newline(int $count = 1, bool $print = true): void
    {
        if (self::$_ConsoleOutput === null) {
            self::$_ConsoleOutput = new ConsoleOutput();
        }
        self::$_ConsoleOutput->write(str_repeat("\n", $count));

    } // newline()


} // class Prompt {}
