"""Regression tests for the Settings → Extensions diagnostics and toggles."""
from pathlib import Path
import re
import shutil
import subprocess

import pytest


ROOT = Path(__file__).parent.parent
INDEX_HTML = (ROOT / "static" / "index.html").read_text(encoding="utf-8")
PANELS_JS = (ROOT / "static" / "panels.js").read_text(encoding="utf-8")
STYLE_CSS = (ROOT / "static" / "style.css").read_text(encoding="utf-8")
I18N_JS = (ROOT / "static" / "i18n.js").read_text(encoding="utf-8")
DOCS_EXTENSIONS = (ROOT / "docs" / "EXTENSIONS.md").read_text(encoding="utf-8")
ROUTES_PY = (ROOT / "api" / "routes.py").read_text(encoding="utf-8")


def _function_block(name: str, *, extra: int = 2200) -> str:
    start = PANELS_JS.find(f"function {name}")
    assert start >= 0, f"{name} not found"
    return PANELS_JS[start:start + extra]


def _between(start_marker: str, end_marker: str) -> str:
    start = PANELS_JS.find(start_marker)
    assert start >= 0, f"{start_marker} not found"
    end = PANELS_JS.find(end_marker, start)
    assert end >= 0, f"{end_marker} not found after {start_marker}"
    return PANELS_JS[start:end]


def _locale_count() -> int:
    return len(re.findall(r"^  (?:[A-Za-z_][A-Za-z0-9_]*|'[^']+'):\s*\{", I18N_JS, re.MULTILINE))


def _locale_blocks() -> dict[str, str]:
    matches = list(re.finditer(r"^  ((?:[A-Za-z_][A-Za-z0-9_]*|'[^']+')):\s*\{", I18N_JS, re.MULTILINE))
    blocks = {}
    for idx, match in enumerate(matches):
        end = matches[idx + 1].start() if idx + 1 < len(matches) else len(I18N_JS)
        blocks[match.group(1)] = I18N_JS[match.start():end]
    return blocks


def _locale_string(block: str, key: str) -> str:
    match = re.search(rf"\b{re.escape(key)}:\s*([\"'])(.*?)\1", block, re.DOTALL)
    assert match, f"{key} not found in locale block"
    return match.group(2)


def _contains_post_method(block: str) -> bool:
    """Return True when a JS block contains a method: 'POST' style mutation."""
    return bool(re.search(r"\bmethod\s*:\s*([\"'`])POST\1", block))


def _run_node(script: str):
    node = shutil.which("node")
    if not node:
        pytest.skip("node is required for extension settings panel runtime tests")
    result = subprocess.run(
        [node, "-e", script],
        cwd=ROOT,
        text=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        timeout=20,
    )
    assert result.returncode == 0, result.stderr + result.stdout


def test_settings_sidebar_has_extensions_section_and_pane():
    assert 'data-settings-section="extensions"' in INDEX_HTML
    assert "switchSettingsSection('extensions',{fromSidebarItem:true})" in INDEX_HTML
    assert 'id="settingsPaneExtensions"' in INDEX_HTML
    assert 'id="extensionsDiagnostics"' in INDEX_HTML
    assert 'id="extensionsCopyDiagnosticsBtn"' in INDEX_HTML
    assert 'data-i18n="settings_tab_extensions"' in INDEX_HTML


def test_extensions_panel_warns_about_trust_model_and_stays_install_free():
    pane_start = INDEX_HTML.index('id="settingsPaneExtensions"')
    pane_end = INDEX_HTML.index('id="settingsPaneSystem"', pane_start)
    pane = INDEX_HTML[pane_start:pane_end]

    assert "Extensions run in the WebUI browser origin" in pane
    assert "Settings and extension-owned storage are browser-local and not for secrets" in pane
    assert "Only load trusted local extension directories" in pane
    assert "takes effect after reload" in pane
    assert "copyExtensionsDiagnostics()" in pane
    assert "saveSettings(" not in pane
    assert "api('/api/settings'" not in pane
    assert "type=\"checkbox\"" not in pane
    assert "marketplace" not in pane.lower()
    assert "static/extension_settings.js" in INDEX_HTML
    assert INDEX_HTML.index("static/extension_settings.js") < INDEX_HTML.index("static/panels.js")


