<?php
/**
 * Helper Class
 *
 * @todo        Caching von "app.env" abhängig machen und einen Mechanismus für die automatische
 *              Aktualisierung des Cache entwickeln.
 *
 * @version     1.0.$Revision:$
 * @version     SVN: $Id:$
 * @package     bplan-base/globals
 * @subpackage  Helpers
 * @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\Support;


use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;
use Livewire\Livewire;


/**
 * Helper Class
 *
 * @version     3.0.0 / 2026-05-03
 * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
 */
class LivewireComponentAutoDiscovery
{


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


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


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


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


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


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


    /**
     * Livewire 3 / Laravel 12 discovery.
     *
     * addNamespace() is not available, so all components — prefixed or not — are registered
     * individually via Livewire::component(). Prefixed components receive the '<prefix>::' alias.
     *
     * @param       string $namespace
     *
     * @param       string $basePath
     *
     * @param       null|string $prefix
     *
     * @return      void
     *
     * @version     1.0.0 / 2026-05-03
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    protected static function _discoverL12(string $namespace, string $basePath, ?string $prefix): void
    {
        static::_registerComponents($namespace, $basePath, $prefix);

    } // _discoverL12()


    /**
     * Livewire 4 / Laravel 13+ discovery.
     *
     * For namespaced packages (prefix given): registers the namespace via addNamespace() so that
     * resolveClassComponentClassName() can resolve namespaced component names (containing '::').
     * Livewire::component() writes to classComponents which is never checked for '::' names.
     *
     * For non-prefixed components: enumerates files and calls Livewire::component() for each.
     *
     * @param       string $namespace
     *
     * @param       string $basePath
     *
     * @param       null|string $prefix
     *
     * @return      void
     *
     * @version     1.0.0 / 2026-05-03
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    protected static function _discoverL13(string $namespace, string $basePath, ?string $prefix): void
    {
        if ($prefix !== null) {
            Livewire::addNamespace($prefix, classNamespace: $namespace);

            return;
        }
        static::_registerComponents($namespace, $basePath);

    } // _discoverL13()


    /**
     * Enumerates PHP files under $basePath and registers each with Livewire::component().
     * When $prefix is given the alias is '<prefix>::<kebab.path>', otherwise '<kebab.path>'.
     *
     * @param       string $namespace
     *
     * @param       string $basePath
     *
     * @param       null|string $prefix
     *
     * @return      void
     *
     * @version     1.0.0 / 2026-05-03
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    protected static function _registerComponents(string $namespace, string $basePath, ?string $prefix = null): void
    {
        $components = [];

        foreach (File::allFiles($basePath) as $File) {
            $fileName = $File->getFilename();
            /*
            **  Ignore files whose name begins with a dot or a hash symbol. */
            if ($fileName[0] === '.' || $fileName[0] === '#') {
                continue;
            }

            $fullPath        = str_replace('\\', '/', $File->getPathname());
            $relativePath    = Str::after($fullPath, $basePath.'/');
            $relativeClassPath = Str::replaceLast('.php', '', $relativePath);

            $class     = $namespace.'\\'.str_replace('/', '\\', $relativeClassPath);
            $pathParts = explode('/', $relativeClassPath);
            $kebab     = implode('.', array_map([Str::class, 'kebab'], $pathParts));

            $alias = $prefix !== null ? $prefix.'::'.$kebab : $kebab;

            $components[$alias] = $class;
        }

        foreach ($components as $alias => $class) {
            Livewire::component($alias, $class);
        }

    } // _registerComponents()


    /**
     * Write the cache file
     *
     * @param       string $cacheFile
     *
     * @param       array $data
     *
     * @version     1.0.0 / 2025-04-22
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    protected static function _writeCache(string $cacheFile, array $data): void
    {
        File::ensureDirectoryExists(dirname($cacheFile));

        $content = '<?php'
            ."\n\n"
            .'return '.var_export($data, true)
            .";\n";

        file_put_contents($cacheFile, $content);

    } // _writeCache()


    /**
     * Discovers all Livewire components under the given path.
     * Delegates to _discoverL13() when addNamespace() is available (Livewire 4 / Laravel 13+),
     * otherwise falls back to _discoverL12().
     *
     * @param       string $baseNamespace
     *
     * @param       string $basePath
     *
     * @param       null|string $prefix
     *
     * @return      void
     *
     * @version     1.1.0 / 2026-05-03
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public static function discover(string $baseNamespace, string $basePath, null|string $prefix = null): void
    {
        $basePath  = str_replace('\\', '/', realpath($basePath));
        $namespace = rtrim($baseNamespace, '\\');

        if (empty($basePath)) {
            return;
        }

        if (method_exists(App::make('livewire'), 'addNamespace')) {
            static::_discoverL13($namespace, $basePath, $prefix);
        } else {
            static::_discoverL12($namespace, $basePath, $prefix);
        }
    } // discover()


} // class LivewireComponentAutoDiscovery {}
