<?php

declare(strict_types=1);

/**
 * 站台外掛／插件註冊與開關（fate/data/fate_plugins.json、data/fate_plugins_registry.json）。
 * LITE 版不含 forum/ 時視為不支援外掛；完整版以 forum/bootstrap.php 是否存在判定。
 * 自訂外掛可由 admin/plugins.php 表單寫入 registry JSON（僅完整版）。
 */

if (!function_exists('fate_plugins_fate_root')) {
    function fate_plugins_fate_root(): string
    {
        return dirname(__DIR__);
    }
}

if (!function_exists('fate_plugins_forum_bootstrap_path')) {
    function fate_plugins_forum_bootstrap_path(): string
    {
        return fate_plugins_fate_root() . DIRECTORY_SEPARATOR . 'forum' . DIRECTORY_SEPARATOR . 'bootstrap.php';
    }
}

if (!function_exists('fate_plugins_feature_available')) {
    /**
     * 是否為完整版（含 forum/bootstrap.php）。
     * LITE 版無此檔案時，外掛管理、註冊表與討論區相關功能一律不開放。
     */
    function fate_plugins_feature_available(): bool
    {
        static $cached = null;
        if ($cached !== null) {
            return $cached;
        }

        return $cached = is_file(fate_plugins_forum_bootstrap_path());
    }
}

if (!function_exists('fate_plugins_path')) {
    function fate_plugins_path(): string
    {
        return fate_plugins_fate_root() . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'fate_plugins.json';
    }
}

if (!function_exists('fate_plugins_registry_path')) {
    function fate_plugins_registry_path(): string
    {
        return fate_plugins_fate_root() . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'fate_plugins_registry.json';
    }
}

if (!function_exists('fate_plugins_builtin_registry')) {
    /**
     * 內建外掛（隨站台提供，不可由後台刪除）。
     *
     * @return array<string, array{
     *   label:string,
     *   description:string,
     *   default_enabled:bool,
     *   install_path:string,
     *   admin_href:?string,
     *   public_href:?string
     * }>
     */
    function fate_plugins_builtin_registry(): array
    {
        $root = fate_plugins_fate_root();

        return [
            'forum' => [
                'label' => '討論區',
                'description' => '店家與技師討論串、會員發文與回覆；可於論壇管理後台審核檢舉與版區。',
                'default_enabled' => true,
                'install_path' => 'forum/bootstrap.php',
                'admin_href' => '../forum/admin/index.php',
                'public_href' => '../forum/index.php',
            ],
        ];
    }
}

if (!function_exists('fate_plugins_custom_registry_read')) {
    /** @return array<string, array<string, mixed>> */
    function fate_plugins_custom_registry_read(): array
    {
        $p = fate_plugins_registry_path();
        if (!is_file($p)) {
            return [];
        }
        $raw = file_get_contents($p);
        if ($raw === false || trim($raw) === '') {
            return [];
        }
        $j = json_decode($raw, true);
        if (!is_array($j) || !isset($j['plugins']) || !is_array($j['plugins'])) {
            return [];
        }
        $out = [];
        foreach ($j['plugins'] as $id => $def) {
            if (!is_string($id) || !is_array($def)) {
                continue;
            }
            $out[$id] = $def;
        }

        return $out;
    }
}

if (!function_exists('fate_plugins_custom_registry_persist')) {
    /** @param array<string, array<string, mixed>> $plugins */
    function fate_plugins_custom_registry_persist(array $plugins): bool
    {
        $dir = dirname(fate_plugins_registry_path());
        if (!is_dir($dir) && !mkdir($dir, 0755, true) && !is_dir($dir)) {
            return false;
        }
        $payload = ['plugins' => $plugins];
        $json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
        if ($json === false) {
            return false;
        }

        return file_put_contents(fate_plugins_registry_path(), $json . "\n", LOCK_EX) !== false;
    }
}

if (!function_exists('fate_plugins_validate_id')) {
    function fate_plugins_validate_id(string $id): ?string
    {
        $id = strtolower(trim($id));
        if ($id === '' || preg_match('/^[a-z][a-z0-9_]{1,31}$/', $id) !== 1) {
            return '識別碼須為 2–32 字的小寫英文、數字或底線，且以英文字母開頭。';
        }

        return null;
    }
}