def test_switch_settings_section_supports_extensions_lazy_load():
    switch_block = _function_block("switchSettingsSection", extra=2300)

    assert "name==='extensions'" in switch_block
    assert "plugins:'Plugins',extensions:'Extensions'" in switch_block
    assert "'plugins','extensions','system'" in switch_block.replace("\n", "")
    assert "if(section==='extensions') loadExtensionsPanel();" in switch_block


def test_settings_search_knows_extensions_pane():
    build_block = _function_block("_buildSettingsIndex", extra=2600)
    filter_block = _function_block("filterSettings", extra=1700)
    resolve_block = _function_block("_resolveSettingsField", extra=1400)

    assert "loadExtensionsPanel()" in build_block
    assert "settingsPaneExtensions: 'extensions'" in build_block
    assert "extensions: t('settings_tab_extensions') || 'Extensions'" in filter_block
    assert "extensions: 'settingsPaneExtensions'" in resolve_block


def test_extensions_panel_fetches_status_endpoint_without_mutating_settings():
    load_block = _function_block("loadExtensionsPanel", extra=900)

    assert "api('/api/extensions/status')" in load_block
    assert "api('/api/settings'" not in load_block
    assert not _contains_post_method(load_block)
    assert "extensions-error" in load_block


def test_extensions_do_not_add_generic_backend_settings_write_route():
    assert "/api/extensions/settings" not in ROUTES_PY
    assert "/api/extensions/storage" not in ROUTES_PY
    assert "set_extension_settings" not in ROUTES_PY
    assert "write_extension_settings" not in ROUTES_PY


def test_extensions_diagnostics_tab_refreshes_runtime_status():
    tab_block = _function_block("switchExtensionsTab", extra=900)

    assert "if(tab==='diagnostics') loadExtensionsPanel({preserveExisting:true});" in tab_block
    assert "if(tab==='gallery'&&!_extensionsGalleryLoaded) loadExtensionsGallery();" in tab_block


def test_extensions_panel_renders_sanitized_status_payload():
    render_block = _between("function _renderExtensionsPanel", "async function loadExtensionsPanel")
    warning_block = _function_block("_extensionWarningList", extra=900)
    asset_block = _function_block("_extensionAssetList", extra=500)

    assert "extension_dir_configured" in render_block
    assert "extension_dir_valid" in render_block
    assert "manifest.status" in render_block
    assert "manifest.entry_count" in render_block
    assert "manifest.script_count" in render_block
    assert "manifest.stylesheet_count" in render_block
    assert "manifest.sidecar_count" in render_block
    assert "script_urls" in render_block
    assert "stylesheet_urls" in render_block
    assert "data&&data.sidecars" in render_block
    assert "data&&data.extensions" in render_block
    assert "counts,'manifest_extensions'" in render_block
    assert "counts,'user_disabled'" in render_block
    assert "_extensionInstalledList(extensions,!!(data&&data.extension_dir_configured),'diagnostics')" in render_block
    assert "_extensionSidecarCard(sidecars)" in render_block
    assert "data&&data.warnings" in render_block
    assert "esc(url)" in asset_block
    assert "esc(manifest.status||'unknown')" in render_block
    assert "const rawCode=(item&&item.code)||'unknown_warning'" in warning_block
    assert "const code=esc(rawCode)" in warning_block
    assert "esc((item&&item.source)||'unknown')" in warning_block
    assert "extension_state_unknown_ids" in warning_block
    # #7581: the hint text is now routed through t(); the raw English must live
    # only in the locale bundle, not inline in the JS.
    assert "Some saved disabled-extension overrides no longer match the current manifest" not in warning_block
    assert "ext_state_unknown_ids_hint" in warning_block
    assert "Rejected" not in render_block  # rejected values must never be rendered directly


