<?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\Console\BaseSetup;
use BplanBase\Globals\Setup\FilePublisher;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan;


use function Laravel\Prompts\clear;


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


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


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


/* +++ 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     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()


    /**
     *
     * @return 	    void
     *
     * @version     1.1.0 / 2026-02-28
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _finishPackageSetup(): void
    {
        if ($this->_installPackages > 0) {
            $this->_Output->caption('Package installation');
            $this->_Output->print('Run `composer update`', bullet: true);

            $this->_runShellCommand([
                'composer',
                'update',
            ]);
            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.5.0 / 2026-02-28
     * @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', []);

        $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 	    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.3.0 / 2026-02-25
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _prepareEnvFiles()
    {
        $envFile = base_path('.env');
        $inFile = base_path('.env.in');

        if (file_exists($inFile) === false) {
            $this->_Output->caption('Preparing configuration');
            $this->_Output->bulletMessage('Prepare file .env');

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

            $envFilecontents = $this->_reorganizeEnvFilecontents();

            file_put_contents($envFile, rtrim($envFilecontents));

            copy($envFile, $inFile);

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

            $envFileContents =
                "\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";

            $inFileContents = "\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";

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

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

            file_put_contents($inFile, $inFileContents, FILE_APPEND);

        } else {
            if ($this->_requireHttpService === true) {
                // @todo    Beim nachträglichen Hinzufügen der Service-Klasse sollte auch die .env-Datei erweitert werden.
            }
        }
    } // _prepareEnvFiles()


    /**
     *
     * @param       null|string $localPath
     *
     * @return 	    void
     *
     * @version     1.0.0 / 2025-06-22
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _registerPrivateGit(null|string $localPath = null): void
    {
        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 	    string
     *
     * @version     1.0.0 / 2025-07-22
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _reorganizeEnvFilecontents(): string
    {
        /*
        **  Disassemble .env file. */
        $lines = file(base_path('.env'));

        $result = [];

        foreach ($lines as $line) {
            $line = trim($line);

            if ($line === '') {
                continue;
            }
            $comment = false;
            $quote = false;

            if ($line[0] === '#') {
                $comment = true;
                $line = trim(substr($line, 1));
            }
            /*
            **  Dismantle a single line. */
            $parts = explode('=', $line);

            $key = trim($parts[0]);

            if (!isset($parts[1])) {
                $value = false;

            } else {
                $value = trim($parts[1]);

                if ($value !== '' && $value[0] === '"') {
                    $quote = true;
                }
            }
            /*
            **  Get the key prefix. */
            $keyParts = explode('_', $key, 2);

            $prefix = $keyParts[0];

            if (!isset($result[$prefix])) {
                $result[$prefix] = [];
            }
            $result[$prefix][$key] = [
                'comment' => $comment,
                'key' => $key,
                'line' => $line,
                'quote' => $quote,
                'value' => $value,
            ];
        }
        /*
        **  Assemble the filecontents. */
        $filecontents = '#[GLOBAL]'."\n";

        ksort($result);

        foreach ($result as $settings) {
            ksort($settings);

            $separator = '';

            foreach ($settings as $setting) {
                $comment = '';

                if ($setting['comment'] === true) {
                    $comment = '# ';
                }
                if ($setting['value'] === false) {
                    $filecontents .= $separator.$comment.$setting['line'];

                    continue;
                }
                $filecontents .= $separator.$comment.rtrim($setting['key'].' = '.$setting['value']);
                $separator = "\n";
            }
            $filecontents .= "\n\n";
        }
        return $filecontents;

    } // _reorganizeEnvFilecontents()


    /**
     *
     * @return 	    void
     *
     * @version     1.0.0 / 2025-06-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _setupApiPackage(): void
    {
        if ($this->_hasApi === true) {
            return;
        }
        $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()


    /**
     *
     * @param       null|string $localPath
     *
     * @return 	    void
     *
     * @version     1.0.1 / 2025-06-22
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _setupCodeGenerator(null|string $localPath = null): void
    {

        if ($this->_hasCodeGenerator === true) {
            return;
        }
        if ($localPath !== null) {
            $this->_Output->bulletMessage('Update composer.json with local repository');

            $package = $this->_codeGeneratorPackage;

            $this->_runShellCommand([
                'composer',
                'config',
                'repositories.'.$this->_codeGeneratorPackage,
                'path',
                str_replace('\\', '/', $localPath)
            ]);
            $package .= ':@dev';

            $this->_Output->bulletMessage('Update composer.json with code-generator ['.$this->_codeGeneratorPackage.']');

        } else {
            $this->_Output->bulletMessage('Download package ['.$this->_codeGeneratorPackage.']');
        }
        $this->_runShellCommand([
            'composer',
            'require',
            $package,
            '--dev',
            '--no-update', // '--no-install',
            // '--no-scripts',  '--no-autoloader',
        ]);
        $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.2.0 / 2025-06-19
     * @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.include',
            '--json',
            '["composer.local.json"]',
        ]);
        $this->_Output->bulletMessage('Configure "extra.merge-plugin.merge-dev"');

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

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

    } // _setupMergePlugin()


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

        clear();

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

        $this->_registerPrivateGit();

        if ($this->_firstRun === true && $this->_Prompt->confirm('Does the current project require an API?', true)) {
            $this->_requireApi = true;

            $this->_setupApiPackage();
        }
        $this->_setupHttpService();

        if ($this->_hasCodeGenerator === false) {
            $setupCodeGenerator = (int) $this->_Prompt->choice('Do you want to install the code generator?', ['no', 'yes (local)', 'yes (from Git)'], 0);

            $codeGeneratorPath = null;

            switch ($setupCodeGenerator) {
                case 1:
                    $codeGeneratorPath = $this->_Prompt->ask('Please enter the full path to the local repository', validator: function ($input) {
                        return file_exists($input.'/src/GeneratorServiceProvider.php');
                    });
                    // no break

                case 2:
                    $this->_setupCodeGenerator($codeGeneratorPath);
            }
        }
        $this->_setupCodeGeneratorLoader();
        $this->_setupMergePlugin();
        $this->_linkStorage();
        $this->_publishFiles();
        $this->_prepareEnvFiles();
        $this->_setupLangPackage();

        $this->_finishPackageSetup();


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

        return Command::SUCCESS;

    } // handle()


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


} // class GlobalsSetup extends Command {}