if (!function_exists('fate_plugins_validate_path_rel')) {
    function fate_plugins_validate_path_rel(string $path, string $fieldLabel = '路徑'): ?string
    {
        $path = str_replace('\\', '/', trim($path));
        $path = ltrim($path, '/');
        if ($path === '') {
            return $fieldLabel . '不可空白。';
        }
        if (str_contains($path, '..') || preg_match('#^[a-zA-Z0-9_./-]+$#', $path) !== 1) {
            return $fieldLabel . '格式不正確（請使用英文路徑，勿含 ..）。';
        }
        if (!str_ends_with(strtolower($path), '.php')) {
            return $fieldLabel . '須指向 .php 檔案（例如 forum/bootstrap.php）。';
        }

        return null;
    }
}

if (!function_exists('fate_plugins_validate_href_rel')) {
    function fate_plugins_validate_href_rel(string $href): ?string
    {
        $href = trim($href);
        if ($href === '') {
            return null;
        }
        if (preg_match('#^(\.\./)?[a-zA-Z0-9_./?=&%-]+$#', $href) !== 1) {
            return '連結格式不正確（請使用相對路徑，例如 ../forum/index.php）。';
        }

        return null;
    }
}

if (!function_exists('fate_plugins_resolve_install_path')) {
    function fate_plugins_resolve_install_path(string $pathRel): string
    {
        $pathRel = str_replace('\\', '/', ltrim(trim($pathRel), '/'));

        return fate_plugins_fate_root() . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $pathRel);
    }
}

if (!function_exists('fate_plugins_install_path_to_rel')) {
    function fate_plugins_install_path_to_rel(string $path): string
    {
        $path = str_replace('\\', '/', $path);
        $root = str_replace('\\', '/', fate_plugins_fate_root());
        if (str_starts_with($path, $root . '/')) {
            return ltrim(substr($path, strlen($root)), '/');
        }

        return ltrim($path, '/');
    }
}

if (!function_exists('fate_plugins_normalize_registry_entry')) {
    /**
     * @param array<string, mixed> $raw
     * @return array{ok:bool,error:?string,entry:?array{
     *   label:string,
     *   description:string,
     *   default_enabled:bool,
     *   install_path:string,
     *   admin_href:?string,
     *   public_href:?string
     * }}
     */
    function fate_plugins_normalize_registry_entry(array $raw): array
    {
        $label = trim((string) ($raw['label'] ?? ''));
        if ($label === '') {
            return ['ok' => false, 'error' => '請填寫顯示名稱。', 'entry' => null];
        }
        if (mb_strlen($label, 'UTF-8') > 40) {
            return ['ok' => false, 'error' => '顯示名稱最多 40 字。', 'entry' => null];
        }

        $description = trim((string) ($raw['description'] ?? ''));
        if (mb_strlen($description, 'UTF-8') > 400) {
            return ['ok' => false, 'error' => '說明最多 400 字。', 'entry' => null];
        }

        $installRel = str_replace('\\', '/', trim((string) ($raw['install_path'] ?? '')));
        $pathErr = fate_plugins_validate_path_rel($installRel, '安裝檢查檔案');
        if ($pathErr !== null) {
            return ['ok' => false, 'error' => $pathErr, 'entry' => null];
        }

        $adminHref = trim((string) ($raw['admin_href'] ?? ''));
        $publicHref = trim((string) ($raw['public_href'] ?? ''));
        foreach ([['管理連結', $adminHref], ['前台連結', $publicHref]] as [$labelName, $href]) {
            $hrefErr = fate_plugins_validate_href_rel($href);
            if ($hrefErr !== null) {
                return ['ok' => false, 'error' => $labelName . '：' . $hrefErr, 'entry' => null];
            }
        }

        return [
            'ok' => true,
            'error' => null,
            'entry' => [
                'label' => $label,
                'description' => $description,
                'default_enabled' => !empty($raw['default_enabled']),
                'install_path' => $installRel,
                'admin_href' => $adminHref !== '' ? $adminHref : null,
                'public_href' => $publicHref !== '' ? $publicHref : null,
            ],
        ];
    }
}

if (!function_exists('fate_plugins_is_builtin')) {
    function fate_plugins_is_builtin(string $id): bool
    {
        return array_key_exists($id, fate_plugins_builtin_registry());
    }
}