def test_extensions_panel_renders_loopback_sidecar_monitor_safely():
    runtime_block = _between("function _extensionRuntimeStatusValue", "function _extensionSidecarCard")
    sidecar_block = _between("function _extensionSidecarCard", "function _setExtensionSidecarHealth")
    runtime_setter_block = _between("function _setExtensionSidecarRuntime", "async function _checkExtensionSidecarHealth")
    monitor_block = _between("async function _checkExtensionSidecarHealth", "function _renderExtensionsPanel")
    render_block = _between("function _renderExtensionsPanel", "async function loadExtensionsPanel")
    load_block = _between("async function loadExtensionsPanel", "async function copyExtensionsDiagnostics")
    load_catch_block = load_block[load_block.index("}catch(e){"):]

    assert "ext_sidecars_title" in sidecar_block
    assert "ext_sidecars_none" in sidecar_block
    assert "esc(title)" in sidecar_block
    assert "esc(meta)" in sidecar_block
    assert "esc(origin)" in sidecar_block
    assert "esc(healthPath)" in sidecar_block
    assert "esc(healthUrl)" in sidecar_block
    assert "sidecar&&sidecar.proxy" in sidecar_block
    assert "proxy.available===true" in sidecar_block
    assert "proxy.consented===true" in sidecar_block
    assert "proxy.consent_required===true" in sidecar_block
    assert "proxy.origin_changed===true" in sidecar_block
    assert "ext_sidecar_proxy_path" in sidecar_block
    assert "data-extension-sidecar-proxy-id" in sidecar_block
    assert "data-extension-sidecar-proxy-approved" in sidecar_block
    assert 'data-sidecar-runtime-index="${index}"' in sidecar_block
    assert "fetch(healthUrl,{credentials:'omit',cache:'no-store'" in monitor_block
    assert "function _monitorExtensionSidecars(sidecars,seq)" in monitor_block
    assert "const seq=_extensionsSidecarMonitorSeq" not in monitor_block
    assert "_monitorExtensionSidecars(sidecars,seq)" in render_block
    assert "function _renderExtensionsPanel(data,seq)" in render_block
    assert "_bindExtensionSidecarProxyButtons(target)" in render_block
    assert "const seq=++_extensionsSidecarMonitorSeq" in load_block
    assert "opts&&opts.preserveExisting&&target.innerHTML.trim()" in load_block
    # A failed refresh must NOT be preserved as "existing content": the Loading/
    # error placeholders are excluded so a fetch error always renders the error
    # instead of leaving the panel stuck on "Loading extension diagnostics…".
    assert "!target.querySelector('.extensions-loading,.extensions-error')" in load_block
    assert "if(!preserveExisting) target.innerHTML" in load_block
    assert "loadExtensionsPanel({preserveExisting:true})" in load_block
    assert "if(seq!==_extensionsSidecarMonitorSeq) return;" in load_block
    assert "if(seq!==_extensionsSidecarMonitorSeq) return;" in load_catch_block
    assert "if(preserveExisting&&target.innerHTML.trim()) return;" in load_catch_block
    assert "_renderExtensionsPanel(data,seq)" in load_block
    assert "res.ok" in monitor_block
    assert "res.text" not in monitor_block
    assert "body=await res.json()" in monitor_block
    assert "_setExtensionSidecarRuntime(index,body&&typeof body==='object'?body.runtime:null)" in monitor_block
    assert "String(value??'').trim()" in runtime_block
    assert r"/^\d+(?:\.\d+)?$/.test(text)" in runtime_block
    assert "seconds>now+300" in runtime_block
    assert "runtime.sidecar" in runtime_block
    assert "runtime.native_host" in runtime_block
    assert "runtime.bridge" in runtime_block
    assert "runtime.last_seen_at" in runtime_block
    assert "runtime.webui_origin" in runtime_block
    assert "el.innerHTML=details" in runtime_setter_block
    assert "api('/api/settings'" not in monitor_block
    assert "api('/extensions/" not in monitor_block
    assert "sidecar/*" not in monitor_block
    assert not _contains_post_method(monitor_block)


def test_extensions_panel_sidecar_proxy_consent_uses_dedicated_endpoint():
    bind_block = _between("function _bindExtensionSidecarProxyButtons", "async function handleExtensionToggle")
    consent_block = _between("async function handleExtensionSidecarProxyConsent", "function _readExtensionSettingsForm")

    assert "handleExtensionSidecarProxyConsent" in bind_block
    assert "data-extension-sidecar-proxy-id" in bind_block
    assert "api('/api/extensions/sidecar-proxy-consent',{method:'POST',body:JSON.stringify({id,approved})})" in consent_block
    assert "Extension sidecar proxy approved." in consent_block
    assert "Extension sidecar proxy consent revoked." in consent_block
    assert "Failed to update extension sidecar proxy consent" in consent_block
    assert "api('/api/extensions/toggle'" not in consent_block


