<?php

declare(strict_types=1);

/**
 * 本文件属于 DBErp 进销存系统。
 *
 * 版权所有 © 2026 北京珑大钜商科技有限公司。保留所有权利。
 *
 * 未经官方授权，不得删除、篡改或隐藏本文件中的版权声明、产品标识与来源信息。
 * 商业授权、官方更新与技术支持服务，请以珑大官方支持中心记录为准。
 *
 * @link https://support.loongdom.cn
 * @see  https://www.dberp.com.cn
 */

namespace App\Support\Plugin;

use Composer\Autoload\ClassLoader;
use DBCore\Plugin\PluginManifestReader;

/**
 * 按 plugins 目录内 plugin.json 注册 PSR-4 autoload，避免每增插件改宿主 composer.json。
 */
final class PluginAutoloadRegistrar
{
    private static bool $registered = false;

    public static function register(?string $projectRoot = null): void
    {
        if (self::$registered) {
            return;
        }

        self::$registered = true;

        $root = $projectRoot ?? self::defaultProjectRoot();
        $pluginsRoot = self::resolvePluginsRoot($root);
        $loader = new ClassLoader;

        foreach (self::discoverPluginDirectories($pluginsRoot) as $pluginDirectory) {
            $mapping = self::resolvePsr4Mapping($pluginDirectory);

            if ($mapping === null) {
                continue;
            }

            [$namespace, $sourceDirectory] = $mapping;
            $loader->addPsr4($namespace, $sourceDirectory);
        }

        if ($loader->getPrefixesPsr4() !== []) {
            $loader->register(true);
        }
    }

    /**
     * @return list<string>
     */
    public static function discoverPluginDirectories(string $pluginsRoot): array
    {
        if (! is_dir($pluginsRoot)) {
            return [];
        }

        $directories = [];

        foreach (glob($pluginsRoot.DIRECTORY_SEPARATOR.'*', GLOB_ONLYDIR) ?: [] as $entry) {
            if (! is_string($entry)) {
                continue;
            }

            if (is_file($entry.DIRECTORY_SEPARATOR.PluginManifestReader::MANIFEST_FILE)) {
                $directories[] = self::normalizeDirectoryPath($entry);

                continue;
            }

            foreach (glob($entry.DIRECTORY_SEPARATOR.'*', GLOB_ONLYDIR) ?: [] as $nestedEntry) {
                if (! is_string($nestedEntry)) {
                    continue;
                }

                if (is_file($nestedEntry.DIRECTORY_SEPARATOR.PluginManifestReader::MANIFEST_FILE)) {
                    $directories[] = self::normalizeDirectoryPath($nestedEntry);
                }
            }
        }

        return $directories;
    }

    /**
     * @return array{0: non-empty-string, 1: non-empty-string}|null
     */
    public static function resolvePsr4Mapping(string $pluginDirectory): ?array
    {
        $pluginDirectory = self::normalizeDirectoryPath($pluginDirectory);
        $manifestPath = $pluginDirectory.DIRECTORY_SEPARATOR.PluginManifestReader::MANIFEST_FILE;

        if (! is_file($manifestPath)) {
            return null;
        }

        $decoded = json_decode((string) file_get_contents($manifestPath), true);

        if (! is_array($decoded)) {
            return null;
        }

        $namespace = PluginAutoloadNamespaceResolver::resolve($decoded);

        if ($namespace === null) {
            return null;
        }

        $sourceDirectory = self::normalizeDirectoryPath($pluginDirectory.DIRECTORY_SEPARATOR.'src');

        if (! is_dir($sourceDirectory)) {
            return null;
        }

        return [$namespace, $sourceDirectory];
    }

    private static function defaultProjectRoot(): string
    {
        return dirname(__DIR__, 3);
    }

    private static function resolvePluginsRoot(string $projectRoot): string
    {
        $configured = getenv('DBERP_PLUGIN_PATH');

        if (! is_string($configured) || trim($configured) === '') {
            $configured = getenv('DBCORE_PLUGIN_PATH');
        }

        $configured = is_string($configured) ? trim($configured) : '';

        if ($configured === '') {
            $configured = 'plugins';
        }

        if (self::isAbsolutePath($configured)) {
            return rtrim($configured, '\\/');
        }

        return rtrim($projectRoot, '\\/').DIRECTORY_SEPARATOR.str_replace(['/', '\\'], DIRECTORY_SEPARATOR, trim($configured, '\\/'));
    }

    private static function isAbsolutePath(string $path): bool
    {
        if (str_starts_with($path, '/')) {
            return true;
        }

        return preg_match('/^[A-Za-z]:[\\\\\/]/', $path) === 1;
    }

    private static function normalizeDirectoryPath(string $directory): string
    {
        $resolved = realpath($directory);

        if ($resolved !== false) {
            return $resolved;
        }

        return rtrim(str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $directory), DIRECTORY_SEPARATOR);
    }
}
