<?php

declare(strict_types=1);

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

namespace App\Services\Plugin;

use DBCore\Plugin\Enums\PluginStatus;
use DBCore\Plugin\PluginManifestReader;
use Illuminate\Support\Facades\Schema;
use Throwable;

/**
 * 从已安装官方插件 manifest 汇总动态权限定义，供权限目录同步。
 */
final class HostPluginManifestPermissionCatalog
{
    public function __construct(
        private readonly DberpDatabasePluginRepository $repository,
        private readonly DberpPluginDiscoveryManager $discoveryManager,
        private readonly HostPluginPermissionResolver $permissionResolver,
        private readonly DberpOfficialPluginGuard $officialPluginGuard,
    ) {}

    /**
     * @return array<string, array{code: string, display_name: string, guard_name: string, module: string, navigation_group: string, description: string, sort_order: int, is_system: bool, default_allow_when_unconfigured: bool}>
     */
    public function records(): array
    {
        if (! $this->pluginTableReady()) {
            return [];
        }

        try {
            $this->discoveryManager->scan();
        } catch (Throwable) {
            return [];
        }

        $records = [];

        foreach ($this->repository->all() as $state) {
            if (! $state->isInstalled()) {
                continue;
            }

            if ($state->getStatus() === PluginStatus::Uninstalled->value) {
                continue;
            }

            $identifier = trim($state->getName());

            if ($identifier === '') {
                continue;
            }

            $manifest = $this->manifestForPermissions($identifier);

            foreach ($this->permissionResolver->permissionDefinitionsFromManifest($identifier, $manifest) as $code => $definition) {
                $records[$code] = $definition;
            }
        }

        return $records;
    }

    private function pluginTableReady(): bool
    {
        try {
            return Schema::hasTable('core_plugins');
        } catch (Throwable) {
            return false;
        }
    }

    /**
     * DBCore 扫描结果可能不含 permissions，需回读插件目录内 manifest 以同步声明权限。
     *
     * @return array<string, mixed>|null
     */
    private function manifestForPermissions(string $identifier): ?array
    {
        $manifest = $this->discoveryManager->find($identifier);

        if (! is_array($manifest)) {
            return $this->readInstalledPluginManifest($identifier);
        }

        $fromDisk = $this->readInstalledPluginManifest($identifier);

        if (is_array($manifest['permissions'] ?? null) && $manifest['permissions'] !== []) {
            return $this->mergeManifestPermissionMetadata($manifest, $fromDisk);
        }

        if ($fromDisk === null) {
            return $manifest;
        }

        if (is_array($fromDisk['permissions'] ?? null) && $fromDisk['permissions'] !== []) {
            $manifest['permissions'] = $fromDisk['permissions'];
        }

        return $this->mergeManifestPermissionMetadata($manifest, $fromDisk);
    }

    /**
     * @param  array<string, mixed>  $manifest
     * @param  array<string, mixed>|null  $fromDisk
     * @return array<string, mixed>
     */
    private function mergeManifestPermissionMetadata(array $manifest, ?array $fromDisk): array
    {
        if ($fromDisk === null) {
            return $manifest;
        }

        if (is_array($fromDisk['permission_labels'] ?? null) && $fromDisk['permission_labels'] !== []) {
            $manifest['permission_labels'] = $fromDisk['permission_labels'];
        }

        if (trim((string) ($manifest['display_name'] ?? '')) === '' && is_string($fromDisk['display_name'] ?? null)) {
            $manifest['display_name'] = $fromDisk['display_name'];
        }

        if (trim((string) ($manifest['name'] ?? '')) === '' && is_string($fromDisk['name'] ?? null)) {
            $manifest['name'] = $fromDisk['name'];
        }

        return $manifest;
    }

    /**
     * @return array<string, mixed>|null
     */
    private function readInstalledPluginManifest(string $identifier): ?array
    {
        $normalized = trim($identifier);

        if ($normalized === '') {
            return null;
        }

        $discovered = $this->discoveryManager->find($normalized);
        $pluginPath = '';

        if (is_array($discovered)) {
            /** @var array<string, mixed> $extra */
            $extra = is_array($discovered['extra'] ?? null) ? $discovered['extra'] : [];
            $pluginPath = trim((string) ($extra['path'] ?? ''));
        }

        if ($pluginPath === '') {
            $flat = $this->officialPluginGuard->officialRootPath().DIRECTORY_SEPARATOR.$normalized;

            if (is_dir($flat)) {
                $pluginPath = $flat;
            } else {
                foreach (glob($this->officialPluginGuard->officialRootPath().DIRECTORY_SEPARATOR.'*', GLOB_ONLYDIR) ?: [] as $vendorDirectory) {
                    if (! is_string($vendorDirectory)) {
                        continue;
                    }

                    $candidate = $vendorDirectory.DIRECTORY_SEPARATOR.$normalized;

                    if (is_dir($candidate)) {
                        $pluginPath = $candidate;

                        break;
                    }
                }
            }
        }

        $manifestPath = $pluginPath.DIRECTORY_SEPARATOR.PluginManifestReader::MANIFEST_FILE;

        if ($pluginPath === '' || ! is_file($manifestPath)) {
            return null;
        }

        try {
            $decoded = json_decode((string) file_get_contents($manifestPath), true, 512, JSON_THROW_ON_ERROR);

            return is_array($decoded) ? $decoded : null;
        } catch (Throwable) {
            return null;
        }
    }
}