def test_extensions_panel_toggle_uses_dedicated_endpoint_without_settings_or_install():
    installed_block = _between("function _extensionInstalledList", "function _extensionSidecarHealthBadge")
    toggle_block = _between("async function handleExtensionToggle", "async function loadExtensionsPanel")

    assert "data-extension-toggle-id" in installed_block
    assert "data-extension-next-enabled" in installed_block
    assert "settings_extensions_no_dir" in installed_block
    assert "settings_extensions_installed_empty" in installed_block
    assert "extensionDirConfigured" in installed_block
    assert "Manifest-disabled entries cannot be enabled from WebUI." in installed_block
    assert "api('/api/extensions/toggle',{method:'POST',body:JSON.stringify({id,enabled})})" in toggle_block
    assert "Reload WebUI to apply changes" in toggle_block
    combined = installed_block + toggle_block
    assert "api('/api/settings'" not in combined
    assert ">Install<" not in combined
    assert "Install extension" not in combined
    assert "Uninstall" not in combined
    assert "marketplace" not in combined.lower()


def test_extensions_installed_settings_route_through_shared_accessor():
    installed_block = _between("function _extensionInstalledList", "function _extensionSidecarHealthBadge")
    settings_block = _between("function _configureExtensionSettingsFromStatus", "function _extensionInstalledList")
    bind_block = _between("function _bindExtensionSettingsButtons", "async function loadExtensionsPanel")
    gallery_block = _between("function _renderExtensionsGallery", "function _bindExtensionGalleryButtons")
    installed_surface_block = _between("function _renderInstalledExtensionsSurface", "async function loadExtensionsGallery")

    assert "entry&&entry.storage_owned" in settings_block
    assert "window.HermesExtensionSettings.settingsForExtension(id)" in settings_block
    assert "settingsApi&&settingsApi.schema" in settings_block
    assert "settingsApi||!settingsApi.trusted" in settings_block
    assert "data-extension-settings-save" in settings_block
    assert "data-extension-settings-reset" in settings_block
    assert "data-extension-storage-clear" in settings_block
    assert "Browser-local extension settings" in settings_block
    assert "Do not store secrets here" in settings_block
    assert "Reload WebUI after enabling or installing this extension to edit browser-local settings." in settings_block
    assert "_extensionSettingsControls(entry)" in installed_block
    assert "window.HermesExtensionSettings.settingsForExtension(id).reset()" in bind_block
    assert "window.HermesExtensionSettings.storageForExtension(id).clear()" in bind_block
    assert "api('/api/extensions/status')" not in bind_block
    assert "api('/api/settings'" not in bind_block
    assert "localStorage" not in bind_block
    assert "_renderInstalledExtensionsSurface(statusData)" in gallery_block
    assert "_bindExtensionSettingsButtons(installedEl)" in installed_surface_block


def test_extension_configure_hook_is_scoped_to_installed_rows_and_current_status():
    installed_block = _between("function _extensionInstalledList", "function _extensionSidecarHealthBadge")
    configure_block = _between("function _extensionConfigureButton", "function _extensionInstalledList")
    bind_block = _between("function _bindExtensionConfigureButtons", "async function handleExtensionToggle")
    diagnostics_block = _between("function _renderExtensionsPanel", "function _bindExtensionToggleButtons")
    gallery_block = _between("function _renderInstalledExtensionsSurface", "function _bindExtensionGalleryButtons")

    assert "surface!=='installed'" in configure_block
    assert "entry&&entry.effective_enabled" in configure_block
    assert "_configureStateForExtension(id)" in configure_block
    assert "state.available" in configure_block
    assert "data-extension-configure-id" in configure_block
    assert "aria-busy" in configure_block
    assert "_extensionConfigureButton(entry,surface)" in installed_block
    assert "_extensionInstalledList(extensions,!!(data&&data.extension_dir_configured),'diagnostics')" in diagnostics_block
    assert "statusData&&statusData.extensions" in gallery_block
    assert "'installed'" in gallery_block
    assert "_bindExtensionConfigureButtons(installedEl)" in gallery_block
    assert "_invokeConfigure(id,{" in bind_block
    assert "opener:btn" in bind_block
    assert "Extension configuration failed." in bind_block
    assert "showToast" in bind_block
    assert "data-extension-configure-id" in bind_block


