<?php
/**
 * Setup Command Class
 *
 * @version     1.0.$Revision:$
 * @version     SVN: $Id:$
 * @package     bplan-base/globals
 * @subpackage  Commands
 * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
 * @copyright   Copyright (C) 2025, 2026 bplan-solutions GmbH & Co. KG <https://www.bplan-solutions.de/>
 * /Δ\
 */

namespace BplanBase\Globals\Console\Commands;


use BplanBase\Globals\Composer\ComposerJson;
use BplanBase\Globals\Console\BaseSetup;
use BplanBase\Globals\Env\EnvFile;
use BplanBase\Globals\Json\JsonFile;
use BplanBase\Globals\Setup\FilePublisher;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan;


use function Laravel\Prompts\clear;


/**
 * Setup Command Class
 *
 * @version     12.0.0 / 2026-04-22
 * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
 */
class GlobalsSetup extends BaseSetup
{


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


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


    /**
     * Git checkout: .env.in exists, .env does not. Installation mode must be prompted.
     *
     * @var     string
     */
    const STATE_CHECKOUT   = 'checkout';


    /**
     * Setup was already run: both .env and .env.in exist. Mode is derived from APP_ENV.
     *
     * @var     string
     */
    const STATE_CONFIGURED = 'configured';


    /**
     * Fresh Laravel install: .env exists, .env.in does not. Mode defaults to local.
     *
     * @var     string
     */
    const STATE_FRESH      = 'fresh';


    /**
     * Neither .env nor .env.in found. Cannot proceed.
     *
     * @var     string
     */
    const STATE_INVALID    = 'invalid';


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


    /**
     * @var     string
     */
    private string $_apiPackage = 'laravel-json-api/laravel';


    /**
     * @var     string
     */
    private string $_codeGeneratorLoaderPackage = 'bplan-base/laravel-code-generator-loader';


    /**
     * @var     string
     */
    private string $_codeGeneratorPackage = 'bplan-base/laravel-code-generator';


    /**
     * @var     array
     */
    private array $_composerJson;


    /**
     * @var     boolean
     */
    private bool $_hasApi;


    /**
     * @var     boolean
     */
    private bool $_hasCodeGenerator;


    /**
     * @var     boolean
     */
    private bool $_hasCodeGeneratorLoader;


    /**
     * @var     boolean
     */
    private bool $_hasHttpService;


    /**
     * @var     boolean
     */
    private bool $_hasLangPackage;


    /**
     * @var     boolean
     */
    private bool $_hasMergePlugin;


    /**
     * @var     boolean
     */
    private bool $_hasPrivateGit;


    /**
     * @var     string  'local' | 'dev-server' | 'production' | 'demo'
     */
    private string $_installationMode = 'local';


    /**
     * @var     string  One of the STATE_* constants.
     */
    private string $_installationState = self::STATE_INVALID;


    /**
     * @var     int
     */
    private int $_installPackages = 0;


    /**
     * @var     string
     */
    private string $_mergePlugin = 'wikimedia/composer-merge-plugin';


    /**
     * @var array
     */
    private array $_missingLocales = [];


    /**
     * @var     string
     */
    private string $_privateGitName = 'bplan/composer-packages';


    /**
     * @var     string
     */
    private string $_privateGitUrl = 'https://satis.bplan.solutions';


    /**
     * @var     boolean
     */
    private bool $_requireApi = false;


    /**
     * @var     bool
     */
    private bool $_requireCodeGenerator = false;


    /**
     * @var     bool
     */
    private bool $_requireHttpService = false;


    /**
     * @var     bool
     */
    private bool $_requireMergePlugin;


    /**
     * @var     string
     */
    protected $description = 'Prepares the Laravel project environment';


    /**
     * @var     string
     */
    protected $signature = 'globals:setup';


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


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