if (!function_exists('fate_plugins_registry')) {
    /**
     * 已登記的外掛清單（內建 + data/fate_plugins_registry.json）。
     *
     * @return array<string, array{
     *   label:string,
     *   description:string,
     *   default_enabled:bool,
     *   install_path:string,
     *   admin_href:?string,
     *   public_href:?string
     * }>
     */
    function fate_plugins_registry(): array
    {
        if (!fate_plugins_feature_available()) {
            return [];
        }
        $merged = fate_plugins_builtin_registry();
        foreach (fate_plugins_custom_registry_read() as $id => $def) {
            if (!is_array($def) || fate_plugins_is_builtin($id)) {
                continue;
            }
            $norm = fate_plugins_normalize_registry_entry(array_merge(['label' => $id], $def));
            if ($norm['ok'] && is_array($norm['entry'])) {
                $merged[$id] = $norm['entry'];
            }
        }

        return $merged;
    }
}

if (!function_exists('fate_plugins_read')) {
    /** @return array<string, mixed> */
    function fate_plugins_read(): array
    {
        $p = fate_plugins_path();
        if (!is_file($p)) {
            return [];
        }
        $raw = file_get_contents($p);
        if ($raw === false || trim($raw) === '') {
            return [];
        }
        $j = json_decode($raw, true);

        return is_array($j) ? $j : [];
    }
}

if (!function_exists('fate_plugins_normalize')) {
    /**
     * @param array<string, mixed> $merge
     * @return array{plugins: array<string, array{enabled:bool}>}
     */
    function fate_plugins_normalize(array $merge): array
    {
        $out = ['plugins' => []];
        $registry = fate_plugins_registry();
        $rawPlugins = isset($merge['plugins']) && is_array($merge['plugins']) ? $merge['plugins'] : [];

        foreach ($registry as $id => $def) {
            $enabled = (bool) ($def['default_enabled'] ?? false);
            if (isset($rawPlugins[$id]) && is_array($rawPlugins[$id]) && array_key_exists('enabled', $rawPlugins[$id])) {
                $enabled = !empty($rawPlugins[$id]['enabled']);
            } elseif ($id === 'forum') {
                $legacy = fate_plugins_forum_legacy_enabled();
                if ($legacy !== null) {
                    $enabled = $legacy;
                }
            }
            $out['plugins'][$id] = ['enabled' => $enabled];
        }

        return $out;
    }
}

if (!function_exists('fate_plugins_forum_legacy_enabled')) {
    /** 舊版 data/forum_config.json 的 enabled（無 fate_plugins.json 時沿用）。 */
    function fate_plugins_forum_legacy_enabled(): ?bool
    {
        $p = fate_plugins_fate_root() . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'forum_config.json';
        if (!is_file($p)) {
            return null;
        }
        $raw = file_get_contents($p);
        if ($raw === false || trim($raw) === '') {
            return null;
        }
        $j = json_decode($raw, true);
        if (!is_array($j) || !array_key_exists('enabled', $j)) {
            return null;
        }

        return !empty($j['enabled']);
    }
}

if (!function_exists('fate_plugins_persist')) {
    /** @param array{plugins: array<string, array{enabled:bool}>} $data */
    function fate_plugins_persist(array $data): bool
    {
        $dir = dirname(fate_plugins_path());
        if (!is_dir($dir) && !mkdir($dir, 0755, true) && !is_dir($dir)) {
            return false;
        }
        $json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
        if ($json === false) {
            return false;
        }

        return file_put_contents(fate_plugins_path(), $json . "\n", LOCK_EX) !== false;
    }
}

if (!function_exists('fate_plugin_definition')) {
    /** @return array{label:string,description:string,default_enabled:bool,install_path:string,admin_href:?string,public_href:?string}|null */
    function fate_plugin_definition(string $id): ?array
    {
        $registry = fate_plugins_registry();

        return $registry[$id] ?? null;
    }
}

if (!function_exists('fate_plugin_is_installed')) {
    function fate_plugin_is_installed(string $id): bool
    {
        $def = fate_plugin_definition($id);
        if ($def === null) {
            return false;
        }
        $path = (string) ($def['install_path'] ?? '');
        if ($path === '') {
            return false;
        }
        if (!str_contains($path, DIRECTORY_SEPARATOR) && str_contains($path, '/')) {
            $path = fate_plugins_resolve_install_path($path);
        } elseif (!preg_match('#^[A-Za-z]:\\\\|^/#', $path)) {
            $path = fate_plugins_resolve_install_path($path);
        }

        return is_file($path);
    }
}