def test_extension_configure_rendered_surface_runtime():
    configure_block = _between("function _extensionConfigureButton", "function _extensionInstalledList")
    installed_block = _between("function _extensionInstalledList", "function _extensionSidecarHealthBadge")
    script = "\n".join(
        [
            "const assert = require('assert');",
            "const states = new Map([['alpha.ext', {available:true,pending:false}], ['beta.ext', {available:true,pending:false}], ['gamma.ext', {available:false,pending:false}]]);",
            "const window = {HermesExtensionSettings:{_configureStateForExtension(id){return states.get(id)||{available:false,pending:false};}}};",
            "function esc(value){return String(value??'');}",
            "function _extensionEntryBadge(){return '';}",
            "function _extensionSettingsControls(){return '';}",
            configure_block,
            installed_block,
            "const entries=[",
            "  {id:'alpha.ext',name:'Alpha',effective_enabled:true,can_toggle:true,user_enabled:true},",
            "  {id:'beta.ext',name:'Beta',effective_enabled:false,can_toggle:true,user_enabled:false},",
            "  {id:'gamma.ext',name:'Gamma',effective_enabled:true,can_toggle:true,user_enabled:true},",
            "];",
            "const installed=_extensionInstalledList(entries,true,'installed');",
            "const diagnostics=_extensionInstalledList(entries,true,'diagnostics');",
            "assert.strictEqual((installed.match(/data-extension-configure-id=/g)||[]).length,1);",
            "assert.ok(installed.includes('data-extension-configure-id=\"alpha.ext\"'));",
            "assert.strictEqual((diagnostics.match(/data-extension-configure-id=/g)||[]).length,0);",
            "states.set('alpha.ext',{available:true,pending:true});",
            "const pending=_extensionInstalledList(entries,true,'installed');",
            "assert.ok(pending.includes('aria-busy=\"true\" disabled'));",
            "assert.ok(pending.includes('Opening…'));",
            "states.set('alpha.ext',{available:false,pending:false});",
            "assert.strictEqual((_extensionInstalledList(entries,true,'installed').match(/data-extension-configure-id=/g)||[]).length,0);",
            "states.set('alpha.ext',{available:true,pending:false});",
            "assert.strictEqual((_extensionInstalledList(entries,true,'installed').match(/data-extension-configure-id=/g)||[]).length,1);",
        ]
    )
    _run_node(script)


def test_extension_configure_late_registration_rerenders_only_installed_surface():
    listener_block = _between("function _handleExtensionConfigureChange", "function _extensionSafeHttpUrl")
    diagnostics_block = _between("function _renderExtensionsPanel", "function _bindExtensionToggleButtons")

    assert "_onConfigureChange(_handleExtensionConfigureChange)" in listener_block
    assert "change.reason==='pending'" in listener_block
    assert "_syncExtensionConfigureButtonState(change.id)" in listener_block
    assert "_renderInstalledExtensionsSurface" in listener_block
    assert "extensionsDiagnostics" not in listener_block
    assert "_extensionsGalleryData.statusData=data||null" in diagnostics_block


def test_extensions_gallery_renders_post_install_guidance():
    url_block = _between("function _extensionSafeHttpUrl", "function _extensionPostInstallNote")
    gallery_block = _between("function _extensionPostInstallNote", "async function loadExtensionsGallery")
    render_block = _between("function _renderExtensionsGallery", "function _bindExtensionGalleryButtons")
    install_block = _between("async function handleExtensionInstall", "async function handleExtensionUninstall")

    assert "/^https?:\\/\\//i.test(raw)" in url_block
    assert "url.username||url.password" in url_block
    assert "entry.post_install" in gallery_block
    assert "post&&post.docs_url" in gallery_block
    assert "sidecar_start_required" in gallery_block
    assert "native_host_start_required" in gallery_block
    assert "requires_local_app" in gallery_block
    assert "local_app_label" in gallery_block
    assert "t('ext_gallery_local_component_required')" in gallery_block
    assert "t('ext_gallery_local_app_label')" in gallery_block
    assert "t('ext_gallery_required_suffix',localAppLabel)" in gallery_block
    assert "t('ext_gallery_sidecar_required')" in gallery_block
    assert "t('ext_gallery_native_host_required')" in gallery_block
    assert "t('ext_gallery_open_setup_guide')" in gallery_block
    assert "t(isInstalled?'ext_gallery_next_step':'ext_gallery_after_install')" in gallery_block
    assert "target=\"_blank\"" in gallery_block
    assert "rel=\"noopener noreferrer\"" in gallery_block
    assert "extension-gallery-next-step" in gallery_block
    assert "esc(summary)" in gallery_block
    assert "esc(docsUrl)" in gallery_block
    assert "esc(item)" in gallery_block
    assert "_extensionPostInstallNote(entry,isInstalled)" in render_block
    assert "t('ext_gallery_install_restart_required')" in install_block
    assert "t('ext_gallery_install_followup')" in install_block
    assert "t('ext_gallery_install_ok')" in install_block
    assert "webui_restart_required" in install_block