    /**
     *
     * @return      void
     *
     * @version     1.0.0 / 2026-02-28
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _addLangUpdateCommand(): void
    {
        $path = base_path('composer.json');

        $json = json_decode(file_get_contents($path), true);

        if (!in_array('@php artisan lang:publish', $json['scripts']['post-update-cmd'])) {
            $this->_Output->bulletMessage('Add [artisan lang:publish] to "scripts.post-update-cmd" in composer.json');

            $json['scripts']['post-update-cmd'][] = '@php artisan lang:publish';

            file_put_contents($path, json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES).PHP_EOL);
        }
    } // _addLangUpdateCommand()


    /**
     * Asks the user for database connection settings and writes them to .env.
     *
     * Skipped when: user declines, or DB is already configured with a non-sqlite driver
     * and all required fields are filled. Re-asked when sqlite is active or required
     * fields are empty.
     *
     * @return      void
     *
     * @version     1.1.0 / 2026-04-20
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _configureDatabase(): void
    {
        $Env        = EnvFile::loadOrCreate(base_path('.env'));
        $connection = $Env->get('DB_CONNECTION', 'sqlite');
        $host       = $Env->get('DB_HOST');
        $database   = $Env->get('DB_DATABASE');
        $username   = $Env->get('DB_USERNAME');

        /*
        **  Determine whether to prompt based on current state. */
        $needsConfig = $connection === 'sqlite'
            || $host === ''
            || $database === ''
            || $username === '';

        if ($needsConfig === false) {
            return;
        }
        if ($this->_Prompt->confirm('Do you want to configure the database connection?', true) === false) {
            return;
        }
        $this->_Output->caption('Database configuration');

        $driverIndex = (int) $this->_Prompt->choice(
            'Select database driver:',
            ['sqlite', 'mysql', 'pgsql', 'sqlsrv'],
            $connection === 'sqlite' ? 0 : (array_search($connection, ['sqlite', 'mysql', 'pgsql', 'sqlsrv']) ?: 1)
        );
        $driver = ['sqlite', 'mysql', 'pgsql', 'sqlsrv'][$driverIndex];

        if ($driver === 'sqlite') {
            $Env->set('DB_CONNECTION', $driver)
                ->set('DB_DATABASE',   'database/database.sqlite')
                ->save();

            return;
        }
        $defaultPort = match ($driver) {
            'mysql'  => '3306',
            'pgsql'  => '5432',
            'sqlsrv' => '1433',
            default  => '3306',
        };

        $dbHost     = $this->_Prompt->ask('DB_HOST',     $host !== ''     ? $host     : '127.0.0.1');
        $dbPort     = $this->_Prompt->ask('DB_PORT',     $Env->get('DB_PORT', $defaultPort));
        $dbDatabase = $this->_Prompt->ask('DB_DATABASE', $database !== '' ? $database : '');
        $dbUsername = $this->_Prompt->ask('DB_USERNAME', $username !== '' ? $username : '');
        $dbPassword = $this->_Prompt->secret('DB_PASSWORD');