if (!function_exists('fate_store_forum_plugin_active')) {
    /** 店家頁：討論區外掛是否已安裝且啟用。 */
    function fate_store_forum_plugin_active(): bool
    {
        static $cached = null;
        if ($cached !== null) {
            return $cached;
        }
        $boot = fate_plugins_forum_bootstrap_path();
        if (!is_file($boot)) {
            return $cached = false;
        }

        return $cached = fate_plugin_is_enabled('forum');
    }
}

if (!function_exists('fate_forum_module_installed')) {
    /** 論壇模組是否存在（LITE 版無 forum/bootstrap.php 時為 false）。 */
    function fate_forum_module_installed(): bool
    {
        return fate_plugins_feature_available();
    }
}

if (!function_exists('fate_plugins_any_installed')) {
    /** 完整版且至少有一個外掛目錄已部署（供後台是否顯示外掛管理）。 */
    function fate_plugins_any_installed(): bool
    {
        if (!fate_plugins_feature_available()) {
            return false;
        }
        foreach (fate_plugins_registry() as $id => $_) {
            if (fate_plugin_is_installed($id)) {
                return true;
            }
        }

        return false;
    }
}

if (!function_exists('fate_plugins_admin_rows_installed')) {
    /**
     * 後台列表：僅已安裝的外掛（精簡版不顯示「未安裝的討論區」卡片）。
     *
     * @return list<array{id:string,label:string,description:string,installed:bool,enabled:bool,admin_href:?string,public_href:?string}>
     */
    function fate_plugins_admin_rows_installed(): array
    {
        return array_values(array_filter(
            fate_plugins_admin_rows(),
            static fn (array $r): bool => !empty($r['installed'])
        ));
    }
}

if (!function_exists('fate_plugin_is_enabled')) {
    function fate_plugin_is_enabled(string $id): bool
    {
        if (!fate_plugins_feature_available()) {
            return false;
        }
        if (!fate_plugin_is_installed($id)) {
            return false;
        }

        $stored = fate_plugins_normalize(fate_plugins_read());

        return !empty($stored['plugins'][$id]['enabled']);
    }
}

if (!function_exists('fate_plugins_sync_forum_config_enabled')) {
    function fate_plugins_sync_forum_config_enabled(bool $enabled): void
    {
        $p = fate_plugins_fate_root() . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'forum_config.json';
        $data = [];
        if (is_file($p)) {
            $raw = file_get_contents($p);
            if ($raw !== false && trim($raw) !== '') {
                $j = json_decode($raw, true);
                if (is_array($j)) {
                    $data = $j;
                }
            }
        }
        $data['enabled'] = $enabled;
        $dir = dirname($p);
        if (!is_dir($dir)) {
            mkdir($dir, 0755, true);
        }
        $json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
        if ($json !== false) {
            file_put_contents($p, $json . "\n", LOCK_EX);
        }
    }
}

if (!function_exists('fate_plugins_save_admin_post')) {
    /**
     * @param array<string, mixed> $post
     * @return array{ok:bool, error?:string}
     */
    function fate_plugins_save_admin_post(array $post): array
    {
        if (!fate_plugins_feature_available()) {
            return ['ok' => false, 'error' => '此外掛功能僅完整版提供（需含 forum/bootstrap.php）。'];
        }
        $registry = fate_plugins_registry();
        $current = fate_plugins_normalize(fate_plugins_read());
        $posted = isset($post['plugin_enabled']) && is_array($post['plugin_enabled']) ? $post['plugin_enabled'] : [];

        foreach ($registry as $id => $def) {
            if (!fate_plugin_is_installed($id)) {
                continue;
            }
            $enabled = !empty($posted[$id]);
            $current['plugins'][$id] = ['enabled' => $enabled];
            if ($id === 'forum') {
                fate_plugins_sync_forum_config_enabled($enabled);
            }
        }

        if (!fate_plugins_persist($current)) {
            return ['ok' => false, 'error' => '無法寫入設定檔（請確認 data/ 目錄可寫）。'];
        }

        return ['ok' => true];
    }
}