def test_extensions_gallery_links_sources_and_humanizes_permissions():
    helper_block = _between("function _extensionRegistrySourceUrl", "function _extensionPostInstallNote")
    render_block = _between("function _renderExtensionsGallery", "function _bindExtensionGalleryButtons")

    assert "entry.homepage" in helper_block
    assert "entry.repository_url" in helper_block
    assert "entry.entry_path||entry.runtime_manifest_path" in helper_block
    assert "hermes-webui/hermes-webui-extensions/tree/main" in helper_block
    assert "encodeURIComponent" in helper_block
    assert "extension-gallery-source-link" in helper_block
    assert "target=\"_blank\"" in helper_block
    assert "rel=\"noopener noreferrer\"" in helper_block
    assert "t('ext_gallery_permissions_empty')" in helper_block
    assert "webui_api" in helper_block
    assert "sidecar_commands" in helper_block
    assert "dom.mutates_core_views" in helper_block
    assert "storage.shared_webui_keys" in helper_block
    assert "loopback_sidecar" in helper_block
    assert "native_host" in helper_block
    assert "network_external" in helper_block
    assert "extension-gallery-permission-row" in helper_block
    assert "_extensionSourceLink(entry)" in render_block
    assert "_extensionPermissionSummary(perms)" in render_block
    assert "JSON.stringify(perms" not in render_block
    assert "<pre>" not in render_block


def test_copy_extensions_diagnostics_copies_current_sanitized_payload():
    copy_block = _between("function copyExtensionsDiagnostics", "// ── Plugins panel")

    assert "JSON.stringify(_extensionsStatusData,null,2)" in copy_block
    assert "navigator.clipboard.writeText(text)" in copy_block
    assert "api('/api/settings'" not in copy_block
    assert "api('/api/extensions'" not in copy_block
    assert not _contains_post_method(copy_block)


def test_extensions_styles_are_scoped_to_extensions_panel():
    assert ".extensions-diagnostics" in STYLE_CSS
    assert ".extensions-trust-note" in STYLE_CSS
    assert ".extension-summary-grid" in STYLE_CSS
    assert ".extension-warning-list" in STYLE_CSS
    assert ".extension-url-list" in STYLE_CSS
    assert ".extension-installed-list" in STYLE_CSS
    assert ".extension-toggle-btn" in STYLE_CSS
    assert ".extension-settings-box" in STYLE_CSS
    assert ".extension-settings-actions" in STYLE_CSS
    assert ".extension-installed-actions" in STYLE_CSS
    assert ".extension-configure-btn" in STYLE_CSS
    assert ".extension-sidecar-list" in STYLE_CSS
    assert ".extension-sidecar-runtime" in STYLE_CSS
    assert ".extension-sidecar-status-badge" in STYLE_CSS
    assert ".extension-gallery-source-link" in STYLE_CSS
    assert ".extension-gallery-next-step" in STYLE_CSS
    assert ".extension-gallery-next-link" in STYLE_CSS
    assert ".extension-gallery-permission-list" in STYLE_CSS
    assert ".extension-gallery-permission-row" in STYLE_CSS


