<?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\PluginManifestReader;
use Throwable;

/**
 * 从插件目录回读 plugin.json，补全 DBCore 扫描未透传的 DBErp 官方扩展字段。
 */
final class HostPluginManifestFileReader
{
    /**
     * @var list<string>
     */
    private const DISK_MERGE_KEYS = [
        'navigation',
        'feature_entries',
        'permissions',
        'display_name',
        'product',
        'type',
        'vendor',
        'support_center',
        'requires',
    ];

    public function __construct(
        private readonly DberpPluginDiscoveryManager $discoveryManager,
        private readonly DberpOfficialPluginGuard $officialPluginGuard,
    ) {}

    /**
     * @return array<string, mixed>|null
     */
    public function enriched(string $identifier): ?array
    {
        $manifest = $this->discoveryManager->find($identifier);
        $fromDisk = $this->readFromDisk($identifier, $manifest);

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

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

        foreach (self::DISK_MERGE_KEYS as $key) {
            if ($this->shouldMergeKey($manifest, $fromDisk, $key)) {
                $manifest[$key] = $fromDisk[$key];
            }
        }

        return $manifest;
    }

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

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

        $pluginPath = $this->resolvePluginPath($normalized, $discovered);
        $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;
        }
    }

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

            if ($pluginPath !== '') {
                return $pluginPath;
            }
        }

        $flat = $this->officialPluginGuard->officialRootPath().DIRECTORY_SEPARATOR.$identifier;

        if (is_dir($flat)) {
            return $flat;
        }

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

            $candidate = $vendorDirectory.DIRECTORY_SEPARATOR.$identifier;

            if (is_dir($candidate)) {
                return $candidate;
            }
        }

        return '';
    }

    /**
     * @param  array<string, mixed>  $manifest
     * @param  array<string, mixed>  $fromDisk
     */
    private function shouldMergeKey(array $manifest, array $fromDisk, string $key): bool
    {
        if (! array_key_exists($key, $fromDisk)) {
            return false;
        }

        $diskValue = $fromDisk[$key];

        if ($diskValue === null) {
            return false;
        }

        if (is_array($diskValue) && $diskValue === []) {
            return false;
        }

        if (is_string($diskValue) && trim($diskValue) === '') {
            return false;
        }

        if (! array_key_exists($key, $manifest)) {
            return true;
        }

        $current = $manifest[$key];

        if (is_array($current) && $current === []) {
            return true;
        }

        if (is_string($current) && trim($current) === '') {
            return true;
        }

        return false;
    }
}