if (!function_exists('fate_plugins_admin_rows')) {
    /**
     * 後台列表用：合併註冊表、安裝狀態與開關。
     *
     * @return list<array{
     *   id:string,
     *   label:string,
     *   description:string,
     *   installed:bool,
     *   enabled:bool,
     *   admin_href:?string,
     *   public_href:?string,
     *   is_builtin:bool,
     *   install_path_rel:string
     * }>
     */
    function fate_plugins_admin_rows(): array
    {
        $stored = fate_plugins_normalize(fate_plugins_read());
        $rows = [];
        foreach (fate_plugins_registry() as $id => $def) {
            $installed = fate_plugin_is_installed($id);
            $installRel = fate_plugins_install_path_to_rel((string) ($def['install_path'] ?? ''));
            $rows[] = [
                'id' => $id,
                'label' => (string) ($def['label'] ?? $id),
                'description' => (string) ($def['description'] ?? ''),
                'installed' => $installed,
                'enabled' => $installed && !empty($stored['plugins'][$id]['enabled']),
                'admin_href' => isset($def['admin_href']) && $def['admin_href'] !== null ? (string) $def['admin_href'] : null,
                'public_href' => isset($def['public_href']) && $def['public_href'] !== null ? (string) $def['public_href'] : null,
                'is_builtin' => fate_plugins_is_builtin($id),
                'install_path_rel' => $installRel,
            ];
        }

        return $rows;
    }
}

if (!function_exists('fate_plugins_discover_candidates')) {
    /**
     * 掃描站台目錄，找出已部署但未登記的外掛。
     *
     * @return list<array{
     *   suggested_id:string,
     *   suggested_label:string,
     *   install_path_rel:string,
     *   suggested_public_href:?string,
     *   suggested_admin_href:?string
     * }>
     */
    function fate_plugins_discover_candidates(): array
    {
        if (!fate_plugins_feature_available()) {
            return [];
        }
        $root = fate_plugins_fate_root();
        $registered = array_keys(fate_plugins_registry());
        $items = [];

        $pluginsDir = $root . DIRECTORY_SEPARATOR . 'plugins';
        if (is_dir($pluginsDir)) {
            foreach (scandir($pluginsDir) ?: [] as $name) {
                if ($name === '.' || $name === '..' || !is_dir($pluginsDir . DIRECTORY_SEPARATOR . $name)) {
                    continue;
                }
                $rel = 'plugins/' . $name . '/bootstrap.php';
                $full = fate_plugins_resolve_install_path($rel);
                if (!is_file($full)) {
                    continue;
                }
                $id = preg_replace('/[^a-z0-9_]/', '_', strtolower($name)) ?? '';
                $id = trim((string) $id, '_');
                if ($id === '' || str_starts_with($id, '0')) {
                    $id = 'plugin_' . substr(md5($name), 0, 8);
                }
                $items[] = [
                    'suggested_id' => $id,
                    'suggested_label' => $name,
                    'install_path_rel' => $rel,
                    'suggested_public_href' => '../plugins/' . rawurlencode($name) . '/index.php',
                    'suggested_admin_href' => is_file($pluginsDir . DIRECTORY_SEPARATOR . $name . DIRECTORY_SEPARATOR . 'admin' . DIRECTORY_SEPARATOR . 'index.php')
                        ? '../plugins/' . rawurlencode($name) . '/admin/index.php'
                        : null,
                ];
            }
        }

        $out = [];
        foreach ($items as $item) {
            if (in_array($item['suggested_id'], $registered, true)) {
                continue;
            }
            $out[] = $item;
        }

        return $out;
    }
}

if (!function_exists('fate_plugins_registry_form_defaults')) {
    /** @return array<string, string|bool> */
    function fate_plugins_registry_form_defaults(?array $prefill = null): array
    {
        $prefill = $prefill ?? [];

        return [
            'plugin_id' => (string) ($prefill['plugin_id'] ?? ''),
            'label' => (string) ($prefill['label'] ?? ''),
            'description' => (string) ($prefill['description'] ?? ''),
            'install_path' => (string) ($prefill['install_path'] ?? ''),
            'public_href' => (string) ($prefill['public_href'] ?? ''),
            'admin_href' => (string) ($prefill['admin_href'] ?? ''),
            'default_enabled' => !empty($prefill['default_enabled']),
        ];
    }
}