        $Env->set('DB_CONNECTION', $driver)
            ->set('DB_HOST',       $dbHost)
            ->set('DB_PORT',       $dbPort)
            ->set('DB_DATABASE',   $dbDatabase)
            ->set('DB_USERNAME',   $dbUsername)
            ->set('DB_PASSWORD',   $dbPassword)
            ->save();

    } // _configureDatabase()


    /**
     * Asks the user for mail settings and writes them to .env.
     *
     * Skipped when: user declines, or MAIL_HOST is already configured.
     * Re-asked when MAIL_HOST is empty.
     *
     * @return      void
     *
     * @version     1.1.0 / 2026-04-20
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _configureMail(): void
    {
        $Env        = EnvFile::loadOrCreate(base_path('.env'));
        $mailHost   = $Env->get('MAIL_HOST');
        $mailMailer = $Env->get('MAIL_MAILER', 'smtp');

        if ($mailHost !== '' && $mailMailer !== 'log' && $mailMailer !== 'array') {
            return;
        }
        if ($this->_Prompt->confirm('Do you want to configure the mail connection?', false) === false) {
            return;
        }
        $this->_Output->caption('Mail configuration');

        $mailers     = ['smtp', 'mailgun', 'ses', 'log', 'array'];
        $mailerIndex = (int) $this->_Prompt->choice(
            'Select mail driver:',
            $mailers,
            array_search($mailMailer, $mailers) ?: 0
        );
        $mailer = $mailers[$mailerIndex];

        if ($mailer !== 'log' && $mailer !== 'array') {
            $host       = $this->_Prompt->ask('MAIL_HOST',       $mailHost !== '' ? $mailHost : '');
            $port       = $this->_Prompt->ask('MAIL_PORT',       $Env->get('MAIL_PORT', '587'));
            $username   = $this->_Prompt->ask('MAIL_USERNAME',   $Env->get('MAIL_USERNAME'));
            $password   = $this->_Prompt->secret('MAIL_PASSWORD');
            $encryption = $this->_Prompt->ask('MAIL_ENCRYPTION', $Env->get('MAIL_ENCRYPTION', 'tls'));

            $Env->set('MAIL_HOST',       $host)
                ->set('MAIL_PORT',       $port)
                ->set('MAIL_USERNAME',   $username)
                ->set('MAIL_PASSWORD',   $password)
                ->set('MAIL_ENCRYPTION', $encryption);
        }
        $fromAddress = $this->_Prompt->ask('MAIL_FROM_ADDRESS', $Env->get('MAIL_FROM_ADDRESS'));
        $fromName    = $this->_Prompt->ask('MAIL_FROM_NAME',    trim($Env->get('MAIL_FROM_NAME'), '"'));

        $Env->set('MAIL_MAILER',       $mailer)
            ->set('MAIL_FROM_ADDRESS', $fromAddress)
            ->set('MAIL_FROM_NAME',    '"'.$fromName.'"')
            ->save();

    } // _configureMail()


    /**
     *
     * @return 	    void
     *
     * @version     1.2.0 / 2026-04-20
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _finishPackageSetup(): void
    {
        if ($this->_installPackages > 0) {
            $this->_Output->caption('Package installation');

            if ($this->_installationMode === 'local') {
                $this->_Output->print('Run `composer update`', bullet: true);

                $this->_runShellCommand(['composer', 'update']);
            } else {
                $this->_Output->print('Run `composer install`', bullet: true);

                $this->_runShellCommand(['composer', 'install']);
            }
            if ($this->_requireApi === true) {
                $Publisher = new FilePublisher($this->_basePath, ['etc/setup/src/config/jsonapi.php' => 'config/jsonapi.php'], [$this, 'publishMessage']);

                $Publisher->publish();
            }
        }
        $this->_Output->caption('Update existing locales');

        $this->call('lang:update');

        if (!empty($this->_missingLocales)) {
            $this->_Output->caption('Add new locales');

            foreach ($this->_missingLocales as $locale) {
                $this->_Output->bulletMessage('Install locales for ['.$locale.']');

                Artisan::call('lang:add '.$locale);
            }
        }
        if ($this->_hasLangPackage === true) {
            $this->_addLangUpdateCommand();
        }
    } // _finishPackageSetup()


    /**
     *
     * @return 	    void
     *
     * @version     1.6.0 / 2026-04-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    protected function _init(): void
    {
        parent::_init();

        $this->_composerJson = json_decode(file_get_contents(base_path('composer.json')), true);

        $this->_basePath               = str_replace('\\', '/', realpath(__DIR__.'/../../../'));
        $this->_firstRun               = !file_exists(config_path('project.php'));
        $this->_hasApi                 = file_exists(base_path('vendor/'.$this->_apiPackage.'/core/composer.json'));
        $this->_hasCodeGenerator       = file_exists(base_path('vendor/'.$this->_codeGeneratorPackage.'/composer.json'));
        $this->_hasCodeGeneratorLoader = file_exists(base_path('vendor/'.$this->_codeGeneratorLoaderPackage.'/composer.json'));
        $this->_hasHttpService         = file_exists(config_path('http-service.php'));
        $this->_hasLangPackage         = file_exists(base_path('vendor/laravel-lang/lang/composer.json'));//composer require --dev laravel-lang/lang
        $this->_hasMergePlugin         = file_exists(base_path('vendor/'.$this->_mergePlugin.'/composer.json'));
        $this->_hasPrivateGit          = isset($this->_composerJson['repositories'][$this->_privateGitName]);

        $this->_publishFiles = config('globals-setup.publish', []);

        $hasEnv   = file_exists(base_path('.env'));
        $hasEnvIn = file_exists(base_path('.env.in'));

        $this->_installationState = match (true) {
            $hasEnv  && $hasEnvIn  => self::STATE_CONFIGURED,
            $hasEnv  && !$hasEnvIn => self::STATE_FRESH,
            !$hasEnv && $hasEnvIn  => self::STATE_CHECKOUT,
            default                => self::STATE_INVALID,
        };

        if ($this->_installationState === self::STATE_INVALID) {
            $this->_Output->error('Neither .env nor .env.in found. Cannot determine installation state.');
        }
        $this->_initMissingLocales();

    } // _init()


    /**
     *
     * @return      void
     *
     * @version     1.0.0 / 2026-02-28
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _initMissingLocales()
    {
        foreach (config('globals-setup.locales') as $locale) {
            if (file_exists(lang_path($locale.'.json'))) {
                continue;
            }
            $this->_missingLocales[] = $locale;
        }
    } // _initMissingLocales()


    /**
     * @return      bool
     *
     * @version     1.0.0 / 2026-04-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _isDevMode(): bool
    {
        return $this->_installationMode === 'local' || $this->_installationMode === 'dev-server';

    } // _isDevMode()


    /**
     * @return      bool
     *
     * @version     1.0.0 / 2026-04-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _isProdMode(): bool
    {
        return $this->_installationMode === 'production' || $this->_installationMode === 'demo';

    } // _isProdMode()


    /**
     *
     * @return 	    void
     *
     * @version     1.1.0 / 2025-06-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _linkStorage(): void
    {
        /*
        **  Storage-Link erzeugen. */
        $this->_Output->caption('Link storage directory');

        if (file_exists(public_path('storage'))) {
            $this->_Output->bulletMessage('Storage link already exists');

            return;
        }
        Artisan::call('storage:link');

        $this->_Output->bulletMessage(Artisan::output());

    } // _linkStorage()


    /**
     *
     * @return 	    void
     *
     * @version     1.5.0 / 2026-04-20
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _prepareEnvFiles(): void
    {
        $envFile = base_path('.env');
        $inFile  = base_path('.env.in');

        if ($this->_installationMode !== 'local') {
            /*
            **  dev-server / production / demo: .env.in (committed template) → .env,
            **  then set APP_ENV/APP_DEBUG according to mode. */
            if (file_exists($envFile) === false) {
                $this->_Output->caption('Preparing configuration');
                $this->_Output->bulletMessage('Copy .env.in > .env');

                copy($inFile, $envFile);
            }
            [$appEnv, $appDebug] = match ($this->_installationMode) {
                'production' => ['production', 'false'],
                'demo'       => ['demo',       'false'],
                default      => ['local',      'true'],  // dev-server
            };

            EnvFile::load($envFile)
                ->set('APP_ENV',   $appEnv)
                ->set('APP_DEBUG', $appDebug)
                ->save();

            return;
        }

        /*
        **  Local: reorganize existing .env, copy to .env.in, append custom block. */
        if (file_exists($inFile) === false) {
            $this->_Output->caption('Preparing configuration');
            $this->_Output->bulletMessage('Prepare file .env');
            $this->_Output->bulletMessage('Copy .env > .env.in');

            EnvFile::load($envFile)->reorganize()->save();
            copy($envFile, $inFile);

            $this->_Output->bulletMessage('Extend file .env');

            $customBlock =
                "\n\n\n".'#[CUSTOM]'
                ."\n"
                ."\n".'CODE_GENERATOR_ACTIVE = '.($this->_requireCodeGenerator ? 'true' : 'false')
                ."\n".'CODE_GENERATOR_BUILD_ONLY_MODE = true'
                ."\n".'CODE_GENERATOR_CREATE_JSONAPI = '.($this->_requireApi ? 'true' : 'false')
                ."\n".'CODE_GENERATOR_SKIP_ROUTING = true'
                ."\n"
                ."\n".'GLOBAL_TENANT = Customer'
                ."\n";

            $inCustomBlock =
                "\n\n\n".'#[CUSTOM]'
                ."\n"
                ."\n".'CODE_GENERATOR_ACTIVE = false'
                ."\n".'CODE_GENERATOR_BUILD_ONLY_MODE = true'
                ."\n".'CODE_GENERATOR_CREATE_JSONAPI = '.($this->_requireApi ? 'true' : 'false')
                ."\n".'CODE_GENERATOR_SKIP_ROUTING = true'
                ."\n"
                ."\n".'GLOBAL_TENANT = Customer'
                ."\n";

            if ($this->_requireHttpService === true) {
                $customBlock   .= "\n".'HTTP_SERVICE_PROFILE_DEFAULT_BASE_URL = '."\n";
                $inCustomBlock .= "\n".'HTTP_SERVICE_PROFILE_DEFAULT_BASE_URL = '."\n";
            }
            file_put_contents($envFile, $customBlock, FILE_APPEND);

            $this->_Output->bulletMessage('Extend file .env.in');

            file_put_contents($inFile, $inCustomBlock, FILE_APPEND);

        } else {
            if ($this->_requireHttpService === true) {
                EnvFile::load($envFile)->insertInSection('HTTP_SERVICE_PROFILE_DEFAULT_BASE_URL', '', 'CUSTOM')->save();
                EnvFile::load($inFile)->insertInSection('HTTP_SERVICE_PROFILE_DEFAULT_BASE_URL', '', 'CUSTOM')->save();
            }
        }
    } // _prepareEnvFiles()




    /**
     *
     * @param       null|string $localPath
     *
     * @return 	    void
     *
     * @version     1.1.0 / 2026-04-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _registerPrivateGit(null|string $localPath = null): void
    {
        if ($this->_installationMode !== 'local') {
            return;
        }
        if ($this->_hasPrivateGit === true) {
            return;
        }
        $this->_Output->caption('Register private Git Repository');
        $this->_Output->bulletMessage('Update composer.json with private Git repository');

        $this->_runShellCommand([
            'composer',
            'config',
            'repositories.'.$this->_privateGitName,
            'composer',
            $this->_privateGitUrl
        ]);
    } // _registerPrivateGit()


    /**
     *
     * @return      void
     *
     * @version     1.0.0 / 2026-04-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _runMigrations(): void
    {
        /*
        **  Production: manual step only — never run migrations automatically. */
        if ($this->_installationMode === 'production') {
            $this->_Output->caption('Database migrations');
            $this->_Output->bulletMessage('Run `php artisan migrate` manually after deployment.');

            return;
        }

        $this->_Output->caption('Database migrations');

        /*
        **  Initial setup: always migrate:fresh --seed, no prompt. */
        $isInitial = $this->_installationState === self::STATE_FRESH
            || $this->_installationState === self::STATE_CHECKOUT;

        if ($isInitial === true) {
            $this->_runShellCommand([PHP_BINARY, base_path('artisan'), 'config:clear'], showOutput: true);
            $this->_runShellCommand([PHP_BINARY, base_path('artisan'), 'migrate:fresh', '--seed'], showOutput: true);

            return;
        }

        /*
        **  Subsequent run: let the user choose. Seeds only with migrate:fresh. */
        $choices = ['skip', 'migrate', 'migrate:fresh'];
        $choice  = (int) $this->_Prompt->choice('Run database migrations?', $choices, 0);

        if ($choice === 0) {
            return;
        }
        $this->_runShellCommand([PHP_BINARY, base_path('artisan'), 'config:clear'], showOutput: true);

        if ($choice === 1) {
            $this->_runShellCommand([PHP_BINARY, base_path('artisan'), 'migrate'], showOutput: true);
        } else {
            $this->_runShellCommand([PHP_BINARY, base_path('artisan'), 'migrate:fresh', '--seed'], showOutput: true);
        }
    } // _runMigrations()




    /**
     *
     * @return      void
     *
     * @version     1.1.0 / 2026-04-20
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _runProductionSetup(): void
    {
        /*
        **  Generate APP_KEY if missing. */
        $Env    = EnvFile::load(base_path('.env'));
        $appKey = $Env->get('APP_KEY');

        if ($appKey === '' || str_starts_with($appKey, 'base64:') === false) {
            $this->_Output->caption('Generate application key');

            Artisan::call('key:generate');

            $this->_Output->bulletMessage(trim(Artisan::output()));
        }

        /*
        **  Install dependencies without dev packages. */
        $this->_Output->caption('Install dependencies');
        $this->_Output->bulletMessage('Run `composer install --no-dev --optimize-autoloader`');

        $this->_runShellCommand([
            'composer',
            'install',
            '--no-dev',
            '--optimize-autoloader',
        ]);

        /*
        **  Build frontend assets. */
        $this->_Output->caption('Build frontend assets');
        $this->_Output->bulletMessage('Run `npm ci`');

        $this->_runShellCommand(['npm', 'ci']);

        $this->_Output->bulletMessage('Run `npm run build`');

        $this->_runShellCommand(['npm', 'run', 'build']);

        /*
        **  Optimize caches. */
        $this->_Output->caption('Optimize caches');
        $this->_Output->bulletMessage('config:cache');

        Artisan::call('config:cache');

        $this->_Output->bulletMessage('event:cache');

        Artisan::call('event:cache');

        $this->_Output->bulletMessage('route:cache');

        Artisan::call('route:cache');

        $this->_Output->bulletMessage('view:cache');

        Artisan::call('view:cache');

        /*
        **  Restart queue workers if not using the sync driver. */
        $queueConnection = $Env->get('QUEUE_CONNECTION', 'sync');

        if ($queueConnection !== 'sync') {
            if ($this->_Prompt->confirm('Restart queue workers?', true) === true) {
                $this->_Output->caption('Queue');

                Artisan::call('queue:restart');

                $this->_Output->bulletMessage(trim(Artisan::output()) ?: 'Broadcasting restart signal.');
            }
        }
    } // _runProductionSetup()




    /**
     *
     * @return      void
     *
     * @version     1.1.0 / 2026-04-20
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _setInstallationMode(): void
    {
        switch ($this->_installationState) {
            case self::STATE_CHECKOUT:
                $modeIndex = (int) $this->_Prompt->choice(
                    'Select installation mode:',
                    ['local', 'dev-server', 'production', 'demo'],
                    0
                );
                $this->_installationMode = ['local', 'dev-server', 'production', 'demo'][$modeIndex];

                break;

            case self::STATE_CONFIGURED:
                $appEnv = EnvFile::loadOrCreate(base_path('.env'))->get('APP_ENV', 'local');

                $this->_installationMode = match ($appEnv) {
                    'production' => 'production',
                    'demo'       => 'demo',
                    default      => 'local',
                };

                break;

            case self::STATE_FRESH:
            default:
                $this->_installationMode = 'local';

                break;
        }
    } // _setInstallationMode()


    /**
     *
     * @return 	    void
     *
     * @version     1.1.0 / 2026-04-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _setupApiPackage(): void
    {
        if ($this->_hasApi === true) {
            return;
        }
        if ($this->_firstRun === false) {
            return;
        }
        if ($this->_Prompt->confirm('Does the current project require an API?', true) === false) {
            return;
        }
        $this->_requireApi = true;

        $this->_Output->bulletMessage('Update composer.json with api-package ['.$this->_apiPackage.']');

        $this->_runShellCommand([
            'composer',
            'require',
            $this->_apiPackage,
            '--no-update', // '--no-install',
            // '--no-scripts',  '--no-autoloader',
        ]);
        $this->_installPackages++;

    } // _setupApiPackage()


    /**
     *
     * @return 	    void
     *
     * @version     2.0.0 / 2026-04-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _setupCodeGenerator(): void
    {
        if ($this->_hasCodeGenerator === true) {
            return;
        }
        $choice = (int) $this->_Prompt->choice(
            'Do you want to install the code generator?',
            ['no', 'yes (local)', 'yes (from Git)'],
            0
        );

        if ($choice === 0) {
            return;
        }
        $ComposerLocal = ComposerJson::loadOrCreate(base_path('composer.local.json'));

        $constraint = '*';

        if ($choice === 1) {
            $localPath = $this->_Prompt->ask(
                'Please enter the full path to the local repository',
                validator: function ($input) {
                    return file_exists($input.'/src/GeneratorServiceProvider.php');
                }
            );
            $this->_Output->bulletMessage('Update composer.local.json with local repository');

            $ComposerLocal->addRepository(
                $this->_codeGeneratorPackage,
                'path',
                str_replace('\\', '/', $localPath)
            );
            $constraint = '@dev';
        }
        $this->_Output->bulletMessage('Update composer.local.json with code-generator ['.$this->_codeGeneratorPackage.']');

        $ComposerLocal->addPackage($this->_codeGeneratorPackage, $constraint, dev: true);
        $ComposerLocal->save();

        $this->_installPackages++;

    } // _setupCodeGenerator()


    /**
     *
     * @return 	    void
     *
     * @version     1.0.0 / 2025-06-22
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _setupCodeGeneratorLoader(): void
    {
        if ($this->_hasCodeGeneratorLoader === true) {
            return;
        }
        $this->_Output->caption('Update composer.json with CodeGenerator Loader');

        $this->_Output->bulletMessage('Download package ['.$this->_codeGeneratorLoaderPackage.']');

        $this->_runShellCommand([
            'composer',
            'require',
            $this->_codeGeneratorLoaderPackage.':*',
            '--no-update', // '--no-install',
            // '--no-scripts',  '--no-autoloader',
        ]);
        $this->_installPackages++;

    } // _setupCodeGeneratorLoader()


    /**
     *
     * @return      void
     *
     * @version     1.0.0 / 2026-02-25
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _setupHttpService(): void
    {
        if ($this->_hasHttpService === false) {
            if ($this->_Prompt->confirm('Does the current project need to access an external API?', true) === true) {
                $this->_Output->bulletMessage('Publishing "config/http-service.php"');

                Artisan::call('vendor:publish', [
                    '--provider' => \BplanBase\Globals\PackageServiceProvider::class,
                    '--tag' => 'http-service',
                ]);
                $this->_requireHttpService = true;
            }
        }
    } // _setupHttpService()


    /**
     *
     * @return 	    void
     *
     * @version     1.0.0 / 2026-02-28
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _setupLangPackage(): void
    {
        if ($this->_hasLangPackage === true) {
            return;
        }
        $this->_Output->caption('Install language package');

        $this->_Output->bulletMessage('Download package [laravel-lang/lang]');

        $this->_runShellCommand([
            'composer',
            'require',
            'laravel-lang/lang',
            '--no-update', // '--no-install',
        ]);
        $this->_addLangUpdateCommand();

        $this->_installPackages++;

    } // _setupLangPackage()


    /**
     *
     * @return 	    void
     *
     * @version     1.3.0 / 2026-04-22
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _setupMergePlugin(): void
    {
        if ($this->_hasMergePlugin === true) {
            return;
        }
        $this->_Output->caption('Update composer.json with merge-plugin ['.$this->_mergePlugin.']');

        $this->_Output->bulletMessage('Configure "allow-plugins".');

        $this->_runShellCommand([
            'composer',
            'config',
            'allow-plugins.'.$this->_mergePlugin,
            'true',
        ]);
        $this->_Output->bulletMessage('Configure "extra.merge-plugin.include"');

        $this->_runShellCommand([
            'composer',
            'config',
            'extra.merge-plugin.ignore-duplicates',
            'false',
        ]);
        $this->_runShellCommand([
            'composer',
            'config',
            'extra.merge-plugin.include',
            '--json',
            '["composer.local.json"]',
        ]);
        $this->_Output->bulletMessage('Configure "extra.merge-plugin.merge-dev"');

        $this->_runShellCommand([
            'composer',
            'config',
            'extra.merge-plugin.replace',
            'true',
        ]);

        $this->_runShellCommand([
            'composer',
            'config',
            'extra.merge-plugin.ignore-duplicates',
            'false',
        ]);
        $this->_Output->bulletMessage('Download package ['.$this->_mergePlugin.']');

        $this->_runShellCommand([
            'composer',
            'require',
            $this->_mergePlugin,
            '--dev',
             '--no-update', // '--no-install',
        ]);
        $this->_installPackages++;

    } // _setupMergePlugin()


    /**
     * Registers the globals package Blade view paths in config/tailwind-content.json.
     *
     * Does nothing when the file does not exist or the path is already present.
     *
     * @return      void
     *
     * @version     1.0.0 / 2026-04-22
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _setupTailwindContent(): void
    {
        $contentFile = base_path('config/tailwind-content.json');

        if (file_exists($contentFile) === false) {
            return;
        }
        $globalsPath = './vendor/bplan-base/globals/resources/views/**/*.blade.php';

        $File = JsonFile::load($contentFile);

        if ($File->hasInArray('content', $globalsPath) === false) {
            $this->_Output->bulletMessage('Register globals views in tailwind-content.json');

            $File->addToArray('content', $globalsPath)->save();
        }
    } // _setupTailwindContent()


    /**
     *
     * @return      int
     *
     * @version     2.5.0 / 2026-04-22
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function handle(): int
    {
        $this->_init();

        if ($this->_installationState === self::STATE_INVALID) {
            return Command::FAILURE;
        }
        clear();

        $this->_Output->title('Globals Setup');

        $this->_setInstallationMode();

        $this->_Output->newline();
        $this->_Output->bulletMessage('Mode: '.$this->_installationMode.' | State: '.$this->_installationState);

        if ($this->_isDevMode() === true) {
            $this->_registerPrivateGit();
            $this->_setupApiPackage();
            $this->_setupHttpService();
            $this->_setupCodeGenerator();
            $this->_setupCodeGeneratorLoader();
            $this->_setupMergePlugin();
            $this->_setupLangPackage();
            $this->_prepareEnvFiles();
            $this->_finishPackageSetup();
        }

        $this->_linkStorage();
        $this->_publishFiles();
        $this->_setupTailwindContent();

        if ($this->_isProdMode() === true) {
            $this->_prepareEnvFiles();
        }
        $this->_configureDatabase();
        $this->_configureMail();

        $this->_runMigrations();

        if ($this->_isProdMode() === true) {
            $this->_runProductionSetup();
        }

        $this->_Output->print('Setup completed ✅', color: 'title', indent: 3, prependNewline: 2);

        return Command::SUCCESS;

    } // handle()


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


} // class GlobalsSetup extends Command {}