def test_extensions_i18n_keys_exist_for_all_locales():
    blocks = _locale_blocks()

    assert len(blocks) == _locale_count()
    required_keys = [
        "settings_tab_extensions",
        "ext_gallery_next_step",
        "ext_gallery_after_install",
        "ext_gallery_permissions_empty",
        "ext_gallery_local_component_required",
        "ext_gallery_local_app_label",
        "ext_gallery_required_suffix",
        "ext_gallery_sidecar_required",
        "ext_gallery_native_host_required",
        "ext_gallery_open_setup_guide",
        "ext_gallery_install_restart_required",
        "ext_gallery_install_followup",
    ]
    missing = {
        name: [key for key in required_keys if key not in block]
        for name, block in blocks.items()
    }
    missing = {name: keys for name, keys in missing.items() if keys}
    assert not missing, f"Locale(s) missing extension i18n key(s): {missing}"


def test_extensions_post_install_i18n_is_localized_outside_english():
    blocks = _locale_blocks()
    english = blocks["en"]
    post_install_keys = [
        "ext_gallery_next_step",
        "ext_gallery_after_install",
        "ext_gallery_local_component_required",
        "ext_gallery_local_app_label",
        "ext_gallery_required_suffix",
        "ext_gallery_sidecar_required",
        "ext_gallery_native_host_required",
        "ext_gallery_open_setup_guide",
        "ext_gallery_install_restart_required",
        "ext_gallery_install_followup",
    ]
    english_values = {key: _locale_string(english, key) for key in post_install_keys}
    untranslated = {
        name: [
            key
            for key in post_install_keys
            if _locale_string(block, key) == english_values[key]
        ]
        for name, block in blocks.items()
        if name != "en"
    }
    untranslated = {name: keys for name, keys in untranslated.items() if keys}

    assert not untranslated, f"Locale(s) keep English post-install guidance: {untranslated}"
    for name, block in blocks.items():
        assert "{0}" in _locale_string(block, "ext_gallery_required_suffix"), (
            f"{name} must preserve the local-app label placeholder"
        )


def test_extensions_i18n_does_not_include_replacement_characters():
    assert "\ufffd" not in I18N_JS


def test_extensions_docs_mentions_settings_panel_without_install_or_proxy_claims():
    diagnostics_section = DOCS_EXTENSIONS[DOCS_EXTENSIONS.index("## Diagnostics"):]

    assert "Settings → Extensions" in diagnostics_section
    assert "`POST /api/extensions/toggle`" in diagnostics_section
    assert "WebUI-managed override" in diagnostics_section
    assert "does not edit extension" in diagnostics_section
    assert "manifests" in diagnostics_section
    assert "fetch new extension assets" in diagnostics_section
    assert "uninstall files" in diagnostics_section
    assert "GET /api/extensions/status" in diagnostics_section
    assert "`POST /api/extensions/sidecar-proxy-consent`" in diagnostics_section
    assert "sanitized loopback sidecars" in diagnostics_section
    assert "credentials: 'omit'" in diagnostics_section
    assert "fixed per-extension sidecar path" in diagnostics_section
    assert "WebUI strips `Cookie`, `Authorization`, and CSRF headers" in diagnostics_section
    assert "does not create arbitrary extension-owned backend routes" in diagnostics_section
    assert "optional top-level `runtime` object" in diagnostics_section
    assert "allowlisted scalar fields" in diagnostics_section
    assert "browser-local controls" in diagnostics_section
    assert "`window.HermesExtensionSettings`" in DOCS_EXTENSIONS
    assert "does not store extension settings or expose a generic settings write route" in DOCS_EXTENSIONS
    assert "Settings persist only non-default overrides" in DOCS_EXTENSIONS
    assert "do **not**" in diagnostics_section
    assert "return `HERMES_WEBUI_EXTENSION_DIR`" in diagnostics_section
    assert "override state-file path" in diagnostics_section


def test_extensions_docs_define_scoped_configure_editor_contract():
    configure_section = DOCS_EXTENSIONS[
        DOCS_EXTENSIONS.index("### Custom Configure editors"):
        DOCS_EXTENSIONS.index("### Turn lifecycle events")
    ]

    assert "ext?.settings?.registerConfigure?." in configure_section
    assert "does not require `settings_schema` or extension-owned storage" in configure_section
    assert "Settings → Extensions → Installed" in configure_section
    assert "Diagnostics" in configure_section
    assert "Late registration" in configure_section
    assert "same-ID reinstall" in configure_section
    assert "one-shot `restoreFocus()`" in configure_section
    assert "return a thenable" in configure_section
    assert "remains disabled until reload" in configure_section
    assert "Synchronous throws" in configure_section
    assert "generic failure" in configure_section