if (!function_exists('fate_plugins_registry_entry_for_edit')) {
    /** @return array<string, string|bool>|null */
    function fate_plugins_registry_entry_for_edit(string $id): ?array
    {
        if (fate_plugins_is_builtin($id)) {
            return null;
        }
        $custom = fate_plugins_custom_registry_read();
        if (!isset($custom[$id]) || !is_array($custom[$id])) {
            return null;
        }
        $def = $custom[$id];

        return fate_plugins_registry_form_defaults([
            'plugin_id' => $id,
            'label' => (string) ($def['label'] ?? $id),
            'description' => (string) ($def['description'] ?? ''),
            'install_path' => (string) ($def['install_path'] ?? ''),
            'public_href' => (string) ($def['public_href'] ?? ''),
            'admin_href' => (string) ($def['admin_href'] ?? ''),
            'default_enabled' => !empty($def['default_enabled']),
        ]);
    }
}

if (!function_exists('fate_plugins_save_registry_post')) {
    /**
     * @param array<string, mixed> $post
     * @return array{ok:bool, error?:string, redirect?:string}
     */
    function fate_plugins_save_registry_post(array $post): array
    {
        if (!fate_plugins_feature_available()) {
            return ['ok' => false, 'error' => '此外掛功能僅完整版提供（需含 forum/bootstrap.php）。'];
        }
        $action = isset($post['registry_action']) && is_string($post['registry_action']) ? $post['registry_action'] : '';
        $custom = fate_plugins_custom_registry_read();

        if ($action === 'delete') {
            $id = isset($post['plugin_id']) && is_string($post['plugin_id']) ? strtolower(trim($post['plugin_id'])) : '';
            if ($id === '' || fate_plugins_is_builtin($id)) {
                return ['ok' => false, 'error' => '無法刪除此項目。'];
            }
            if (!isset($custom[$id])) {
                return ['ok' => false, 'error' => '找不到此外掛。'];
            }
            unset($custom[$id]);
            if (!fate_plugins_custom_registry_persist($custom)) {
                return ['ok' => false, 'error' => '無法寫入外掛清單（請確認 data/ 目錄可寫）。'];
            }

            return ['ok' => true, 'redirect' => 'plugins.php?registry_saved=1'];
        }

        $id = isset($post['plugin_id']) && is_string($post['plugin_id']) ? strtolower(trim($post['plugin_id'])) : '';
        $idErr = fate_plugins_validate_id($id);
        if ($idErr !== null) {
            return ['ok' => false, 'error' => $idErr];
        }

        if ($action === 'add' && (fate_plugins_is_builtin($id) || isset($custom[$id]))) {
            return ['ok' => false, 'error' => '此外掛識別碼已被使用，請換一個。'];
        }
        if ($action === 'edit') {
            if (fate_plugins_is_builtin($id)) {
                return ['ok' => false, 'error' => '內建外掛不可在此修改（僅能啟用／停用）。'];
            }
            if (!isset($custom[$id])) {
                return ['ok' => false, 'error' => '找不到此外掛。'];
            }
        }
        if ($action !== 'add' && $action !== 'edit') {
            return ['ok' => false, 'error' => '未知的操作。'];
        }

        $norm = fate_plugins_normalize_registry_entry([
            'label' => $post['label'] ?? '',
            'description' => $post['description'] ?? '',
            'install_path' => $post['install_path'] ?? '',
            'public_href' => $post['public_href'] ?? '',
            'admin_href' => $post['admin_href'] ?? '',
            'default_enabled' => !empty($post['default_enabled']),
        ]);
        if (!$norm['ok'] || !is_array($norm['entry'])) {
            return ['ok' => false, 'error' => $norm['error'] ?? '資料格式錯誤。'];
        }

        $custom[$id] = $norm['entry'];
        if (!fate_plugins_custom_registry_persist($custom)) {
            return ['ok' => false, 'error' => '無法寫入外掛清單（請確認 data/ 目錄可寫）。'];
        }

        $redirect = $action === 'edit'
            ? ('plugins.php?edit=' . rawurlencode($id) . '&registry_saved=1')
            : 'plugins.php?registry_saved=1';

        return ['ok' => true, 'redirect' => $redirect];
    }
}
