<%
    local fs = require "luci.openclash"
    local plugin_version = fs.oc_version()
    local uci = require("luci.model.uci").cursor()
    local RELEASE_BRANCH = fs.uci_get_config("config", "release_branch")
%>
<html>
<head>
<script src="/luci-static/resources/openclash/js/common.js?v=<%=plugin_version%>"></script>
<link rel="stylesheet" href="/luci-static/resources/openclash/css/oc.css?v=<%=plugin_version%>">
<script src="/luci-static/resources/openclash/js/cm6.min.js?v=<%=plugin_version%>"></script>
</head>
<fieldset class="cbi-section">
    <table width="100%">
    <tr>
        <td colspan="4">
            <div class="oc">
                <div class="main-cards-container">
                    <div class="announcement-card">
                        <div id="announcement-banner" class="announcement-banner">
                            <div class="megaphone-container">
                                <svg id="megaphone" xmlns="http://www.w3.org/2000/svg" width="15" height="15" fill="currentColor" viewBox="0 0 256 256">
                                    <path d="M228.54,86.66l-176.06-54A16,16,0,0,0,32,48V192a16,16,0,0,0,16,16,16,16,0,0,0,4.52-.65L136,181.73V192a16,16,0,0,0,16,16h32a16,16,0,0,0,16-16v-29.9l28.54-8.75A16.09,16.09,0,0,0,240,138V102A16.09,16.09,0,0,0,228.54,86.66ZM136,165,48,192V48l88,27Zm48,27H152V176.82L184,167Zm40-54-.11,0L152,160.08V79.92l71.89,22,.11,0v36Z"></path>
                                </svg>
                            </div>
                            <div id="announcement-content" class="announcement-content"></div>
                        </div>
                    </div>
                    <div class="main-card">
                        <!-- Row 1: Running Status -->
                        <div class="card-row full-width core-status-row">
                            <div class="sub-card">
                                <div class="card-title"><%:Running Status%></div>
                                <div class="version-display-container">
                                    <div class="version-display" id="plugin-version-display" style="display: none;">
                                        <span class="version-text" id="plugin-version-text">-</span>
                                    </div>
                                    <div class="version-display" id="core-version-display" style="display: none;">
                                        <span class="version-text" id="core-version-text">-</span>
                                    </div>
                                </div>
                                <div class="core-main-controls">
                                    <div class="core-status-toggle">
                                        <span id="_clash" class="value-indicator"><%:Collecting data...%></span>
                                        <span id="_oclog" class="value-indicator" style="display: none;"><%:Collecting data...%></span>
                                        <div class="plugin-toggle-container">
                                            <label class="plugin-toggle-switch">
                                                <input type="checkbox" id="plugin_toggle" onchange="togglePlugin(this)">
                                                <span class="plugin-toggle-slider"></span>
                                            </label>
                                        </div>
                                    </div>
                                    <div class="core-control-buttons">
                                        <button type="button" class="icon-btn action-btn" id="restart_core" title="<%:Restart%>" onclick="return restartCore()">
                                            <svg width="14" height="14"><use href="#oc-icon-refresh"/></svg>
                                        </button>
                                        <button type="button" class="icon-btn action-btn" id="edit_overwrite" title="<%:Overwrite Module%>" onclick="return editOverwrite()">
                                            <svg width="32" height="32"><use href="#oc-icon-overwrite"/></svg>
                                            <span class="btn-text"><%:Overwrite Module%></span>
                                        </button>
                                    </div>
                                </div>
                                <div class="card-actions">
                                    <button type="button" class="icon-btn theme-toggle-btn" id="theme-toggle" onclick="toggleThemeMode()">
                                        <svg id="sun-icon" width="14" height="14"><use href="#oc-icon-sun"/></svg>
                                        <svg id="moon-icon" width="14" height="14" style="display: none"><use href="#oc-icon-moon"/></svg>
                                        <svg id="auto-icon" width="14" height="14" style="display: none"><use href="#oc-icon-auto-theme"/></svg>
                                    </button>
                                    <button type="button" class="icon-btn action-btn" id="go_wiki" title="<%:Wiki%>" onclick="return wikipage()"><svg width="14" height="14"><use href="#oc-icon-wiki"/></svg></button>
                                    <button type="button" class="icon-btn action-btn" id="go_tutorials" title="<%:Tutorials%>" onclick="return gitbookpage()"><svg width="14" height="14"><use href="#oc-icon-book"/></svg></button>
                                    <button type="button" class="icon-btn action-btn" id="go_star" title="<%:Star%>" onclick="return homepage()"><svg width="14" height="14"><use href="#oc-icon-star"/></svg></button>
                                    <button type="button" class="icon-btn action-btn" id="go_telegram" title="<%:Telegram%>" onclick="return telegrampage()"><svg width="14" height="14"><use href="#oc-icon-telegram"/></svg></button>
                                    <button type="button" class="icon-btn action-btn" id="go_sponsor" title="<%:Sponsor%>" onclick="return sponsorpage()"><svg width="14" height="14"><use href="#oc-icon-heart"/></svg></button>
                                    <button type="button" class="icon-btn action-btn" id="logo_btn" title="Mihomo" onclick="return go_mihomo();">
                                        <img id="_logo" src="/luci-static/resources/openclash/img/logo.png?v=<%=plugin_version%>" loading="lazy" width="14px" height="14px" alt="Mihomo"/>
                                        <span class="logo-text">Powered by <span class="mihomo-link">Mihomo</span></span>
                                    </button>
                                </div>
                            </div>
                        </div>

                        <!-- Row 2: Running Mode & Proxy Mode -->
                        <div class="card-row dual-cards mode-row">
                            <div class="sub-card">
                                <div class="card-title"><%:Running Mode%></div>
                                <div class="card-content">
                                    <div class="card-value"><span id="_mode" class="value-indicator"><%:Collecting data...%></span></div>
                                    <div class="card-controls">
                                        <div id="radio-ru-mode" class="cbi-button-group" style="display: inline-flex;">
                                            <input type="radio" id="normal" name="radios-ru" value="" onclick="return switch_run_mode(this.value)"/><label for="normal" id="run_normal" class="cbi-button-option"><%:Compat%></label>
                                            <input type="radio" id="tun" name="radios-ru" value="-tun" onclick="return switch_run_mode(this.value)"/><label for="tun" class="cbi-button-option"><%:TUN%></label>
                                            <input type="radio" id="mix" name="radios-ru" value="-mix" onclick="return switch_run_mode(this.value)"/><label for="mix" class="cbi-button-option"><%:Mix%></label>
                                        </div>
                                    </div>
                                </div>
                            </div>
                            <div class="sub-card">
                                <div class="card-title"><%:Proxy Mode%></div>
                                <div class="card-content">
                                    <div class="card-controls">
                                        <div id="radio-mode" class="cbi-button-group" style="display: inline-flex;">
                                            <input type="radio" id="rule" name="radios" value="rule" onclick="return switch_rule_mode(this.value)"/><label for="rule" class="cbi-button-option"><%:Rule%></label>
                                            <input type="radio" id="global" name="radios" value="global" onclick="return switch_rule_mode(this.value)"/><label for="global" class="cbi-button-option"><%:Global%></label>
                                            <input type="radio" id="direct" name="radios" value="direct" onclick="return switch_rule_mode(this.value)"/><label for="direct" class="cbi-button-option"><%:Direct%></label>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>

                        <!-- Row 3: Settings -->
                        <div class="card-row stats-grid">
                            <div class="sub-card">
                                <div class="card-title"><%:Area Bypass%><div class="help-link" title="<%:Bypass Specified Regions Network Flows, Improve Performance, If Inaccessibility on Bypass Gateway, Try to Enable Bypass Gateway Compatible Option%>"><svg width="12" height="12"><use href="#oc-icon-help"/></svg></div></div>
                                <div class="card-content">
                                    <div class="card-controls">
                                        <div id="oc-setting-oversea" class="cbi-button-group" style="display: inline-flex;">
                                            <input type="radio" id="oc_setting_oversea_1" name="oc-setting-oversea" value="1" onclick="return switch_oc_setting_oversea('1')"/><label for="oc_setting_oversea_1" class="cbi-button-option"><%:Mainland%></label>
                                            <input type="radio" id="oc_setting_oversea_2" name="oc-setting-oversea" value="2" onclick="return switch_oc_setting_oversea('2')"/><label for="oc_setting_oversea_2" class="cbi-button-option"><%:Oversea%></label>
                                            <input type="radio" id="oc_setting_oversea_0" name="oc-setting-oversea" value="0" onclick="return switch_oc_setting_oversea('0')"/><label for="oc_setting_oversea_0" class="cbi-button-option"><%:Off%></label>
                                        </div>
                                    </div>
                                </div>
                            </div>
                            <div class="sub-card">
                                <div class="card-title"><%:Sniffer%><a href="javascript:void(0);" onclick="window.open('https://wiki.metacubex.one/config/sniff/?h=sniff#_1', '_blank');" class="help-link" title="<%:Sniff the domain name of the traffics to avoid rule-based proxy failure%>"><svg width="12" height="12"><use href="#oc-icon-help"/></svg></a></div>
                                <div class="card-content">
                                    <div class="card-controls">
                                        <div id="dns-setting-sniffer" class="cbi-button-group" style="display: inline-flex;">
                                            <input type="radio" id="meta_sniffer_on" name="meta-sniffer-radios" value="1" onclick="return switch_meta_sniffer('1')"/><label for="meta_sniffer_on" class="cbi-button-option"><%:On%></label>
                                            <input type="radio" id="meta_sniffer_off" name="meta-sniffer-radios" value="0" onclick="return switch_meta_sniffer('0')"/><label for="meta_sniffer_off" class="cbi-button-option"><%:Off%></label>
                                        </div>
                                    </div>
                                </div>
                            </div>
                            <div class="sub-card">
                                <div class="card-title"><%:DNS Proxy%><a href="javascript:void(0);" onclick="window.open('https://wiki.metacubex.one/config/dns/?h=res#respect-rules', '_blank');" class="help-link" title="<%:DNS querys respect-rules to preventing access failure%>"><svg width="12" height="12"><use href="#oc-icon-help"/></svg></a></div>
                                <div class="card-content">
                                    <div class="card-controls">
                                        <div id="dns-setting-respect" class="cbi-button-group" style="display: inline-flex;">
                                            <input type="radio" id="respect_rules_on" name="respect-rules-radios" value="1" onclick="return switch_respect_rules('1')"/><label for="respect_rules_on" class="cbi-button-option"><%:On%></label>
                                            <input type="radio" id="respect_rules_off" name="respect-rules-radios" value="0" onclick="return switch_respect_rules('0')"/><label for="respect_rules_off" class="cbi-button-option"><%:Off%></label>
                                        </div>
                                    </div>
                                </div>
                            </div>
                            <div class="sub-card">
                                <div class="card-title"><%:Stream Unlock%><div class="help-link" title="<%:Auto Select Proxy For Streaming Unlock, Support Netflix, Disney Plus, HBO And YouTube Premium, etc%>"><svg width="12" height="12"><use href="#oc-icon-help"/></svg></div></div>
                                <div class="card-content">
                                    <div class="card-controls">
                                        <div id="stream-unlock-setting" class="cbi-button-group" style="display: inline-flex;">
                                            <input type="radio" id="stream_unlock_on" name="stream-unlock-radios" value="1" onclick="return switch_stream_unlock('1')"/><label for="stream_unlock_on" class="cbi-button-option"><%:On%></label>
                                            <input type="radio" id="stream_unlock_off" name="stream-unlock-radios" value="0" onclick="return switch_stream_unlock('0')"/><label for="stream_unlock_off" class="cbi-button-option"><%:Off%></label>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>

                        <!-- Row 4: Config File -->
                        <div class="card-row full-width">
                            <div class="sub-card">
                                <div class="card-title"><%:Config File%></div>
                                <div class="config-file-content">
                                    <div id="subscription-info-display" class="subscription-info-container" style="display: none;">
                                        <div class="subscription-nav-arrow left" id="subscription-prev-arrow" onclick="switchToPreviousConfig()" title="<%:Previous%>"></div>
                                        <div class="subscription-nav-arrow right" id="subscription-next-arrow" onclick="switchToNextConfig()" title="<%:Next%>"></div>
                                        <div class="subscription-info-main">
                                            <div class="subscription-info-header">
                                                <div class="config-file-name-wrapper">
                                                    <div class="config-file-name" id="current-config-name"><%:No Config Selected%></div>
                                                    <span id="age-file-icon" class="age-file-icon" style="display:none;"></span>
                                                </div>
                                                <div class="file-info" id="file-info-section">
                                                    <span id="file-modify-time" class="file-info-item"><%:Modified: %> --</span>
                                                </div>
                                            </div>
                                            <div class="subscription-info-details">
                                                <div class="subscription-progress" id="subscription-progress-section" style="display: none;">
                                                    <div class="subscription-progress-bar">
                                                        <div id="subscription-progress-fill" class="subscription-progress-fill high" style="width: 0%"></div>
                                                    </div>
                                                    <div id="subscription-info-text" class="subscription-info-text"><%:Collecting data...%></div>
                                                </div>
                                                <div class="card-actions subscription-actions-row">
                                                    <div id="subscription-overwrite-tags" class="subscription-overwrite-tags" style="display: none;"></div>
                                                    <div class="subscription-action-buttons">
                                                        <button type="button" class="icon-btn action-btn" id="refresh-subscription" title="<%:Refresh%>" onclick="return refreshSubscriptionInfo()">
                                                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                                                <path d="M23 4v6h-6"></path>
                                                                <path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path>
                                                            </svg>
                                                        </button>
                                                        <button type="button" class="icon-btn action-btn" id="config-subscription-url" title="<%:Specify URL%>" onclick="return setSubscriptionUrl()">
                                                            <svg width="14" height="14" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4">
                                                                <path d="M4 6H44V36H29L24 41L19 36H4V6Z"/>
                                                                <path d="M23 21H25.0025" />
                                                                <path d="M33.001 21H34.9999"/>
                                                                <path d="M13.001 21H14.9999"/>
                                                            </svg>
                                                        </button>
                                                    </div>
                                                </div>
                                            </div>
                                        </div>
                                    </div>

                                    <div id="config-file-empty-state" class="config-file-empty-state" style="display: none;">
                                        <div class="config-file-empty-message"><%:No config files found, Please upload a config file to get started%></div>
                                        <button type="button" class="config-upload-btn-large" id="upload_config_large" onclick="return uploadConfig()">
                                            <svg width="20" height="20"><use href="#oc-icon-download"/></svg>
                                            <%:Add Config File%>
                                        </button>
                                    </div>

                                    <div class="config-file-bottom">
                                        <div class="config-selector">
                                            <select id="config_file_select" class="config-select"><option value=""><%:Collecting data...%></option></select>
                                        </div>
                                        <div class="card-actions">
                                            <button type="button" class="icon-btn action-btn" id="switch_config" title="<%:SwiTch%>" onclick="return switchConfig()"><svg width="14" height="14"><use href="#oc-icon-switch"/></svg></button>
                                            <button type="button" class="icon-btn action-btn" id="update_config" title="<%:Update%>" onclick="return updateConfig()"><svg width="14" height="14"><use href="#oc-icon-update"/></svg></button>
                                            <button type="button" class="icon-btn action-btn" id="edit_config" title="<%:Edit%>" onclick="return editConfig()"><svg width="14" height="14"><use href="#oc-icon-edit"/></svg></button>
                                            <button type="button" class="icon-btn action-btn" id="edit_subscribe" title="<%:Edit Subscription%>" onclick="return editSubscribe()"><svg width="14" height="14"><use href="#oc-icon-settings"/></svg></button>
                                            <button type="button" class="icon-btn action-btn" id="upload_config" title="<%:Add%>" onclick="return uploadConfig()"><svg width="14" height="14"><use href="#oc-icon-upload"/></svg></button>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>

                    <div class="main-card">
                        <!-- Row 1: Control Panel & Mix Proxy -->
                        <div class="card-row dual-cards">
                            <div class="sub-card">
                                <div class="card-title"><%:Control Panel%></div>
                                <div class="card-content">
                                    <div class="card-value">
                                        <span id="_daip" class="value-indicator"><%:Collecting data...%></span>
                                    </div>
                                </div>
                                <div class="card-actions">
                                    <button type="button" class="icon-btn copy-btn" id="copy_address" title="<%:Copy Address%>" onclick="return copyAddress()"><svg width="14" height="14"><use href="#oc-icon-copy"/></svg></button>
                                    <button type="button" class="icon-btn copy-btn" id="copy_secret" title="<%:Copy Secret%>" onclick="return copySecret()"><svg width="14" height="14"><use href="#oc-icon-lock"/></svg></button>
                                </div>
                            </div>
                            <div class="sub-card">
                                <div class="card-title"><%:Mix Proxy%></div>
                                <div class="card-content">
                                    <div class="card-value">
                                        <span id="_mix_proxy" class="value-indicator"><%:Collecting data...%></span>
                                    </div>
                                </div>
                                <div class="card-actions">
                                    <button type="button" class="icon-btn copy-btn" id="copy_mix_address" title="<%:Copy Address%>" onclick="return copyMixAddress()"><svg width="14" height="14"><use href="#oc-icon-copy"/></svg></button>
                                    <button type="button" class="icon-btn action-btn" id="copy_pac_config" title="<%:Get PAC Config%>" onclick="return generatePacConfig()"><svg width="14" height="14"><use href="#oc-icon-globe"/></svg></button>
                                    <button type="button" class="icon-btn copy-btn" id="copy_mix_secret" title="<%:Copy Auth Info%>" onclick="return copyMixAuth()"><svg width="14" height="14"><use href="#oc-icon-lock"/></svg></button>
                                </div>
                            </div>
                        </div>

                        <!-- Row 2: Dashboards -->
                        <div class="card-row full-width">
                            <div class="sub-card dashboard-container">
                                <div class="card-title"><%:Control Panel%></div>
                                <div class="dashboard-buttons">
                                    <button class="dashboard-btn" id="_web"><%:Collecting data...%></button>
                                    <button class="dashboard-btn" id="_webm"><%:Collecting data...%></button>
                                    <button class="dashboard-btn" id="_webz"><%:Collecting data...%></button>
                                    <button class="dashboard-btn" id="_webo"><%:Collecting data...%></button>
                                </div>
                            </div>
                        </div>

                        <!-- Row 3: Quick Actions -->
                        <div class="card-row full-width">
                            <div class="sub-card quick-actions-container">
                                <div class="card-title"><%:Quick Action%></div>
                                <div class="quick-actions-buttons">
                                    <button class="dashboard-btn" id="_close_all_connection_btn"><%:Collecting data...%></button>
                                    <button class="dashboard-btn" id="_reload_firewall_btn"><%:Collecting data...%></button>
                                    <button class="dashboard-btn" id="_flush_dns_cache_btn"><%:Collecting data...%></button>
                                    <button class="dashboard-btn" id="_one_key_update_btn"><%:Collecting data...%></button>
                                </div>
                            </div>
                        </div>

                        <!-- Row 4: Statistics -->
                        <div class="card-row stats-grid">
                            <div class="sub-card stat-item"><span class="stat-label"><%:Up%></span><span id="upload_" class="stat-value">0 KB/S</span></div>
                            <div class="sub-card stat-item"><span class="stat-label"><%:Down%></span><span id="download_" class="stat-value">0 KB/S</span></div>
                            <div class="sub-card stat-item"><span class="stat-label"><%:Up Total%></span><span id="uploadtotal_" class="stat-value">0 KB</span></div>
                            <div class="sub-card stat-item"><span class="stat-label"><%:Down Total%></span><span id="downloadtotal_" class="stat-value">0 KB</span></div>
                            <div class="sub-card stat-item"><span class="stat-label"><%:Connect%></span><span id="connect_t" class="stat-value">0</span></div>
                            <div class="sub-card stat-item"><span class="stat-label"><%:Ram%></span><span id="mem_t" class="stat-value">0 KB</span></div>
                            <div class="sub-card stat-item"><span class="stat-label"><%:CPU%></span><span id="cpu_t" class="stat-value">0 %</span></div>
                            <div class="sub-card stat-item"><span class="stat-label"><%:Load Avg%></span><span id="load_a" class="stat-value">0 %</span></div>
                        </div>
                    </div>
                </div>

            <!-- Usage Help Model -->
            <div class="config-upload-model-overlay" id="usage-help-overlay" style="display: none;">
                <div class="config-upload-model usage-help-model" id="usage-help-model">
                    <div class="config-upload-header">
                        <div class="config-upload-title"><%:Usage Help%></div>
                        <button type="button" class="icon-btn" onclick="UsageHelpManager.hide()">
                            <svg width="14" height="14"><use href="#oc-icon-close"/></svg>
                        </button>
                    </div>
                    <div class="config-upload-content">
                        <div class="form-group">
                            <label class="highlight-label"><%:Copy the following text to any AI (ChatGPT, DeepSeek, etc.) to get usage help for OpenClash:%></label>
                            <textarea id="usage-help-textarea" class="form-textarea" style="display:none"></textarea>
                            <div id="usage-help-editor" style="height: 400px;"></div>
                        </div>
                    </div>
                    <div class="config-upload-footer">
                        <div class="config-upload-status" id="usage-help-status"></div>
                        <div class="config-upload-buttons">
                            <button type="button" class="btn cancel-btn" onclick="UsageHelpManager.hide()"><%:Cancel%></button>
                            <button type="button" class="btn upload-btn" onclick="UsageHelpManager.copyAndHide()"><%:Copy%></button>
                        </div>
                    </div>
                </div>
            </div>
            </div>
        </td>
    </tr>
</table>
</fieldset>

<!-- Usage Help Button -->
<div class="oc-usage-help-float" id="usage-help-float" title="<%:Usage Help%>" onclick="UsageHelpManager.show()">
    <svg width="24" height="24"><use href="#oc-icon-usage-help"/></svg>
</div>

<script src="/luci-static/resources/openclash/js/oc-icons.js?v=<%=plugin_version%>"></script>
<%+openclash/common%>
<script type="text/javascript">//<![CDATA[
    var DOMCache = {
        clash: document.getElementById('_clash'),
        mode: document.getElementById('_mode'),
        web: document.getElementById('_web'),
        webo: document.getElementById('_webo'),
        webm: document.getElementById('_webm'),
        webz: document.getElementById('_webz'),
        daip: document.getElementById('_daip'),
        oclog: document.getElementById('_oclog'),
        close_all_connection: document.getElementById('_close_all_connection_btn'),
        reload_firewall: document.getElementById('_reload_firewall_btn'),
        one_key_update: document.getElementById('_one_key_update_btn'),
        flush_dns_cache: document.getElementById('_flush_dns_cache_btn'),
        radio_mode: document.getElementById('radio-mode'),
        radio: document.getElementsByName("radios"),
        radio_ru: document.getElementsByName("radios-ru"),
        radio_run_normal: document.getElementById("run_normal"),
        copy_secret: document.getElementById('copy_secret'),
        copy_address: document.getElementById('copy_address'),
        copy_mix_address: document.getElementById('copy_mix_address'),
        copy_mix_secret: document.getElementById('copy_mix_secret'),
        copy_pac_config: document.getElementById('copy_pac_config'),
        mix_proxy: document.getElementById('_mix_proxy'),
        dns_setting_sniffer: document.getElementById('dns-setting-sniffer'),
        dns_setting_respect: document.getElementById('dns-setting-respect'),
        meta_sniffer_on: document.getElementById('meta_sniffer_on'),
        meta_sniffer_off: document.getElementById('meta_sniffer_off'),
        respect_rules_on: document.getElementById('respect_rules_on'),
        respect_rules_off: document.getElementById('respect_rules_off'),
        oc_setting_oversea: document.getElementById('oc-setting-oversea'),
        oc_setting_oversea_0: document.getElementById('oc_setting_oversea_0'),
        oc_setting_oversea_1: document.getElementById('oc_setting_oversea_1'),
        oc_setting_oversea_2: document.getElementById('oc_setting_oversea_2'),
        stream_unlock_setting: document.getElementById('stream-unlock-setting'),
        stream_unlock_on: document.getElementById('stream_unlock_on'),
        stream_unlock_off: document.getElementById('stream_unlock_off'),
        core_version_display: document.getElementById('core-version-display'),
        core_version_text: document.getElementById('core-version-text'),
        plugin_version_display: document.getElementById('plugin-version-display'),
        plugin_version_text: document.getElementById('plugin-version-text')
    };

    var getDashboardBaseURL = ocGetDashboardBaseURL;
    var buildDashboardURL = ocBuildDashboardURL;
    var bytesToSize = ocFormatBytes;
    var debounceButton = ocDebounce;

    var DarkModeDetector = {
        init: function() {
            this.applyDarkMode();
        },

        applyDarkMode: function() {
            ocUpdateTheme();

            var sunIcon = document.getElementById('sun-icon');
            var moonIcon = document.getElementById('moon-icon');
            var autoIcon = document.getElementById('auto-icon');
            if (sunIcon && moonIcon && autoIcon) {
                var theme = localStorage.getItem('oc-theme') || 'auto';
                sunIcon.style.display = 'none';
                moonIcon.style.display = 'none';
                autoIcon.style.display = 'none';

                if (theme === 'light') {
                    sunIcon.style.display = 'inline';
                } else if (theme === 'dark') {
                    moonIcon.style.display = 'inline';
                } else {
                    autoIcon.style.display = 'inline';
                }
            }
        }
    };

    var StateManager = {
        current_status: {},
        cached_proxy_info: null,
        last_request_time: {},
        request_cache: {},
        cache_ttl: 5000,
        pending_requests: new Map(),

        _cachedXHR: function(url, params, callback, force) {
            var cacheKey = params ? url + JSON.stringify(params) : url;
            var now = Date.now();

            if (this.pending_requests.has(cacheKey)) {
                this.pending_requests.get(cacheKey).push(callback);
                return;
            }

            var cacheExists = this.request_cache.hasOwnProperty(cacheKey);
            var isCacheStale = cacheExists && (now - this.last_request_time[cacheKey] >= this.cache_ttl);

            if (!force && cacheExists && !isCacheStale) {
                setTimeout(function() {
                    callback({ status: 200, fromCache: true }, StateManager.request_cache[cacheKey]);
                }, 0);
                return;
            }

            if (!force && cacheExists && isCacheStale) {
                setTimeout(function() {
                    callback({ status: 200, fromCache: true, stale: true }, StateManager.request_cache[cacheKey]);
                }, 0);
                this._refreshCacheSilent(url, params, cacheKey);
                return;
            }

            this.pending_requests.set(cacheKey, [callback]);
            var self = this;

            XHR.get(url, params, function(x, data) {
                var callbacks = self.pending_requests.get(cacheKey) || [];
                self.pending_requests.delete(cacheKey);

                if (x && x.status == 200 && data) {
                    self.request_cache[cacheKey] = data;
                    self.last_request_time[cacheKey] = Date.now();
                }

                callbacks.forEach(function(cb) {
                    try {
                        cb(x, data);
                    } catch (e) {
                        console.warn('StateManager: XHR callback error for ' + cacheKey, e);
                    }
                });
            });
        },

        cachedXHRGet: function(url, callback, force) {
            this._cachedXHR(url, null, callback, force);
        },

        cachedXHRGetWithParams: function(url, params, callback, force) {
            this._cachedXHR(url, params, callback, force);
        },

        clearCache: function(url, params) {
            var cacheKey = params ? url + JSON.stringify(params) : url;
            delete this.request_cache[cacheKey];
            delete this.last_request_time[cacheKey];
        },

        clearAllCache: function() {
            this.request_cache = {};
            this.last_request_time = {};
        },

        hasCache: function(url, params) {
            var cacheKey = params ? url + JSON.stringify(params) : url;
            return !!this.request_cache[cacheKey];
        },

        getCacheAge: function(url, params) {
            var cacheKey = params ? url + JSON.stringify(params) : url;
            if (!this.last_request_time[cacheKey]) {
                return null;
            }
            return Date.now() - this.last_request_time[cacheKey];
        },

        setCacheTTL: function(ttl) {
            this.cache_ttl = ttl;
        },

        batchUpdateDOM: function(updates) {
            for (var i = 0; i < updates.length; i++) {
                var update = updates[i];
                if (update.element && update.content !== undefined) {
                    update.element.innerHTML = update.content;
                }
            }
        },

        _refreshCacheSilent: function(url, params, cacheKey) {
            var self = this;
            XHR.get(url, params, function(x, data) {
                if (x && x.status == 200 && data) {
                    self.request_cache[cacheKey] = data;
                    self.last_request_time[cacheKey] = Date.now();
                }
            });
        },

        cachedXHRGetWithRetry: function(url, params, callback, force, maxRetries) {
            maxRetries = maxRetries || 3;
            var retryCount = 0;
            var self = this;

            function attemptRequest() {
                self.cachedXHRGetWithParams(url, params, function(x, data) {
                    if (x && x.status == 200) {
                        callback(x, data);
                    } else if (retryCount < maxRetries) {
                        retryCount++;
                        setTimeout(attemptRequest, 1000 * retryCount);
                    } else {
                        callback(x, data);
                    }
                }, force && retryCount === 0);
            }

            attemptRequest();
        }
    };

    var WSManager = {
        connections: {},
        connectionStates: {
            CONNECTING: 0,
            CONNECTED: 1,
            DISCONNECTED: 2,
            ERROR: 3
        },
        reconnectDelay: 5000,
        heartbeatInterval: 30000,
        heartbeatTimers: {},
        _ws_connect: false,
        _allowedCloseCodes: [1000, 1001, 1005, 1006],

        isSupported: function() {
            return typeof window.WebSocket !== "undefined";
        },

        createConnection: function(type, url, messageHandler, options) {
            options = options || {};

            this.closeConnection(type);

            if (!this.isSupported()) {
                this.enableFallbackMode();
                return null;
            }

            try {
                var ws = new window.WebSocket(url);

                this.connections[type] = {
                    socket: ws,
                    url: url,
                    messageHandler: messageHandler,
                    state: this.connectionStates.CONNECTING,
                    lastActivity: Date.now(),
                    options: options
                };

                if (ws.addEventListener) {
                    ws.addEventListener('open', this.handleOpen.bind(this, type));
                    ws.addEventListener('message', this.handleMessage.bind(this, type));
                    ws.addEventListener('error', this.handleError.bind(this, type));
                    ws.addEventListener('close', this.handleClose.bind(this, type));
                } else {
                    ws.onopen = this.handleOpen.bind(this, type);
                    ws.onmessage = this.handleMessage.bind(this, type);
                    ws.onerror = this.handleError.bind(this, type);
                    ws.onclose = this.handleClose.bind(this, type);
                }

                return ws;
            } catch (error) {
                this.handleConnectionError(type, error);
                return null;
            }
        },

        startHeartbeat: function(type) {
            var self = this;
            this.stopHeartbeat(type);

            this.heartbeatTimers[type] = setInterval(function() {
                var connection = self.connections[type];
                if (connection && connection.socket && connection.state === self.connectionStates.CONNECTED) {
                    var timeSinceLastActivity = Date.now() - connection.lastActivity;
                    if (timeSinceLastActivity > self.heartbeatInterval * 3) {
                        self.reconnectConnection(type);
                    }
                }
            }, this.heartbeatInterval);

            if (!this._visibilityHandlerAdded) {
                document.addEventListener('visibilitychange', function() {
                    if (document.hidden) {
                        for (var t in self.heartbeatTimers) {
                            self.stopHeartbeat(t);
                        }
                    } else {
                        for (var t in self.connections) {
                            if (self.connections[t] && self.connections[t].state === self.connectionStates.CONNECTED) {
                                self.startHeartbeat(t);
                            }
                        }
                    }
                });
                this._visibilityHandlerAdded = true;
            }
        },

        stopHeartbeat: function(type) {
            if (this.heartbeatTimers[type]) {
                clearInterval(this.heartbeatTimers[type]);
                delete this.heartbeatTimers[type];
            }
        },

        reconnectConnection: function(type) {
            var connection = this.connections[type];
            if (connection) {
                this.createConnection(type, connection.url, connection.messageHandler, connection.options);
            }
        },

        closeConnection: function(type) {
            var connection = this.connections[type];
            if (connection && connection.socket) {
                this.stopHeartbeat(type);
                try {
                    connection.socket.close(1000, 'Normal closure');
                } catch (error) {}
                delete this.connections[type];
            }
        },

        closeAll: function() {
            var types = Object.keys(this.connections);
            for (var i = 0; i < types.length; i++) {
                this.closeConnection(types[i]);
            }
            this._ws_connect = false;
        },

        handleMessage: function(type, event) {
            var connection = this.connections[type];
            if (connection && connection.messageHandler) {
                connection.lastActivity = Date.now();
                try {
                    var data;
                    try {
                        data = JSON.parse(event.data);
                    } catch (e) {
                        data = event.data;
                    }
                    connection.messageHandler({ data: data, raw: event.data, event: event });
                } catch (error) {
                    console.warn('WSManager: message handler error for type=' + type, error);
                }
            }
        },

        handleOpen: function(type, event) {
            var connection = this.connections[type];
            if (connection) {
                connection.state = this.connectionStates.CONNECTED;
                connection.lastActivity = Date.now();
            }
            this.startHeartbeat(type);

            if (!this._ws_connect) {
                var connectedCount = 0;
                for (var t in this.connections) {
                    if (this.connections[t].state === this.connectionStates.CONNECTED) {
                        connectedCount++;
                    }
                }
                if (connectedCount >= 1) {
                    this._ws_connect = true;
                    if (NetworkStatsManager.isEnabled) {
                        NetworkStatsManager.stop();
                    }
                }
            }
        },

        handleError: function(type, error) {
            var connection = this.connections[type];
            if (connection) {
                connection.state = this.connectionStates.ERROR;
            }
            this.stopHeartbeat(type);
            this.scheduleReconnect(type);
            this.enableFallbackMode();
        },

        handleClose: function(type, event) {
            var connection = this.connections[type];
            if (connection) {
                connection.state = this.connectionStates.DISCONNECTED;
            }
            this.stopHeartbeat(type);

            if (this._allowedCloseCodes.indexOf(event.code) === -1) {
                this.scheduleReconnect(type);
            }
        },

        handleConnectionError: function(type, error) {
            var connection = this.connections[type];
            if (connection) {
                connection.state = this.connectionStates.ERROR;
            }
            this.scheduleReconnect(type);
            this.enableFallbackMode();
        },

        scheduleReconnect: function(type) {
            var self = this;
            var connection = this.connections[type];

            if (!connection) {
                this.enableFallbackMode();
                return;
            }

            var url = connection.url;
            var handler = connection.messageHandler;
            var options = connection.options;

            setTimeout(function() {
                self.createConnection(type, url, handler, options);
            }, this.reconnectDelay);
        },

        enableFallbackMode: function() {
            this._ws_connect = false;
            this.closeAll();
            LogManager.startLogDisplay();
            NetworkStatsManager.resetStats();
            if (!NetworkStatsManager.isEnabled) {
                NetworkStatsManager.start();
            }
        },

        getConnectionState: function(type) {
            var connection = this.connections[type];
            return connection ? connection.state : this.connectionStates.DISCONNECTED;
        },

        getAllConnectionStates: function() {
            var states = {};
            for (var type in this.connections) {
                states[type] = this.getConnectionState(type);
            }
            return states;
        },

        hasActiveConnections: function() {
            for (var type in this.connections) {
                if (this.getConnectionState(type) === this.connectionStates.CONNECTED) {
                    return true;
                }
            }
            return false;
        }
    };

    var ConfigFileManager = {
        configList: [],
        currentConfig: '',
        currentConfigIndex: -1,
        selectElement: null,
        rawCurrentConfig: '',

        init: function() {
            this.selectElement = document.getElementById('config_file_select');
            if (this.selectElement) {
                this.loadConfigFileList(true);
                this.setupEventListeners();
            }
        },

        setupEventListeners: function() {
            if (this.selectElement) {
                this.selectElement.addEventListener('change', this.onConfigChange.bind(this));
            }
        },

        loadConfigFileList: function(force) {
            this.updateSelectOptions([{value: '', text: '<%:Collecting data...%>', disabled: true}]);

            var params = {};
            var cached = localStorage.getItem('oc_config_fp');
            if (cached) {
                try {
                    var c = JSON.parse(cached);
                    if (c && c.fp) {
                        var ageCache = localStorage.getItem('oc_config_age');
                        if (ageCache) {
                            try {
                                var a = JSON.parse(ageCache);
                                if (a && a.fp === c.fp) params.fingerprint = c.fp;
                            } catch (e) {}
                        }
                    }
                } catch (e) {}
            }

            StateManager.cachedXHRGetWithParams('<%=luci.dispatcher.build_url("admin", "services", "openclash", "config_file_list")%>', params, function(x, data) {
                if (x && x.status == 200) {
                    ConfigFileManager.handleConfigListResponse(data, params.fingerprint);
                } else {
                    ConfigFileManager.handleConfigListError();
                }
            }, !!force);
        },

        handleConfigListResponse: function(data, sentFp) {
            try {
                var configFiles = [];
                var currentConfigFile = '';

                if (data.config_files && Array.isArray(data.config_files)) {
                    configFiles = data.config_files;
                }

                if (data.current_config) {
                    currentConfigFile = data.current_config;
                }

                var fp = data.fingerprint;
                var ageFromCache = false;
                if (fp && sentFp && fp === sentFp) {
                    var ageCache = localStorage.getItem('oc_config_age');
                    if (ageCache) {
                        try {
                            var ac = JSON.parse(ageCache);
                            if (ac && ac.fp === fp && ac.ages) {
                                for (var j = 0; j < configFiles.length; j++) {
                                    var fname = configFiles[j].name.replace(/\.(yaml|yml)$/i, '');
                                    if (ac.ages.hasOwnProperty(fname)) {
                                        configFiles[j].age = ac.ages[fname];
                                    }
                                }
                                ageFromCache = true;
                            }
                        } catch (e) {}
                    }
                }

                if (fp && !ageFromCache) {
                    var ages = {};
                    for (var k = 0; k < configFiles.length; k++) {
                        var fn = configFiles[k].name.replace(/\.(yaml|yml)$/i, '');
                        ages[fn] = configFiles[k].age || false;
                    }
                    (window.requestIdleCallback || function(cb) { setTimeout(cb, 1); })(function() {
                        localStorage.setItem('oc_config_fp', JSON.stringify({fp: fp}));
                        localStorage.setItem('oc_config_age', JSON.stringify({fp: fp, ages: ages}));
                    });
                }

                var currentFileInfo = null;

                this.rawCurrentConfig = currentConfigFile;
                this.configList = configFiles;
                window.configFiles = configFiles;
                this.currentConfig = currentConfigFile;
                this.currentConfigIndex = -1;

                this.toggleEmptyState(configFiles.length === 0);

                this.updateConfigSelect(configFiles, currentConfigFile);

                if (configFiles.length > 0) {
                    if (currentConfigFile) {
                        for (var i = 0; i < configFiles.length; i++) {
                            var file = configFiles[i];
                            var filePath = typeof file === 'string' ? file : (file.path || file.filepath || file);

                            if (filePath === currentConfigFile) {
                                this.currentConfigIndex = i;
                                if (typeof file === 'object' && file.mtime && file.size) {
                                    currentFileInfo = {
                                        mtime: file.mtime,
                                        size: file.size,
                                        age: file.age
                                    };
                                }
                                break;
                            }
                        }
                    }

                    this.updateSubscriptionDisplay(currentConfigFile, currentFileInfo);

                    if (SubscriptionManager.currentConfigFile !== currentConfigFile) {
                        SubscriptionManager.currentConfigFile = currentConfigFile;
                        SubscriptionManager.getSubscriptionInfo();
                    }
                } else {
                    this.hideSubscriptionDisplay();
                    SubscriptionManager.currentConfigFile = '';
                }

                this.updateNavigationArrows();

                if (this._retryGetConfigList) {
                    clearInterval(this._retryGetConfigList);
                    this._retryGetConfigList = null;
                }

            } catch (e) {
                this.handleConfigListError();
            }
        },

        handleConfigListError: function() {
            this.updateSelectOptions([
                {value: '', text: '<%:Failed to load config files%>', disabled: true}
            ]);

            this.toggleEmptyState(true);
            this.hideSubscriptionDisplay();

            if (!this._retryGetConfigList) {
                var self = this;
                this._retryGetConfigList = setInterval(function() {
                    self.loadConfigFileList(true);
                }, 5000);
            }
        },

        toggleEmptyState: function(isEmpty) {
            var emptyStateElement = document.getElementById('config-file-empty-state');
            var configFileBottom = document.querySelector('.config-file-bottom');
            var configFileContent = document.querySelector('.config-file-content');

            if (isEmpty) {
                if (emptyStateElement) {
                    emptyStateElement.style.display = 'flex';
                }
                if (configFileBottom) {
                    configFileBottom.style.display = 'none';
                }
                if (configFileContent) {
                    configFileContent.classList.add('empty-state');
                }
                this.hideSubscriptionDisplay();
            } else {
                if (emptyStateElement) {
                    emptyStateElement.style.display = 'none';
                }
                if (configFileBottom) {
                    configFileBottom.style.display = 'flex';
                }
                if (configFileContent) {
                    configFileContent.classList.remove('empty-state');
                }
            }
        },

        hideSubscriptionDisplay: function() {
            var subscriptionDisplay = document.getElementById('subscription-info-display');
            if (subscriptionDisplay) {
                subscriptionDisplay.style.display = 'none';
            }
        },

        updateConfigSelect: function(configFiles, currentConfig) {
            var options = [];

            if (!configFiles || configFiles.length === 0) {
                options.push({
                    value: '',
                    text: '<%:No config files found%>',
                    disabled: true
                });
            } else {
                options.push({
                    value: '',
                    text: '<%:Please select a config file%>',
                    disabled: false
                });

                configFiles.forEach(function(file) {
                    var fileName, filePath;

                    if (typeof file === 'string') {
                        fileName = file;
                        filePath = file;
                    } else {
                        fileName = file.name || file.filename || file.path || file;
                        filePath = file.path || file.filepath || file.name || file;
                    }

                    var displayName = fileName;

                    options.push({
                        value: filePath,
                        text: displayName,
                        disabled: false,
                        selected: filePath === currentConfig || fileName === currentConfig
                    });
                });
            }

            this.updateSelectOptions(options);
        },

        updateSelectOptions: function(options) {
            if (!this.selectElement) return;

            this.selectElement.innerHTML = '';

            options.forEach(function(option) {
                var optionElement = document.createElement('option');
                optionElement.value = option.value;
                optionElement.textContent = option.text;
                optionElement.disabled = option.disabled || false;
                optionElement.selected = option.selected || false;

                this.selectElement.appendChild(optionElement);
            }, this);
        },

        updateSubscriptionDisplay: function(configFile, fileInfo) {
            var container = document.getElementById('subscription-info-display');
            var configNameElement = document.getElementById('current-config-name');
            var fileModifyTimeElement = document.getElementById('file-modify-time');
            var detailsSection = document.getElementById('subscription-info-details');
            var configFileContent = document.querySelector('.config-file-content');
            var ageFileIconElement = document.getElementById('age-file-icon');

            if (!container) return;

            if (this.configList.length === 0) {
                container.style.display = 'none';
                return;
            }

            if (!configFile) {
                container.style.display = 'none';
                return;
            }

            if (configFileContent && configFileContent.classList.contains('empty-state')) {
                container.style.display = 'none';
                return;
            }

            container.style.display = 'flex';

            if (configNameElement) {
                var displayName = this.formatDisplayName(configFile);
                if (configFile === this.rawCurrentConfig) {
                    configNameElement.innerHTML =
                        '<span class="selected-file-dot"></span>' + displayName;
                } else {
                    configNameElement.textContent = displayName;
                }
                if (configNameElement.scrollWidth > configNameElement.clientWidth) {
                    configNameElement.title = displayName;
                } else {
                    configNameElement.title = '';
                }
            }

            if (fileInfo) {
                if (fileModifyTimeElement) {
                    var modifyTime = this.formatUnixTime(fileInfo.mtime);
                    fileModifyTimeElement.textContent = '<%:Update Time%>: ' + modifyTime;
                    if (fileModifyTimeElement.scrollWidth > fileModifyTimeElement.clientWidth) {
                        fileModifyTimeElement.title = fileModifyTimeElement.textContent;
                    } else {
                        fileModifyTimeElement.title = '';
                    }
                }

                if (detailsSection) {
                    detailsSection.style.display = 'flex';
                }

                if (ageFileIconElement) {
                    if (fileInfo.age) {
                        ageFileIconElement.innerHTML = '<svg width="16" height="16"><use href="#oc-icon-shield-check"/></svg>';
                        ageFileIconElement.title = '<%:Age Encryption File%>';
                        ageFileIconElement.style.display = 'inline-flex';
                    } else {
                        ageFileIconElement.innerHTML = '';
                        ageFileIconElement.style.display = 'none';
                    }
                }
            } else {
                if (fileModifyTimeElement) {
                    fileModifyTimeElement.textContent = '<%:Update Time%> --';
                }

                if (detailsSection) {
                    detailsSection.style.display = 'none';
                }

                if (ageFileIconElement) {
                    ageFileIconElement.innerHTML = '';
                    ageFileIconElement.style.display = 'none';
                }
            }
        },

        formatUnixTime: ocFormatUnixTime,

        formatFileSize: ocFormatFileSize,

        formatDisplayName: function(fileName) {
            if (!fileName) return '<%:Unknown%>';

            var name = fileName.split('/').pop().split('\\').pop();

            //name = name.replace(/\.(yaml|yml)$/i, '');

            if (name.length > 30) {
                name = name.substring(0, 27) + '...';
            }

            return name;
        },

        onConfigChange: function(event) {
            var selectedValue = event.target.value;

            if (selectedValue) {
                this.currentConfig = selectedValue;

                for (var i = 0; i < this.configList.length; i++) {
                    var file = this.configList[i];
                    var filePath = typeof file === 'string' ? file : (file.path || file.filepath || file);

                    if (filePath === selectedValue) {
                        this.currentConfigIndex = i;
                        break;
                    }
                }

                var selectedFileInfo = this.getConfigFileInfo(selectedValue);
                this.updateSubscriptionDisplay(selectedValue, selectedFileInfo);
                this.updateNavigationArrows();

                if (SubscriptionManager.currentConfigFile !== selectedValue) {
                    SubscriptionManager.currentConfigFile = selectedValue;
                    var detailsSection = document.getElementById('subscription-info-details');
                    if (detailsSection) {
                        detailsSection.style.display = 'none';
                    }
                    SubscriptionManager.getSubscriptionInfo();
                }
                OverwriteSubscribeManager.render(OverwriteSubscribeManager.data);
            } else {
                this.currentConfig = '';
                this.currentConfigIndex = -1;
                SubscriptionManager.currentConfigFile = '';
                this.hideSubscriptionDisplay();
                this.updateNavigationArrows();
                OverwriteSubscribeManager.render(OverwriteSubscribeManager.data);
            }
        },

        getConfigFileInfo: function(selectedValue) {
            var selectedFileInfo = null;
            for (var i = 0; i < this.configList.length; i++) {
                var file = this.configList[i];
                var filePath = typeof file === 'string' ? file : (file.path || file.filepath || file);
                if (filePath === selectedValue) {
                    if (typeof file === 'object' && file.mtime && file.size) {
                        selectedFileInfo = {
                            mtime: file.mtime,
                            size: file.size,
                            age: file.age
                        };
                    }
                    break;
                }
            }
            return selectedFileInfo;
        },

        updateNavigationArrows: function() {
            var prevArrow = document.getElementById('subscription-prev-arrow');
            var nextArrow = document.getElementById('subscription-next-arrow');

            if (!prevArrow || !nextArrow) return;

            var hasMultipleConfigs = this.configList.length > 1;
            var currentIndex = this.currentConfigIndex;

            if (!hasMultipleConfigs || currentIndex === -1) {
                prevArrow.style.display = 'none';
                nextArrow.style.display = 'none';
                return;
            }

            prevArrow.style.display = 'block';
            nextArrow.style.display = 'block';

            prevArrow.classList.remove('disabled');
            nextArrow.classList.remove('disabled');
        },

        switchToConfigByIndex: function(index) {
            if (this.configList.length === 0) {
                return false;
            }

            if (index < 0) {
                index = this.configList.length - 1; 
            } else if (index >= this.configList.length) {
                index = 0;
            }

            var file = this.configList[index];
            var filePath = typeof file === 'string' ? file : (file.path || file.filepath || file);
            if (this.selectElement) {
                this.selectElement.value = filePath;
            }

            this.currentConfig = filePath;
            this.currentConfigIndex = index;

            var fileInfo = this.getConfigFileInfo(filePath);
            this.updateSubscriptionDisplay(filePath, fileInfo);
            this.updateNavigationArrows();

            if (SubscriptionManager.currentConfigFile !== filePath) {
                SubscriptionManager.currentConfigFile = filePath;
                var detailsSection = document.getElementById('subscription-info-details');
                if (detailsSection) {
                    detailsSection.style.display = 'none';
                }
                SubscriptionManager.getSubscriptionInfo();
            }

            OverwriteSubscribeManager.render(OverwriteSubscribeManager.data);

            return true;
        },

        refreshConfigList: function() {
            this.loadConfigFileList(false);
        },

        getCurrentConfig: function() {
            return this.currentConfig;
        },

        getSelectedConfig: function() {
            return this.selectElement ? this.selectElement.value : '';
        }
    };

    var SubscriptionManager = {
        currentConfigFile: '',
        retryCount: 0,
        maxRetries: 5,
        updateTimer: null,
        isInitialized: false,

        enableOverflowDrag: function(element) {
            if (!element) return;

            function updateDragState() {
                var isOverflowing = element.scrollWidth > element.clientWidth + 1;
                if (isOverflowing) {
                    element.classList.add('is-draggable');
                } else {
                    element.classList.remove('is-draggable');
                    element.classList.remove('dragging');
                    element.scrollLeft = 0;
                }
            }

            if (!element._dragBound) {
                var isDragging = false;
                var startX = 0;
                var startScrollLeft = 0;
                var touchIdentifier = null;

                function endDrag() {
                    if (!isDragging) return;
                    isDragging = false;
                    touchIdentifier = null;
                    element.classList.remove('dragging');
                }

                element.addEventListener('mousedown', function(e) {
                    if (!element.classList.contains('is-draggable')) return;
                    isDragging = true;
                    startX = e.pageX - element.getBoundingClientRect().left;
                    startScrollLeft = element.scrollLeft;
                    element.classList.add('dragging');
                    e.preventDefault();
                });

                element.addEventListener('mousemove', function(e) {
                    if (!isDragging) return;
                    var x = e.pageX - element.getBoundingClientRect().left;
                    var walk = (x - startX) * 1.2;
                    element.scrollLeft = startScrollLeft - walk;
                    e.preventDefault();
                });

                element.addEventListener('mouseleave', function() {
                    endDrag();
                });

                element.addEventListener('mouseup', function() {
                    endDrag();
                });

                element.addEventListener('touchstart', function(e) {
                    if (!element.classList.contains('is-draggable')) return;
                    if (!e.touches || e.touches.length !== 1) return;

                    var touch = e.touches[0];
                    isDragging = true;
                    touchIdentifier = touch.identifier;
                    startX = touch.pageX - element.getBoundingClientRect().left;
                    startScrollLeft = element.scrollLeft;
                    element.classList.add('dragging');
                }, { passive: true });

                element.addEventListener('touchmove', function(e) {
                    if (!isDragging || !e.touches || e.touches.length === 0) return;

                    var touch = null;
                    for (var i = 0; i < e.touches.length; i++) {
                        if (e.touches[i].identifier === touchIdentifier) {
                            touch = e.touches[i];
                            break;
                        }
                    }

                    if (!touch) return;

                    var x = touch.pageX - element.getBoundingClientRect().left;
                    var walk = (x - startX) * 1.2;
                    element.scrollLeft = startScrollLeft - walk;
                    e.preventDefault();
                }, { passive: false });

                element.addEventListener('touchend', function() {
                    endDrag();
                });

                element.addEventListener('touchcancel', function() {
                    endDrag();
                });

                element.addEventListener('dragstart', function(e) {
                    e.preventDefault();
                });

                element._dragBound = true;
            }

            updateDragState();
        },

        init: function() {
            if (this.isInitialized) return;
            this.isInitialized = true;
            SubscriptionManager.loadSubscriptionInfo();
            SubscriptionManager.startAutoUpdate();
        },

        loadSubscriptionInfo: function() {
            var currentConfig = ConfigFileManager.getCurrentConfig() || ConfigFileManager.getSelectedConfig();
            if (currentConfig !== this.currentConfigFile) {
                this.currentConfigFile = currentConfig;
                this.getSubscriptionInfo();
            }
        },

        getSubscriptionInfo: function() {
            if (ConfigFileManager.configList.length === 0) {
                return;
            }

            if (!this.currentConfigFile) return;

            var requestConfigFile = this.currentConfigFile;
            var filename = this.extractFilename(this.currentConfigFile);
            if (!filename) return;

            var cachedData = localStorage.getItem('sub_info_' + filename);
            var shouldFetchNew = true;

            if (cachedData) {
                try {
                    var parsedData = JSON.parse(cachedData);

                    if (parsedData.providers) {
                        if (parsedData.providers.length > 0) {
                            if (this.currentConfigFile === requestConfigFile) {
                                this.displaySubscriptionInfo(parsedData);
                            }
                        } else {
                            if (this.currentConfigFile === requestConfigFile) {
                                this.showNoInfo();
                            }
                        }
                    }

                    if (parsedData.get_time) {
                        var currentTime = Math.floor(Date.now() / 1000);
                        var cacheTime = parseInt(parsedData.get_time);
                        var timeDiff = currentTime - cacheTime;
                        var halfHourInSeconds = 30 * 60;

                        if (timeDiff <= halfHourInSeconds) {
                            shouldFetchNew = false;
                        }
                    }
                } catch (e) {
                    shouldFetchNew = true;
                }
            }

            if (shouldFetchNew) {
                if (!cachedData) {
                    this.showLoading();
                }

                StateManager.cachedXHRGetWithParams('<%=luci.dispatcher.build_url("admin", "services", "openclash", "sub_info_get")%>', {filename: filename}, function(x, status) {
                    if (SubscriptionManager.currentConfigFile !== requestConfigFile) {
                        return;
                    }

                    var needsErrorHandling = false;
                    if (x && x.status == 200 && status.providers.length > 0) {
                        var newProvidersLength = status.providers.length;
                        var cachedProvidersLength = (parsedData && parsedData.providers) ? parsedData.providers.length : 0;

                        if (newProvidersLength !== cachedProvidersLength && cachedProvidersLength > newProvidersLength) {
                            needsErrorHandling = true;
                        }

                        if (!needsErrorHandling) {
                            SubscriptionManager.retryCount = 0;
                            (window.requestIdleCallback || function(cb) { setTimeout(cb, 1); })(function() { localStorage.setItem('sub_info_' + filename, JSON.stringify(status)); });
                            if (status.providers && status.providers.length === 0) {
                                SubscriptionManager.showNoInfo();
                            }
                            SubscriptionManager.displaySubscriptionInfo(status);
                        }
                    } else {
                        needsErrorHandling = true;
                        if (!cachedData) {
                            SubscriptionManager.showNoInfo();
                        }
                    }

                    if (needsErrorHandling) {
                        SubscriptionManager.handleError(status);
                    }
                }, true);
            }
        },

        displaySubscriptionInfo: function(data) {
            if (ConfigFileManager.configList.length === 0) {
                return;
            }

            var container = document.getElementById('subscription-info-display');
            var progressSection = document.getElementById('subscription-progress-section');
            var detailsSection = document.getElementById('subscription-info-details');

            if (!container) return;

            var configFileContent = document.querySelector('.config-file-content');
            if (configFileContent && configFileContent.classList.contains('empty-state')) {
                return;
            }

            container.style.display = 'flex';

            if (data && data.providers && data.providers.length > 0) {
                if (data.providers.length > 1 || data.providers[0].provider_name) {
                    this.displayMultipleProviders(data.providers, progressSection);
                } else {
                    this.displaySingleProvider(data.providers[0], progressSection);
                }
            } else {
                progressSection.style.display = 'none';
            }

            if (detailsSection) {
                detailsSection.style.display = 'flex';
            }
        },

        displaySingleProvider: function(provider, progressSection) {
            if (!progressSection) return;

            progressSection.innerHTML = '';
            progressSection.style.display = 'flex';
            progressSection.className = 'subscription-progress';

            var progressBar = document.createElement('div');
            progressBar.className = 'subscription-progress-bar';

            var progressFill = document.createElement('div');
            progressFill.className = 'subscription-progress-fill';
            progressBar.appendChild(progressFill);

            var infoText = document.createElement('div');
            infoText.className = 'subscription-info-text';

            progressSection.appendChild(progressBar);
            progressSection.appendChild(infoText);

            var percent = parseFloat(provider.percent) || 0;
            var used = String(provider.surplus || provider.used || '0 B');
            var total = String(provider.total || '0 B');
            var expire = String(provider.expire || '');
            var daysLeft = parseInt(provider.day_left, 10);
            if (isNaN(daysLeft)) daysLeft = 0;

            progressFill.style.width = percent + '%';
            progressFill.className = 'subscription-progress-fill ' +
                (percent >= 50 ? 'high' : (percent >= 20 ? 'medium' : 'low'));

            var infoString = used + ' / ' + total + ' (' + percent + '%)';
            var tooltipString = infoString;
            if (expire && expire !== 'null' && daysLeft > 0) {
                infoString += ' • ' + expire + ' (<%:Remaining%> ' + daysLeft + ' <%:days%>)';
                tooltipString += '\n' + expire + ' (<%:Remaining%> ' + daysLeft + ' <%:days%>)';
            }

            infoText.textContent = infoString;
            infoText.title = tooltipString;
            this.enableOverflowDrag(infoText);
        },

        displayMultipleProviders: function(providers, progressSection) {
            if (!progressSection) return;

            var displayProviders = providers;
            var providerCount = displayProviders.length;

            progressSection.innerHTML = '';
            progressSection.style.display = 'flex';
            progressSection.className = 'subscription-providers-grid';
            progressSection.setAttribute('data-count', providerCount);

            displayProviders.forEach(function(provider) {
                var card = document.createElement('div');
                card.className = 'provider-card';

                var cardName = document.createElement('div');
                var providerName = String(provider.provider_name || 'Unknown');
                cardName.className = 'subscription-info-title';
                cardName.textContent = providerName;
                cardName.title = providerName;
                card.appendChild(cardName);

                var progressBar = document.createElement('div');
                progressBar.className = 'subscription-progress-bar';

                var progressFill = document.createElement('div');
                var percent = parseFloat(provider.percent) || 0;
                progressFill.style.width = percent + '%';
                progressFill.className = 'subscription-progress-fill ' +
                    (percent >= 50 ? 'high' : (percent >= 20 ? 'medium' : 'low'));

                progressBar.appendChild(progressFill);
                card.appendChild(progressBar);

                var infoText = document.createElement('div');
                infoText.className = 'subscription-info-text';

                var used = String(provider.used || '0 B');
                var total = String(provider.total || 'N/A');
                var expire = String(provider.expire || '');
                var daysLeft = parseInt(provider.day_left, 10);
                if (isNaN(daysLeft)) daysLeft = 0;

                var infoString = used + ' / ' + total + ' (' + percent + '%)';
                if (expire && expire !== 'null' && daysLeft > 0) {
                    infoString += ' • ' + expire + ' (<%:Remaining%> ' + daysLeft + ' <%:days%>)';
                }

                var tooltipString = used + ' / ' + total + ' (' + percent + '%)';
                if (expire && expire !== 'null' && daysLeft > 0) {
                    tooltipString += '\n' + expire + ' (<%:Remaining%> ' + daysLeft + ' <%:days%>)';
                }

                infoText.textContent = infoString;
                infoText.title = tooltipString;
                SubscriptionManager.enableOverflowDrag(infoText);

                card.appendChild(infoText);
                progressSection.appendChild(card);
            });

            if (providerCount > 3) {
                this.initDragScroll(progressSection);
            }
        },

        initDragScroll: function(element) {
            var isDragging = false;
            var startX;
            var scrollLeft;
            var hasMoved = false;

            var onMouseDown = function(e) {
                if (e.target.tagName === 'A' || e.target.tagName === 'BUTTON') {
                    return;
                }

                isDragging = true;
                hasMoved = false;
                startX = e.pageX - element.offsetLeft;
                scrollLeft = element.scrollLeft;
                element.classList.add('dragging');
            };

            var onMouseMove = function(e) {
                if (!isDragging) return;

                var x = e.pageX - element.offsetLeft;
                var walk = (x - startX) * 1.5;

                if (Math.abs(walk) > 5) {
                    hasMoved = true;
                    e.preventDefault();
                    element.scrollLeft = scrollLeft - walk;
                }
            };

            var onMouseUp = function() {
                isDragging = false;
                element.classList.remove('dragging');
            };

            var onMouseLeave = function() {
                if (isDragging) {
                    isDragging = false;
                    element.classList.remove('dragging');
                }
            };

            element.addEventListener('mousedown', onMouseDown);
            element.addEventListener('mousemove', onMouseMove);
            element.addEventListener('mouseup', onMouseUp);
            element.addEventListener('mouseleave', onMouseLeave);

            element.addEventListener('dragstart', function(e) {
                e.preventDefault();
            });
        },

        showNoInfo: function() {
            if (ConfigFileManager.configList.length === 0) {
                return;
            }

            var container = document.getElementById('subscription-info-display');
            var progressSection = document.getElementById('subscription-progress-section');
            var detailsSection = document.getElementById('subscription-info-details');

            if (!container) return;

            var configFileContent = document.querySelector('.config-file-content');
            if (configFileContent && configFileContent.classList.contains('empty-state')) {
                return;
            }

            container.style.display = 'flex';

            if (progressSection) {
                progressSection.innerHTML = '<div class="subscription-no-info"><%:No Sub Info Found%></div>';
                progressSection.style.display = 'flex';
                progressSection.className = 'subscription-progress';
            }

            if (detailsSection) {
                var fileModifyTimeElement = document.getElementById('file-modify-time');

                var hasFileInfo = false;
                if (fileModifyTimeElement) {
                    var modifyTimeText = fileModifyTimeElement.textContent || '';
                    hasFileInfo = !modifyTimeText.includes('--');
                }

                detailsSection.style.display = hasFileInfo ? 'flex' : 'none';
            }
        },

        showLoading: function() {
            if (ConfigFileManager.configList.length === 0) {
                return;
            }

            var container = document.getElementById('subscription-info-display');
            var progressSection = document.getElementById('subscription-progress-section');
            var detailsSection = document.getElementById('subscription-info-details');

            if (!container) return;

            var configFileContent = document.querySelector('.config-file-content');
            if (configFileContent && configFileContent.classList.contains('empty-state')) {
                return;
            }

            container.style.display = 'flex';

            if (progressSection) {
                progressSection.innerHTML = '<div class="subscription-loading"><%:Collecting data...%></div>';
                progressSection.style.display = 'flex';
                progressSection.className = 'subscription-progress';
            }

            if (detailsSection) {
                detailsSection.style.display = 'none';
            }
        },

        handleError: function(status) {
            if (this.retryCount >= this.maxRetries) {
                this.retryCount = 0;
                if (this.currentConfigFile && status && status.providers) {
                    var errFilename = this.extractFilename(this.currentConfigFile);
                    if (errFilename) {
                        (window.requestIdleCallback || function(cb) { setTimeout(cb, 1); })(function() { localStorage.setItem('sub_info_' + errFilename, JSON.stringify(status)); });
                    }
                }
                if (!status.providers || status.providers.length === 0) {
                    this.showNoInfo();
                }
                if (status.providers && status.providers.length > 0) {
                    SubscriptionManager.displaySubscriptionInfo(status);
                }
            } else {
                this.retryCount++;
                setTimeout(function() {
                    SubscriptionManager.getSubscriptionInfo();
                }, 5000);
            }
        },

        extractFilename: function(path) {
            if (!path) return '';
            var parts = path.split('/');
            var filename = parts[parts.length - 1];

            if (filename.endsWith('.yaml')) {
                filename = filename.slice(0, -5);
            } else if (filename.endsWith('.yml')) {
                filename = filename.slice(0, -4);
            }

            return filename;
        },

        startAutoUpdate: function() {
            if (this.updateTimer) {
                clearTimeout(this.updateTimer);
            }

            this.updateTimer = setTimeout(function() {
                SubscriptionManager.getSubscriptionInfo();
                SubscriptionManager.startAutoUpdate();
            }, 60000 * 15);
        },

        stopAutoUpdate: function() {
            if (this.updateTimer) {
                clearTimeout(this.updateTimer);
                this.updateTimer = null;
            }
        }
    };

    var OverwriteSubscribeManager = {
        container: null,
        data: null,
        isLoading: false,
        _dragBound: false,
        _isDragging: false,
        _dragStartX: 0,
        _dragStartScrollLeft: 0,
        _touchIdentifier: null,

        init: function() {
            this.container = document.getElementById('subscription-overwrite-tags');
            if (!this.container) return;
            this.bindDragScroll();
            this.load(false);
        },

        hasOverflow: function() {
            if (!this.container) return false;
            return this.container.scrollWidth > this.container.clientWidth + 1;
        },

        updateDragScrollState: function() {
            if (!this.container) return;
            if (this.hasOverflow()) {
                this.container.classList.add('drag-scroll-enabled');
            } else {
                this.container.classList.remove('drag-scroll-enabled');
                this.container.classList.remove('dragging');
            }
        },

        bindDragScroll: function() {
            if (!this.container || this._dragBound) {
                return;
            }

            var self = this;

            function endDrag() {
                if (!self._isDragging) return;
                self._isDragging = false;
                self._touchIdentifier = null;
                self.container.classList.remove('dragging');
            }

            this.container.addEventListener('mousedown', function(e) {
                if (!self.hasOverflow()) {
                    return;
                }
                self._isDragging = true;
                self._dragStartX = e.pageX - self.container.offsetLeft;
                self._dragStartScrollLeft = self.container.scrollLeft;
                self.container.classList.add('dragging');
                e.preventDefault();
            });

            this.container.addEventListener('mousemove', function(e) {
                if (!self._isDragging) {
                    return;
                }
                var x = e.pageX - self.container.offsetLeft;
                var walk = (x - self._dragStartX) * 1.5;
                self.container.scrollLeft = self._dragStartScrollLeft - walk;
                e.preventDefault();
            });

            this.container.addEventListener('mouseleave', function() {
                endDrag();
            });

            this.container.addEventListener('mouseup', function() {
                endDrag();
            });

            this.container.addEventListener('touchstart', function(e) {
                if (!self.hasOverflow() || !e.touches || e.touches.length !== 1) {
                    return;
                }
                var touch = e.touches[0];
                self._isDragging = true;
                self._touchIdentifier = touch.identifier;
                self._dragStartX = touch.pageX - self.container.offsetLeft;
                self._dragStartScrollLeft = self.container.scrollLeft;
                self.container.classList.add('dragging');
            }, { passive: true });

            this.container.addEventListener('touchmove', function(e) {
                if (!self._isDragging || !e.touches || e.touches.length === 0) {
                    return;
                }

                var touch = null;
                for (var i = 0; i < e.touches.length; i++) {
                    if (e.touches[i].identifier === self._touchIdentifier) {
                        touch = e.touches[i];
                        break;
                    }
                }
                if (!touch) {
                    return;
                }

                var x = touch.pageX - self.container.offsetLeft;
                var walk = (x - self._dragStartX) * 1.5;
                self.container.scrollLeft = self._dragStartScrollLeft - walk;
                e.preventDefault();
            }, { passive: false });

            this.container.addEventListener('touchend', function() {
                endDrag();
            });

            this.container.addEventListener('touchcancel', function() {
                endDrag();
            });

            this.container.addEventListener('dragstart', function(e) {
                e.preventDefault();
            });

            this._dragBound = true;
        },

        load: function(force) {
            if (!this.container || this.isLoading) {
                return;
            }

            this.isLoading = true;
            var self = this;

            StateManager.cachedXHRGet('<%=luci.dispatcher.build_url("admin", "services", "openclash", "overwrite_subscribe_info")%>', function(x, status) {
                self.isLoading = false;
                if (x && x.status == 200 && status && status.status === 'success' && status.data) {
                    self.data = status.data;
                    self.render(status.data);
                } else {
                    self.render(null);
                }
            }, !!force);
        },

        getTypeClass: function(type) {
            var normalizedType = String(type || '').toLowerCase();
            if (normalizedType === 'http') return 'type-http';
            if (normalizedType === 'file') return 'type-file';
            return 'type-unknown';
        },

        parseModuleConfigList: function(configValue) {
            if (!Array.isArray(configValue)) {
                return [];
            }
            return configValue.map(function(item) {
                return String(item || '').trim();
            }).filter(function(item) {
                return !!item;
            });
        },

        isModuleForCurrentConfig: function(moduleInfo, currentConfigPath) {
            var configList = this.parseModuleConfigList(moduleInfo ? moduleInfo.config : null);
            if (!configList.length) {
                return true;
            }

            if (configList.indexOf('all') !== -1) {
                return true;
            }

            var fullPath = String(currentConfigPath || '').trim();
            if (!fullPath) {
                return false;
            }

            return configList.indexOf(fullPath) !== -1;
        },

        getEnabledModules: function(data) {
            if (!data || typeof data !== 'object') {
                return [];
            }

            var currentConfigPath = ConfigFileManager.getCurrentConfig() || ConfigFileManager.getSelectedConfig();

            var modules = [];
            for (var moduleName in data) {
                if (!Object.prototype.hasOwnProperty.call(data, moduleName)) {
                    continue;
                }

                var moduleInfo = data[moduleName] || {};
                if (parseInt(moduleInfo.enable, 10) === 1 && this.isModuleForCurrentConfig(moduleInfo, currentConfigPath)) {
                    modules.push({
                        name: moduleName,
                        type: moduleInfo.type || 'unknown',
                        order: parseInt(moduleInfo.order, 10) || 0
                    });
                }
            }

            modules.sort(function(a, b) {
                if (a.order === b.order) {
                    return String(a.name).localeCompare(String(b.name));
                }
                return a.order - b.order;
            });

            return modules;
        },

        render: function(data) {
            if (!this.container) return;

            var enabledModules = this.getEnabledModules(data);
            this.container.innerHTML = '';

            if (enabledModules.length === 0) {
                this.container.style.display = 'none';
                this.updateDragScrollState();
                return;
            }

            for (var i = 0; i < enabledModules.length; i++) {
                var moduleItem = enabledModules[i];
                var tag = document.createElement('span');
                tag.className = 'subscription-overwrite-tag ' + this.getTypeClass(moduleItem.type);
                tag.title = moduleItem.name;
                tag.textContent = moduleItem.name;
                if (moduleItem.type == 'http') {
                    tag.title += ' [<%:HTTP Module%>]';
                } else if (moduleItem.type == 'file') {
                    tag.title += ' [<%:File Module%>]';
                } else {
                    tag.title += ' [<%:Unknown%>]';
                }
                this.container.appendChild(tag);
            }

            this.container.style.display = 'flex';
            this.updateDragScrollState();
        }
    };

    var LogManager = {
        isPolling: false,
        pollTimer: null,
        maxPollTime: 60000,
        pollInterval: 1000,
        startTime: 0,
        lastLogContent: '',
        retryCount: 0,
        maxRetries: 3,
        retryDelay: 1000,

        startLogDisplay: function(initialMessage) {
            if (initialMessage) {
                if (this.isPolling) {
                    this.stopLogDisplay();
                }
            } else {
                if (this.isPolling) {
                    return;
                }
            }
            if (this.pollTimer) {
                clearTimeout(this.pollTimer);
                this.pollTimer = null;
            }

            StateManager.clearCache('<%=luci.dispatcher.build_url("admin", "services", "openclash", "startlog")%>');

            if (DOMCache.oclog && initialMessage) {
                DOMCache.oclog.style.display = 'inline-flex';
                DOMCache.oclog.innerHTML = '<b style="color:var(--tip-color)">' + initialMessage + '</b>';
            }

            this.isPolling = true;
            this.startTime = Date.now();
            this.lastLogContent = '';
            this.retryCount = 0;

            var self = this;
            setTimeout(function() {
                if (self.isPolling) {
                    self.pollLog();
                }
            }, 2000);
        },

        stopLogDisplay: function() {
            this.isPolling = false;
            if (this.pollTimer) {
                clearTimeout(this.pollTimer);
                this.pollTimer = null;
            }
            this.lastLogContent = '';
            this.retryCount = 0;
            if (DOMCache.oclog) {
                setTimeout(function() {
                    if (!LogManager.isPolling) {
                        DOMCache.oclog.style.display = 'none';
                    }
                }, 1000);
            }
        },

        pollLog: function() {
            if (!this.isPolling) return;

            if (Date.now() - this.startTime > this.maxPollTime) {
                this.stopLogDisplay();
                return;
            }

            var self = this;
            StateManager.cachedXHRGet('<%=luci.dispatcher.build_url("admin", "services", "openclash", "startlog")%>', function(x, status) {
                if (!self.isPolling) return;

                if (x && x.status == 200 && status && typeof status.startlog !== 'undefined') {
                    var logContent = (status.startlog || '').trim();

                    if (self.checkForErrors(logContent)) {
                        self.stopLogDisplay();
                        return;
                    }

                    var contentToDisplay = logContent.replace('##FINISH##', '').trim();
                    if (contentToDisplay && contentToDisplay !== self.lastLogContent) {
                        self.displayLog(contentToDisplay);
                    }

                    if (logContent.includes('##FINISH##')) {
                        setTimeout(function() {
                            self.stopLogDisplay();
                        }, 3000);
                        return;
                    }

                    if (logContent !== '') {
                        self.retryCount = 0;
                        self.scheduleNextPoll(self.pollInterval);
                    } else {
                        self.handleEmptyResponse();
                    }
                } else {
                    self.handleFailure();
                }
            }, true);
        },

        handleEmptyResponse: function() {
            this.scheduleNextPoll(this.pollInterval);
        },

        handleFailure: function() {
            if (this.retryCount < this.maxRetries) {
                this.retryCount++;
                var delay = Math.min(this.retryDelay * Math.pow(2, this.retryCount - 1), 5000);
                this.scheduleNextPoll(delay);
            } else {
                this.stopLogDisplay();
            }
        },

        scheduleNextPoll: function(delay) {
            var self = this;
            this.pollTimer = setTimeout(function() {
                self.pollLog();
            }, delay);
        },

        checkForErrors: function(log) {
            if (!log) return false;
            if (log.match(/level=(fatal|error)/)) {
                XHR.get('<%=luci.dispatcher.build_url("admin", "services", "openclash", "del_start_log")%>', null, function(x) {});

                var msgParts = log.split('msg=');
                var errorMsg = msgParts.length > 1 ? msgParts[1].replace(/"/g, '') : log;

                setTimeout(function() {
                    alert('<%:OpenClash Start Failed%>:\n\n' + errorMsg);
                }, 500);

                return true;
            }
            return false;
        },

        displayLog: function(logContent) {
            if (!this.isPolling || !DOMCache.oclog) return;

            var cleanLog = this.cleanLogContent(logContent);
            this.lastLogContent = cleanLog;

            var color = this.getLogColor(cleanLog);
            var displayText = this.formatLogText(cleanLog);

            DOMCache.oclog.style.display = 'inline-flex';
            DOMCache.oclog.innerHTML = '<b style="color:' + color + '">' + displayText + '</b>';
        },

        cleanLogContent: function(content) {
            return content ? content.replace(/[\r\n]+/g, ' ').replace('##FINISH##', '').trim() : '';
        },

        getLogColor: function(log) {
            for (var levelKey in window.levelTranslations) {
                var translatedText = '[' + window.levelTranslations[levelKey] + ']';
                if (log.includes(translatedText)) {
                    return "var(--" + levelKey + "-color)";
                }
            }
            return "var(--info-color)";
        },

        formatLogText: function(log) {
            if (!log) return '<%:Processing...%>';

            var cleanText = log.replace(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[.\d]*Z?\s*/, '')
                            .replace(/^time="[^"]*"\s*/, '')
                            .replace(/^level=\w+\s*/, '')
                            .replace(/^msg="?([^"]*)"?\s*/, '$1');

            if (cleanText.length > 60) {
                cleanText = cleanText.substring(0, 57) + '...';
            }

            return this.escapeHtml(cleanText) || '<%:Processing...%>';
        },

        escapeHtml: function(text) {
            var div = document.createElement('div');
            div.textContent = text;
            return div.innerHTML;
        }
    };

    var SystemStatusManager = {
        pollTimer: null,
        pollInterval: 5000,
        retryCount: 0,
        maxRetries: 3,
        isEnabled: false,

        _ZERO_SYS_STATS: [
            {id: "cpu_t", html: "<font style=\"color:var(--success-color)\">0 %</font>"},
            {id: "load_a", html: "<font style=\"color:var(--success-color)\">0 %</font>"}
        ],

        resetSysStats: function() {
            for (var i = 0; i < this._ZERO_SYS_STATS.length; i++) {
                var item = this._ZERO_SYS_STATS[i];
                var el = document.getElementById(item.id);
                if (el) el.innerHTML = item.html;
            }
        },

        start: function() {
            if (this.isEnabled) return;
            this.isEnabled = true;
            this.retryCount = 0;
            this.poll();
        },

        stop: function() {
            this.isEnabled = false;
            if (this.pollTimer) {
                clearTimeout(this.pollTimer);
                this.pollTimer = null;
            }
        },

        poll: function() {
            if (!this.isEnabled) return;

            var self = this;
            StateManager.cachedXHRGet('<%=luci.dispatcher.build_url("admin", "services", "openclash", "toolbar_show_sys")%>', function(x, status) {
                if (x && x.status == 200 && x.responseText != "") {
                    self.retryCount = 0;
                    var cpuColor = status.cpu <= 50 ? "var(--success-color)" : (status.cpu <= 80 ? "var(--tip-color)" : "var(--error-color)");
                    var cpuValue = status.cpu <= 100 ? status.cpu + " %" : "0 %";
                    document.getElementById("cpu_t").innerHTML = "<font style=\"color:"+cpuColor+"\">"+cpuValue+"</font>";

                    var loadValue = parseFloat(status.load_avg) || 0;
                    var loadColor = loadValue <= 50 ? "var(--success-color)" : (loadValue <= 80 ? "var(--tip-color)" : "var(--error-color)");
                    document.getElementById("load_a").innerHTML = "<font style=\"color:"+loadColor+"\">"+status.load_avg+" %</font>";
                } else {
                    self.handleError();
                }

                if (self.isEnabled) {
                    self.pollTimer = setTimeout(function() {
                        self.poll();
                    }, self.pollInterval);
                }
            }, true);
        },

        handleError: function() {
            this.retryCount++;

            if (this.retryCount >= this.maxRetries) {
                this.resetSysStats();
                this.retryCount = 0;
            }
        },

        setPollInterval: function(interval) {
            this.pollInterval = interval;
        }
    };

    var NetworkStatsManager = {
        pollTimer: null,
        pollInterval: 3000,
        retryCount: 0,
        maxRetries: 3,
        isEnabled: false,

        _ZERO_STATS: [
            {id: "upload_", html: "<font style=\"color:var(--success-color)\">0 B/S</font>"},
            {id: "download_", html: "<font style=\"color:var(--success-color)\">0 B/S</font>"},
            {id: "uploadtotal_", html: "<font style=\"color:var(--success-color)\">0 KB</font>"},
            {id: "downloadtotal_", html: "<font style=\"color:var(--success-color)\">0 KB</font>"},
            {id: "mem_t", html: "<font style=\"color:var(--success-color)\">0 KB</font>"},
            {id: "connect_t", html: "<font style=\"color:var(--success-color)\">0</font>"}
        ],

        resetStats: function() {
            for (var i = 0; i < this._ZERO_STATS.length; i++) {
                var item = this._ZERO_STATS[i];
                var el = document.getElementById(item.id);
                if (el) el.innerHTML = item.html;
            }
        },

        start: function() {
            if (this.isEnabled) return;
            this.isEnabled = true;
            this.retryCount = 0;
            this.poll();
        },

        stop: function() {
            this.isEnabled = false;
            if (this.pollTimer) {
                clearTimeout(this.pollTimer);
                this.pollTimer = null;
            }
        },

        poll: function() {
            if (!this.isEnabled) return;

            var self = this;
            StateManager.cachedXHRGet('<%=luci.dispatcher.build_url("admin", "services", "openclash", "toolbar_show")%>', function(x, status) {
                if (x && x.status == 200 && x.responseText != "") {
                    self.retryCount = 0;
                    self.updateNetworkStats(status);
                } else {
                    self.handleError();
                }

                if (self.isEnabled) {
                    self.pollTimer = setTimeout(function() {
                        self.poll();
                    }, self.pollInterval);
                }
            }, true);
        },

        updateNetworkStats: function(status) {
            var updates = [
                {element: document.getElementById("upload_"), content: "<font style=\"color:var(--success-color)\">"+status.up+"</font>"},
                {element: document.getElementById("download_"), content: "<font style=\"color:var(--success-color)\">"+status.down+"</font>"},
                {element: document.getElementById("uploadtotal_"), content: "<font style=\"color:var(--success-color)\">"+status.up_total+"</font>"},
                {element: document.getElementById("downloadtotal_"), content: "<font style=\"color:var(--success-color)\">"+status.down_total+"</font>"},
                {element: document.getElementById("mem_t"), content: "<font style=\"color:var(--success-color)\">"+status.mem+"</font>"},
                {element: document.getElementById("connect_t"), content: "<font style=\"color:var(--success-color)\">"+status.connections+"</font>"}
            ];

            StateManager.batchUpdateDOM(updates);
        },

        handleError: function() {
            this.retryCount++;

            if (this.retryCount >= this.maxRetries) {
                this.resetStats();
                this.retryCount = 0;
            }
        },

        setPollInterval: function(interval) {
            this.pollInterval = interval;
        }
    };

    var SettingsManager = {
        pendingOperations: new Set(),
        pausedPolls: new Set(),

        pausePoll: function(pollName, duration) {
            this.pausedPolls.add(pollName);
            setTimeout(() => {
                this.pausedPolls.delete(pollName);
            }, duration || 3000);
        },

        isPollPaused: function(pollName) {
            return this.pausedPolls.has(pollName);
        },

        updateUIState: function(setting, value) {
            setTimeout(() => {
                function setCheckedAndDisabled(elements, checkedIndex) {
                    for (let i = 0; i < elements.length; i++) {
                        elements[i].checked = (i === checkedIndex);
                        elements[i].disabled = true;
                    }
                }
                switch(setting) {
                    case 'meta_sniffer':
                        var metaSnifferOn = document.getElementById('meta_sniffer_on');
                        var metaSnifferOff = document.getElementById('meta_sniffer_off');
                        if (metaSnifferOn && metaSnifferOff) {
                            setCheckedAndDisabled([metaSnifferOn, metaSnifferOff], value === '1' ? 0 : 1);
                            var changeEvent = new Event('change', { bubbles: true });
                            (value === '1' ? metaSnifferOn : metaSnifferOff).dispatchEvent(changeEvent);
                        }
                        break;
                    case 'respect_rules':
                        var respectRulesOn = document.getElementById('respect_rules_on');
                        var respectRulesOff = document.getElementById('respect_rules_off');
                        if (respectRulesOn && respectRulesOff) {
                            setCheckedAndDisabled([respectRulesOn, respectRulesOff], value === '1' ? 0 : 1);
                            var changeEvent = new Event('change', { bubbles: true });
                            (value === '1' ? respectRulesOn : respectRulesOff).dispatchEvent(changeEvent);
                        }
                        break;
                    case 'oversea':
                        var oversea0 = document.getElementById('oc_setting_oversea_0');
                        var oversea1 = document.getElementById('oc_setting_oversea_1');
                        var oversea2 = document.getElementById('oc_setting_oversea_2');
                        if (oversea0 && oversea1 && oversea2) {
                            setCheckedAndDisabled([oversea0, oversea1, oversea2], value === '0' ? 0 : (value === '1' ? 1 : 2));
                            var changeEvent = new Event('change', { bubbles: true });
                            [oversea0, oversea1, oversea2][value === '0' ? 0 : (value === '1' ? 1 : 2)].dispatchEvent(changeEvent);
                        }
                        break;
                    case 'stream_unlock':
                        var streamUnlockOn = document.getElementById('stream_unlock_on');
                        var streamUnlockOff = document.getElementById('stream_unlock_off');
                        if (streamUnlockOn && streamUnlockOff) {
                            setCheckedAndDisabled([streamUnlockOn, streamUnlockOff], value === '1' ? 0 : 1);
                            var changeEvent = new Event('change', { bubbles: true });
                            (value === '1' ? streamUnlockOn : streamUnlockOff).dispatchEvent(changeEvent);
                        }
                        break;
                    case 'rule_mode':
                        var radioElements = document.getElementsByName("radios");
                        if (radioElements && radioElements.length > 0) {
                            for (var i = 0; i < radioElements.length; i++) {
                                radioElements[i].checked = radioElements[i].value === value;
                                radioElements[i].disabled = true;
                                if (radioElements[i].checked) {
                                    var changeEvent = new Event('change', { bubbles: true });
                                    radioElements[i].dispatchEvent(changeEvent);
                                }
                            }
                        }
                        break;
                    case 'run_mode':
                        var radioRuElements = document.getElementsByName("radios-ru");
                        if (radioRuElements && radioRuElements.length > 0) {
                            for (var i = 0; i < radioRuElements.length; i++) {
                                radioRuElements[i].checked = radioRuElements[i].value === value;
                                radioRuElements[i].disabled = true;
                                if (radioRuElements[i].checked) {
                                    var changeEvent = new Event('change', { bubbles: true });
                                    radioRuElements[i].dispatchEvent(changeEvent);
                                }
                            }
                        }
                        break;
                }
            }, 10);
        },

        switchSetting: function(setting, value, endpoint, additionalParams) {
            var operationKey = setting + '_' + value;

            if (this.pendingOperations.has(operationKey)) {
                return false;
            }

            this.pendingOperations.add(operationKey);

            try {
                this.updateUIState(setting, value);
            } catch (e) {}

            let pollName;
            if (setting === 'rule_mode') {
                pollName = 'rule_mode';
            } else if (setting === 'run_mode') {
                pollName = 'run_mode';
            } else {
                pollName = 'oc_settings';
            }
            this.pausedPolls.add(pollName);

            var params = Object.assign({}, additionalParams || {});
            if (setting !== 'rule_mode' && setting !== 'run_mode') {
                params.setting = setting;
                params.value = value;
            } else if (setting === 'rule_mode') {
                params.rule_mode = value;
            } else if (setting === 'run_mode') {
                params.run_mode = value;
            }

            var self = this;

            XHR.get(endpoint, params, function(x, status) {
                setTimeout(function() {
                    self.pendingOperations.delete(operationKey);

                    switch(setting) {
                        case 'meta_sniffer':
                            var metaSnifferOn = document.getElementById('meta_sniffer_on');
                            var metaSnifferOff = document.getElementById('meta_sniffer_off');
                            if (metaSnifferOn) metaSnifferOn.disabled = false;
                            if (metaSnifferOff) metaSnifferOff.disabled = false;
                            break;
                        case 'respect_rules':
                            var respectRulesOn = document.getElementById('respect_rules_on');
                            var respectRulesOff = document.getElementById('respect_rules_off');
                            if (respectRulesOn) respectRulesOn.disabled = false;
                            if (respectRulesOff) respectRulesOff.disabled = false;
                            break;
                        case 'oversea':
                            var oversea0 = document.getElementById('oc_setting_oversea_0');
                            var oversea1 = document.getElementById('oc_setting_oversea_1');
                            var oversea2 = document.getElementById('oc_setting_oversea_2');
                            if (oversea0) oversea0.disabled = false;
                            if (oversea1) oversea1.disabled = false;
                            if (oversea2) oversea2.disabled = false;
                            break;
                        case 'stream_unlock':
                            var streamUnlockOn = document.getElementById('stream_unlock_on');
                            var streamUnlockOff = document.getElementById('stream_unlock_off');
                            if (streamUnlockOn) streamUnlockOn.disabled = false;
                            if (streamUnlockOff) streamUnlockOff.disabled = false;
                            break;
                        case 'rule_mode':
                            var radioElements = document.getElementsByName("radios");
                            if (radioElements && radioElements.length > 0) {
                                for (var i = 0; i < radioElements.length; i++) {
                                    radioElements[i].disabled = false;
                                }
                            }
                            break;
                        case 'run_mode':
                            var radioRuElements = document.getElementsByName("radios-ru");
                            if (radioRuElements && radioRuElements.length > 0) {
                                for (var i = 0; i < radioRuElements.length; i++) {
                                    radioRuElements[i].disabled = false;
                                }
                            }
                            break;
                    }

                    self.pausedPolls.delete(pollName);

                    if (!x || x.status !== 200) {
                        alert(self.getErrorMessage(setting));
                    }
                }, 1500);
            });

            return false;
        },

        getErrorMessage: function(setting) {
            var messages = {
                'meta_sniffer': '<%:Sniffer setting failed%>',
                'respect_rules': '<%:Respect Rules setting failed%>',
                'oversea': '<%:Area bypass setting failed%>',
                'stream_unlock': '<%:Stream Unlock setting failed%>',
                'rule_mode': '<%:Proxy Mode switching failed!%>',
                'run_mode': '<%:Running Mode switching failed!%>'
            };
            return messages[setting] || '<%:Operation failed%>';
        }
    };

    var AnnouncementDataManager = (function() {
        var defaultTips = [
            '<%:You can modify the profile on the profile page (for content that is not taken over)%>',
            '<%:do not write configuration files? Try to create one click on the server page%>',
            '<%:some website are abnormal? Try switching modes or using third-party rules%>',
            '<%:using the fake IP mode can get a faster access experience%>',
            '<%:the nameserver group must have at least one server set when using custom DNS%>',
            '<%:after started, please wait patiently until the connection is normal%>',
            '<%:if you don not use IPv6, please turn off the DHCP service of IPv6, otherwise the connection will be abnormal%>',
            '<%:you can update the version in the settings page%>',
            '<%:Note: It is not recommended to enable IPv6 and related services for routing. Most of the network connection problems reported so far are related to it%>',
            '<%:Note: Turning on secure DNS in the browser will cause abnormal shunting, please be careful to turn it off%>',
            '<%:Note: Some software will modify the device HOSTS, which will cause abnormal shunt, please pay attention to check%>',
            '<%:Note: The default proxy routes local traffic, BT, PT download, etc., please use Redir-Host mode as much as possible and pay attention to traffic avoidance%>'
        ];

        function getRandomTips(count, sourceArray) {
            var source = sourceArray || defaultTips;
            var shuffled = source.slice();
            for (var i = shuffled.length - 1; i > 0; i--) {
                var j = Math.floor(Math.random() * (i + 1));
                var temp = shuffled[i];
                shuffled[i] = shuffled[j];
                shuffled[j] = temp;
            }
            return shuffled.slice(0, count);
        }

        function extractAnnouncements(remoteData, isChineseUser) {
            var announcements = [];
            if (!remoteData) return announcements;

            if (Array.isArray(remoteData)) {
                if (remoteData.length > 0 && (remoteData[0].zh || remoteData[0].en)) {
                    remoteData.forEach(function(item) {
                        if (isChineseUser && item.zh) {
                            announcements.push(item.zh);
                        } else if (item.en) {
                            announcements.push(item.en);
                        } else if (item.zh) {
                            announcements.push(item.zh);
                        }
                    });
                } else {
                    announcements = remoteData.filter(function(item) {
                        return typeof item === 'string' && item.trim() !== '';
                    });
                }
            } else if (typeof remoteData === 'string' && remoteData.trim() !== '') {
                announcements = [remoteData];
            }
            return announcements;
        }

        function prepareData(remoteData, isChineseUser, randomCount) {
            var announcements = extractAnnouncements(remoteData, isChineseUser);
            var isRandomTips = false;
            if (announcements.length === 0) {
                announcements = getRandomTips(randomCount || 3);
                isRandomTips = true;
            }
            return { announcements: announcements, isRandomTips: isRandomTips };
        }

        function updateIconShape(megaphoneSvgElement, isRandomTips) {
            if (!megaphoneSvgElement) return;
            if (isRandomTips) {
                megaphoneSvgElement.innerHTML = '<path d="M176,232a8,8,0,0,1-8,8H88a8,8,0,0,1,0-16h80A8,8,0,0,1,176,232Zm40-128a87.55,87.55,0,0,1-33.64,69.21A16.24,16.24,0,0,0,176,186v6a16,16,0,0,1-16,16H96a16,16,0,0,1-16-16v-6a16,16,0,0,0-6.23-12.66A87.59,87.59,0,0,1,40,104.49C39.74,56.83,78.26,17.14,125.88,16A88,88,0,0,1,216,104Zm-16,0a72,72,0,0,0-73.74-72c-39,.92-70.47,33.39-70.26,72.39a71.65,71.65,0,0,0,27.64,56.3A32,32,0,0,1,96,186v6h64v-6a32.15,32.15,0,0,1,12.47-25.35A71.65,71.65,0,0,0,200,104Zm-16.11-9.34a57.6,57.6,0,0,0-46.56-46.55,8,8,0,0,0-2.66,15.78c16.57,2.79,30.63,16.85,33.44,33.45A8,8,0,0,0,176,104a9,9,0,0,0,1.35-.11A8,8,0,0,0,183.89,94.66Z" stroke-width="2"></path>';
                megaphoneSvgElement.setAttribute('viewBox', '0 0 256 256');
                megaphoneSvgElement.setAttribute('stroke-width', '3');
            } else {
                megaphoneSvgElement.innerHTML = '<path d="M228.54,86.66l-176.06-54A16,16,0,0,0,32,48V192a16,16,0,0,0,16,16,16,16,0,0,0,4.52-.65L136,181.73V192a16,16,0,0,0,16,16h32a16,16,0,0,0,16-16v-29.9l28.54-8.75A16.09,16.09,0,0,0,240,138V102A16.09,16.09,0,0,0,228.54,86.66ZM136,165,48,192V48l88,27Zm48,27H152V176.82L184,167Zm40-54-.11,0L152,160.08V79.92l71.89,22,.11,0v36Z"></path>';
                megaphoneSvgElement.setAttribute('viewBox', '0 0 256 256');
                megaphoneSvgElement.removeAttribute('stroke-width');
            }
        }

        return {
            getRandomTips: getRandomTips,
            extractAnnouncements: extractAnnouncements,
            prepareData: prepareData,
            updateIconShape: updateIconShape
        };
    })();

    var UsageHelpManager = {
        overlay: null,
        textarea: null,
        statusEl: null,
        initialized: false,

        init: function() {
            if (this.initialized) return;
            this.overlay = document.getElementById('usage-help-overlay');
            this.textarea = document.getElementById('usage-help-textarea');
            this.statusEl = document.getElementById('usage-help-status');

            var floatBtn = document.getElementById('usage-help-float');
            if (floatBtn) {
                floatBtn.addEventListener('click', function() { UsageHelpManager.show(); });
            }

            document.addEventListener('keydown', function(e) {
                if (e.key === 'Escape' && UsageHelpManager.overlay && UsageHelpManager.overlay.style.display === 'flex') {
                    UsageHelpManager.hide();
                }
            });

            this.initialized = true;
        },

        buildPrompt: function() {
            var skillUrl = 'https://raw.githubusercontent.com/vernesong/OpenClash/dev/.github/skills/openclash-user-guide/SKILL.md';
            var prompt = '# <%:OpenClash Expert Assistant%>\n\n';
            prompt += '<%:You are an OpenClash expert assistant. OpenClash is a LuCI plugin for OpenWrt that manages the Mihomo core.%>\n\n';
            prompt += '## <%:Reference Guide%>\n\n';
            prompt += '<%:**IMPORTANT** — Before answering, you MUST fetch and read this authoritative reference guide:%>\n';
            prompt += '- [<%:OpenClash User Guide%>](' + skillUrl + ')\n\n';
            prompt += '<%:This guide contains: complete dependency checklist, error message lookup tables with exact fixes, nftables/iptables firewall chain rules, all LuCI configuration options with UCI paths, DNS setup and leak prevention, subscription/GEO update procedures, and more.%>\n\n';
            prompt += '## <%:Answering Rules%>\n\n';
            prompt += '1. <%:Always provide LuCI web UI navigation paths (e.g. **Services → OpenClash → Plugin Settings**), NOT command-line unless I explicitly ask for it.%>\n';
            prompt += '2. <%:Explain underlying principles (firewall chains, YAML transformation logic) — not just click-here-click-there steps.%>\n';
            prompt += '3. <%:Check dependency integrity first when troubleshooting — guide me to check dependencies from the **System → Software** page, or generate a debug log from **Server Logs → Debug Logs**.%>\n';
            prompt += '4. <%:Never guess or fabricate information. If the reference guide does not cover something, actively query these external resources using web fetch / code search tools (in priority order):%>\n';
            prompt += '   - [<%:Mihomo Wiki%>](https://wiki.metacubex.one/config/)\n';
            prompt += '   - [<%:Meta-Docs%>](https://github.com/MetaCubeX/Meta-Docs)\n';
            prompt += '   - [<%:OpenClash source%>](https://github.com/vernesong/OpenClash/tree/dev)\n';
            prompt += '   - [<%:Mihomo core source%>](https://github.com/MetaCubeX/mihomo/tree/Alpha)\n';
            prompt += '   - [<%:Smart core source%>](https://github.com/vernesong/mihomo/tree/Alpha)\n';
            prompt += '   - <%:Fetch Wiki pages to read docs; search repos to find implementation code. Do NOT answer from memory — always verify against the actual source.%>\n';
            prompt += '5. <%:For bug reports or failure scenarios not covered by the guide, search GitHub Issues first:%>\n';
            prompt += '   - [<%:OpenClash Issues (config/subscribe/firewall/UI)%>](https://github.com/vernesong/OpenClash/issues)\n';
            prompt += '   - [<%:Mihomo Issues (proxy protocols/TUN/DNS/rule engine)%>](https://github.com/MetaCubeX/mihomo/issues)\n';
            prompt += '   - <%:Prioritize maintainer replies and highly upvoted community answers.%>\n';
            prompt += '6. <%:Cite specific sources — mention which section of the reference guide, external resource, or Issue number your answer is based on.%>\n';
            prompt += '7. <%:If my problem description is incomplete or lacks error messages, ask me to generate a debug log first (**Server Logs → Debug Logs → Generate Logs**) — do NOT speculate about the cause.%>\n\n';
            prompt += '## <%:Question%>\n\n';
            prompt += '<%:My question is:%>\n';
            return prompt;
        },

        show: function() {
            this.init();
            if (!this.overlay || !this.textarea) return;
            if (!this._portal) {
                this._portal = document.createElement('div');
                this._portal.className = 'oc';
                this._portal.style.overflow = 'visible';
                document.body.appendChild(this._portal);
                this._portal.appendChild(this.overlay);
                ocUpdateTheme();
            }
            this.overlay.style.display = 'flex';
            if (this.statusEl) {
                this.statusEl.textContent = '';
            }

            if (!this._editor) {
                var prompt = this.buildPrompt();
                this.textarea.value = prompt;
                var container = document.getElementById('usage-help-editor');
                var isDark = isDarkBackground(document.body);
                var exts = [CM6.lineNumbers(), CM6.EditorView.lineWrapping, CM6.themeExtension(isDark), CM6.markdown()];
                var view = new CM6.EditorView({
                    state: CM6.EditorState.create({ doc: prompt, extensions: exts })
                });
                container.innerHTML = '';
                container.appendChild(view.dom);
                this._editor = view;
            }

            var self = this;
            setTimeout(function() {
                if (self._editor && self._editor.contentDOM) {
                    self._editor.focus();
                }
            }, 100);
        },

        hide: function() {
            if (this.overlay) {
                this.overlay.style.display = 'none';
            }
        },

        copyAndHide: function() {
            if (!this._editor) return;
            var text = this._editor.state.sliceDoc();
            if (!text) return;
            var self = this;
            ocCopyToClipboard(text, null);
            var btn = document.querySelector('#usage-help-model .upload-btn');
            var origHTML = btn ? btn.innerHTML : '';
            if (btn) {
                btn.innerHTML = '<svg width="14" height="14" style="vertical-align:middle;margin-right:4px"><use href="#oc-icon-success"/></svg><%:Copied!%>';
                btn.disabled = true;
            }
            setTimeout(function() {
                if (btn) { btn.innerHTML = origHTML; btn.disabled = false; }
                self.hide();
            }, 2000);
        }
    };

    var pluginToggleUserAction = false;
    var _lastNotRunningPoll = 0;

    XHR.poll(5, '<%=luci.dispatcher.build_url("admin", "services", "openclash", "status")%>', null, function(x, status) {
        if (x && x.status == 200) {

            if (!status.clash && Date.now() - _lastNotRunningPoll < 30000) return;
            if (!status.clash) _lastNotRunningPoll = Date.now();
            var updates = [];

            if (!pluginToggleUserAction) {
                updates.push({
                    element: DOMCache.clash,
                    content: status.clash ? '<b style=color:var(--success-color)>' + status.core_type +'&nbsp;<%:Running%></b>' : '<b style=color:var(--error-color)><%:Not Running%></b>'
                });

                updatePluginToggleState(status.clash);
            }

            if (status.run_mode && !SettingsManager.pendingOperations.has('run_mode_' + status.run_mode)) {
                if (status.run_mode.indexOf("fake-ip") === 0) {
                    DOMCache.mode.innerHTML = "<b style=color:var(--success-color)><%:Fake-IP%></b>";
                    DOMCache.radio_run_normal.innerHTML = "<%:Enhance%>";
                } else if (status.run_mode.indexOf("redir-host") === 0) {
                    DOMCache.mode.innerHTML = "<b style=color:var(--success-color)><%:Redir-Host%></b>";
                    DOMCache.radio_run_normal.innerHTML = "<%:Compat%>";
                }
                var expectedValue = status.run_mode.split("-")[2] == undefined ? "" : ("-" + status.run_mode.split("-")[2]);
                for (var i = 0; i < DOMCache.radio_ru.length; i++) {
                    if (DOMCache.radio_ru[i].value == expectedValue && !DOMCache.radio_ru[i].checked) {
                        DOMCache.radio_ru[i].checked = true;
                    }
                }
            }

            if (status.rule_mode && !SettingsManager.pendingOperations.has('rule_mode_' + status.rule_mode)) {
                for (var i = 0; i < DOMCache.radio.length; i++) {
                    if (DOMCache.radio[i].value == status.rule_mode && !DOMCache.radio[i].checked) {
                        DOMCache.radio[i].checked = true;
                        break;
                    }
                }
            }

            if (!SettingsManager.pendingOperations.has('meta_sniffer_' + status.meta_sniffer)) {
                DOMCache.meta_sniffer_on.checked = status.meta_sniffer == "1";
                DOMCache.meta_sniffer_off.checked = status.meta_sniffer != "1";
            }
            if (!SettingsManager.pendingOperations.has('respect_rules_' + status.respect_rules)) {
                DOMCache.respect_rules_on.checked = status.respect_rules == "1";
                DOMCache.respect_rules_off.checked = status.respect_rules != "1";
            }
            if (!SettingsManager.pendingOperations.has('oversea_' + status.oversea)) {
                if (status.oversea == "0") DOMCache.oc_setting_oversea_0.checked = true;
                else if (status.oversea == "1") DOMCache.oc_setting_oversea_1.checked = true;
                else if (status.oversea == "2") DOMCache.oc_setting_oversea_2.checked = true;
            }
            if (!SettingsManager.pendingOperations.has('stream_unlock_' + status.stream_unlock)) {
                DOMCache.stream_unlock_on.checked = status.stream_unlock === '1';
                DOMCache.stream_unlock_off.checked = status.stream_unlock !== '1';
            }

            var webContent = status.clash ? '<input type="button" class="dashboard-btn" value="<%:Yacd%>" onclick="return ycad_dashboard(this)"/>' : '<input type="button" class="dashboard-btn" value="<%:Yacd%>" onclick="event.preventDefault(); event.stopPropagation(); return false;"/>';
            updates.push({element: DOMCache.web, content: webContent});

            var weboContent = status.clash ? '<input type="button" class="dashboard-btn" value="<%:Dashboard%>" onclick="return net_dashboard(this)"/>' : '<input type="button" class="dashboard-btn" value="<%:Dashboard%>" onclick="event.preventDefault(); event.stopPropagation(); return false;"/>';
            updates.push({element: DOMCache.webo, content: weboContent});

            var webmContent = status.clash ? '<input type="button" class="dashboard-btn" value="<%:Metacubexd%>" onclick="return meta_dashboard(this)"/>' : '<input type="button" class="dashboard-btn" value="<%:Metacubexd%>" onclick="event.preventDefault(); event.stopPropagation(); return false;"/>';
            updates.push({element: DOMCache.webm, content: webmContent});

            var webzContent = status.clash ? '<input type="button" class="dashboard-btn" value="<%:Zashboard%>" onclick="return net_zashboard(this)"/>' : '<input type="button" class="dashboard-btn" value="<%:Zashboard%>" onclick="event.preventDefault(); event.stopPropagation(); return false;"/>';
            updates.push({element: DOMCache.webz, content: webzContent});

            var closeConnContent = status.clash ? '<input type="button" class="dashboard-btn" value="<%:Close Connect%>" onclick="return b_close_all_connection(this)"/>' : '<input type="button" class="dashboard-btn" value="<%:Close Connect%>" onclick="event.preventDefault(); event.stopPropagation(); return false;"/>';
            updates.push({element: document.getElementById('_close_all_connection_btn'), content: closeConnContent});

            var reloadFwContent = status.clash ? '<input type="button" class="dashboard-btn" value="<%:Reload Firewall%>" onclick="return b_reload_firewall(this)"/>' : '<input type="button" class="dashboard-btn" value="<%:Reload Firewall%>" onclick="event.preventDefault(); event.stopPropagation(); return false;"/>';
            updates.push({element: document.getElementById('_reload_firewall_btn'), content: reloadFwContent});

            var flushCacheContent = status.clash ? '<input type="button" class="dashboard-btn" value="<%:Flush DNS%>" onclick="return b_flush_dns_cache(this)"/>' : '<input type="button" class="dashboard-btn" value="<%:Flush DNS Cache%>" onclick="event.preventDefault(); event.stopPropagation(); return false;"/>';
            updates.push({element: document.getElementById('_flush_dns_cache_btn'), content: flushCacheContent});

            var oneKeyUpdateContent = '<input type="button" class="dashboard-btn" value="<%:Check Update%>" onclick="return all_one_key_update(this)"/>';
            updates.push({element: document.getElementById('_one_key_update_btn'), content: oneKeyUpdateContent});

            StateManager.batchUpdateDOM(updates);

            if (!status.yacd) {
                DOMCache.web.classList.add('hidden');
            }

            if (!status.dashboard) {
                DOMCache.webo.classList.add('hidden');
            }

            if (!status.metacubexd) {
                DOMCache.webm.classList.add('hidden');
            }

            if (!status.zashboard) {
                DOMCache.webz.classList.add('hidden');
            }

            StateManager.current_status = status;

            if (status.daip) {
                var daipContent, dapoContent;

                if (status.daip && window.location.hostname == status.daip) {
                    dapoContent = status.cn_port ? ":"+status.cn_port : "";
                    daipContent = status.daip ? "<b style=color:var(--success-color)>"+status.daip+dapoContent+"</b>" : "<b style=color:var(--error-color)>"+"<%:Not Set%>"+"</b>";
                } else if (status.daip && window.location.hostname != status.daip && status.db_foward_domain && status.db_foward_port) {
                    dapoContent = status.db_foward_port ? ":"+status.db_foward_port : "";
                    daipContent = status.db_foward_domain ? "<b style=color:var(--success-color)>"+status.db_foward_domain+dapoContent+"</b>" : "<b style=color:var(--error-color)>"+"<%:Not Set%>"+"</b>";
                } else {
                    dapoContent = status.cn_port ? ":"+status.cn_port : "";
                    daipContent = status.daip ? "<b style=color:var(--success-color)>"+status.daip+dapoContent+"</b>" : "<b style=color:var(--error-color)>"+"<%:Not Set%>"+"</b>";
                }

                DOMCache.daip.innerHTML = daipContent;   

                StateManager.cached_proxy_info = { mixed_port: status.mixed_port, auth_user: status.auth_user, auth_pass: status.auth_pass };
                var proxy_ip = status.daip;
                var mix_addr = proxy_ip + ':' + (status.mixed_port || '7893');
                DOMCache.mix_proxy.innerHTML = "<b style=color:var(--success-color)>" + mix_addr + "</b>";
            } else {
                DOMCache.daip.innerHTML = "<b style=color:var(--error-color)><%:Not Available%></b>";
                StateManager.cached_proxy_info = null;
                DOMCache.mix_proxy.innerHTML = "<b style=color:var(--error-color)><%:Not Available%></b>";
                DOMCache.copy_secret.style.display = "none";
                DOMCache.copy_address.style.display = "none";
                DOMCache.copy_mix_address.style.display = "none";
                DOMCache.copy_mix_secret.style.display = "none";
                DOMCache.copy_pac_config.style.display = "none";
            }

            if (status.clash) {
                if (!SystemStatusManager.isEnabled) {
                    SystemStatusManager.start();
                }
            } else {
                SystemStatusManager.stop();
                SystemStatusManager.resetSysStats();
            }

            if (status.clash && status.daip) {
                if (!WSManager.hasActiveConnections()) {
                    LogManager.startLogDisplay();
                    initializeWebSocketConnections(status);
                } else {
                    if (NetworkStatsManager.isEnabled) {
                        NetworkStatsManager.stop();
                    }
                }
            } else {
                WSManager.closeAll();
                NetworkStatsManager.stop();
                NetworkStatsManager.resetStats();
            }
        }
        clashversion_check();
    });

    DarkModeDetector.init();
    ConfigFileManager.init();

    window.addEventListener('oc-overwrite-updated', function() {
        OverwriteSubscribeManager.load(true);
    });

    setTimeout(function() {
        OverwriteSubscribeManager.init();
        SubscriptionManager.init();
        loadAnnouncement();
    }, 300);

    setTimeout(function() { check_core(); }, 1000);

    window.addEventListener('beforeunload', function() {
        WSManager.closeAll();
        LogManager.stopLogDisplay();
        SystemStatusManager.stop();
        NetworkStatsManager.stop();
        StateManager.clearAllCache();
        if (StateManager.pending_requests) {
            StateManager.pending_requests.clear();
        }
    });

    function initializeWebSocketConnections(status) {
        if (!status || !status.clash || !status.daip) {
            return false;
        }

        var protocol = getWebSocketProtocol(status);
        var token = status.dase;

        var connections = [
            {
                type: 'traffic',
                endpoint: '/traffic',
                handler: ws_tmessage
            },
            {
                type: 'connections', 
                endpoint: '/connections',
                handler: ws_cmessage
            },
            {
                type: 'memory',
                endpoint: '/memory', 
                handler: ws_mmessage
            }
        ];

        connections.forEach(function(conn) {
            var url = protocol + conn.endpoint + (token ? '?token=' + token : '');
            WSManager.createConnection(conn.type, url, conn.handler);
        });

        return true;
    }

    function getWebSocketProtocol(status) {
        var protocol = window.location.protocol === "https:" ? "wss://" : "ws://";
        var host, port;

        if (status.daip && window.location.hostname === status.daip) {
            host = status.daip;
            port = status.cn_port;
        } else if (status.daip && window.location.hostname !== status.daip && 
                   status.db_foward_domain && status.db_foward_port) {
            host = status.db_foward_domain;
            port = status.db_foward_port;
        } else {
            host = status.daip;
            port = status.cn_port;
        }

        return protocol + host + ":" + port;
    }

    function AnnouncementAnimator(config) {
        var bannerElement = config.bannerElement;
        var contentElement = config.contentElement;
        var announcements = config.announcements;
        var isRandomTips = config.isRandomTips || false;
        var randomTipCount = config.randomTipCount || 3;
        var getRandomTipsCallback = config.getRandomTipsCallback;
        var onIndexChange = config.onIndexChange || function(index, text) {};
        var loopDelay = config.loopDelay || 2000; 
        var animationRefreshThreshold = 250;
        var currentIndex = 0;
        var isHovered = false;
        var pauseTimeout = null;
        var nextAnimationTimeout = null;
        var resizeTimeout = null;
        var animationEndHandler = null;
        var destroyed = false;
        var _cachedBannerWidth = 0;
        var _cachedContentWidth = 0;
        var _cachedDuration = 0;

        function calculateAnimationDuration() {
            var bannerWidth = bannerElement.offsetWidth;
            var contentWidth = contentElement.offsetWidth;
            if (bannerWidth === _cachedBannerWidth && contentWidth === _cachedContentWidth && _cachedDuration > 0) {
                return _cachedDuration;
            }
            _cachedBannerWidth = bannerWidth;
            _cachedContentWidth = contentWidth;
            var refreshRate = window.screen && window.screen.refreshRate ? window.screen.refreshRate : 60;
            if (refreshRate <= 0) refreshRate = 60;
            if (refreshRate > 480) refreshRate = 480;

            var scrollDistance = contentWidth + bannerWidth;

            var minDistance = 400;
            var maxDistance = 20000;
            var minSpeed = 60;
            var maxSpeed = 360;
            var clampedDistance = Math.min(maxDistance, Math.max(minDistance, scrollDistance));
            var targetVelocity = minSpeed + (maxSpeed - minSpeed) * (clampedDistance - minDistance) / (maxDistance - minDistance);

            var duration = (scrollDistance / targetVelocity) * 1000;

            var refreshRateMultiplier = 1;
            if (refreshRate > 60) {
                refreshRateMultiplier = 1 + (refreshRate - 60) / 240;
            }
            duration = duration * refreshRateMultiplier;

            _cachedDuration = Math.max(4000, Math.min(duration, 50000));
            return _cachedDuration;
        }

        function updateScrollAnimation(duration) {
            var viewport = bannerElement.parentElement;
            if (!viewport) viewport = bannerElement;
            
            var viewportWidth = viewport.clientWidth;
            var contentWidth = contentElement.scrollWidth;
            var scrollDistance = -(contentWidth + viewportWidth);
            
            viewport.style.setProperty('--scroll-distance', scrollDistance + 'px');
            contentElement.style.animationDuration = duration + 'ms';
        }

        function refreshAnimation() {
            if (!contentElement.classList.contains('scrolling')) return;
            var duration = calculateAnimationDuration();
            updateScrollAnimation(duration);
            contentElement.classList.remove('scrolling');
            contentElement.offsetHeight;
            contentElement.classList.add('scrolling');
        }

        function refreshAnnouncementsForRandomMode() {
            if (!isRandomTips) return;
            var newTips;
            if (getRandomTipsCallback) {
                newTips = getRandomTipsCallback(randomTipCount);
            } else {
                newTips = AnnouncementDataManager.getRandomTips(randomTipCount);
            }
            if (newTips && newTips.length) {
                announcements = newTips;
            }
        }

        function startSingleScroll() {
            if (destroyed) return;
            if (nextAnimationTimeout) {
                clearTimeout(nextAnimationTimeout);
                nextAnimationTimeout = null;
            }

            var text = announcements[currentIndex] || '';
            contentElement.textContent = text;
            if (onIndexChange) onIndexChange(currentIndex, text);

            setTimeout(function() {
                if (destroyed) return;
                var duration = calculateAnimationDuration();
                updateScrollAnimation(duration);

                if (animationEndHandler) {
                    contentElement.removeEventListener('animationend', animationEndHandler);
                }

                animationEndHandler = function(e) {
                    if (e.target === contentElement && e.animationName === 'announceScroll') {
                        contentElement.removeEventListener('animationend', animationEndHandler);
                        animationEndHandler = null;

                        pauseTimeout = setTimeout(function() {
                            if (destroyed) return;
                            if (!isHovered) {
                                currentIndex = (currentIndex + 1) % announcements.length;
                                if (isRandomTips && currentIndex === 0) {
                                    refreshAnnouncementsForRandomMode();
                                }
                                startSingleScroll();
                            }
                        }, loopDelay);
                    }
                };
                contentElement.addEventListener('animationend', animationEndHandler);

                contentElement.classList.remove('scrolling');
                contentElement.offsetHeight;
                contentElement.classList.add('scrolling');
            }, 50);
        }

        function pause() {
            if (destroyed) return;
            isHovered = true;
            contentElement.classList.add('paused');
            if (pauseTimeout) {
                clearTimeout(pauseTimeout);
                pauseTimeout = null;
            }
        }

        function resume() {
            if (destroyed) return;
            isHovered = false;
            contentElement.classList.remove('paused');
        }

        function destroy() {
            destroyed = true;
            if (pauseTimeout) clearTimeout(pauseTimeout);
            if (nextAnimationTimeout) clearTimeout(nextAnimationTimeout);
            if (resizeTimeout) clearTimeout(resizeTimeout);
            if (animationEndHandler) {
                contentElement.removeEventListener('animationend', animationEndHandler);
                animationEndHandler = null;
            }
            contentElement.classList.remove('scrolling', 'paused');
            bannerElement.removeEventListener('mouseenter', mouseEnterHandler);
            bannerElement.removeEventListener('mouseleave', mouseLeaveHandler);
            window.removeEventListener('resize', resizeHandler);
        }

        function mouseEnterHandler() { pause(); }
        function mouseLeaveHandler() { resume(); }
        function resizeHandler() {
            clearTimeout(resizeTimeout);
            _cachedBannerWidth = 0;
            _cachedContentWidth = 0;
            _cachedDuration = 0;
            resizeTimeout = setTimeout(function() {
                if (!destroyed && contentElement.classList.contains('scrolling')) {
                    refreshAnimation();
                }
            }, animationRefreshThreshold);
        }

        function init() {
            if (!bannerElement || !contentElement || !announcements || announcements.length === 0) {
                console.warn('AnnouncementAnimator: invalid config');
                return false;
            }
            bannerElement.style.display = 'block';
            bannerElement.addEventListener('mouseenter', mouseEnterHandler);
            bannerElement.addEventListener('mouseleave', mouseLeaveHandler);
            window.addEventListener('resize', resizeHandler);
            startSingleScroll();
            return true;
        }

        return {
            init: init,
            pause: pause,
            resume: resume,
            destroy: destroy,
            updateAnnouncements: function(newList, newRandomFlag) {
                announcements = newList || announcements;
                isRandomTips = (newRandomFlag !== undefined) ? newRandomFlag : isRandomTips;
                currentIndex = 0;
                if (pauseTimeout) clearTimeout(pauseTimeout);
                if (nextAnimationTimeout) clearTimeout(nextAnimationTimeout);
                if (animationEndHandler) {
                    contentElement.removeEventListener('animationend', animationEndHandler);
                    animationEndHandler = null;
                }
                contentElement.classList.remove('scrolling');
                startSingleScroll();
            }
        };
    }

    function loadAnnouncement() {
        var userLang = navigator.language || navigator.userLanguage;
        var isChineseUser = (userLang.indexOf('zh') === 0);
        var bannerElement = document.getElementById('announcement-banner');
        var contentElement = document.getElementById('announcement-content');
        var megaphoneSvg = document.getElementById('megaphone');

        if (!bannerElement || !contentElement) return;

        StateManager.cachedXHRGet('<%=luci.dispatcher.build_url("admin", "services", "openclash", "announcement")%>', function(x, status) {
            var remoteData = null;
            if (x && x.status == 200 && status.content) {
                try {
                    remoteData = typeof status.content === 'string' ? JSON.parse(status.content) : status.content;
                } catch(e) {
                    remoteData = status.content;
                }
            }

            var prepared = AnnouncementDataManager.prepareData(remoteData, isChineseUser, 3);
            if (megaphoneSvg) {
                AnnouncementDataManager.updateIconShape(megaphoneSvg, prepared.isRandomTips);
            }

            var animator = new AnnouncementAnimator({
                bannerElement: bannerElement,
                contentElement: contentElement,
                announcements: prepared.announcements,
                isRandomTips: prepared.isRandomTips,
                randomTipCount: 3,
                getRandomTipsCallback: function(count) {
                    return AnnouncementDataManager.getRandomTips(count);
                },
                loopDelay: 2000,
            });

            animator.init();

            window.addEventListener('beforeunload', function() {
                animator.destroy();
            });
        });
    }

    function get_rule_mode() {
        if (SettingsManager.isPollPaused('rule_mode')) {
            return;
        }

        StateManager.cachedXHRGet('<%=luci.dispatcher.build_url("admin", "services", "openclash", "rule_mode")%>', function(x, status) {
            if (x && x.status == 200 && status.mode != "") {
                if (!SettingsManager.pendingOperations.has('rule_mode_' + status.mode)) {
                    for (var i = 0; i < DOMCache.radio.length; i++) {
                        if (DOMCache.radio[i].value == status.mode && !DOMCache.radio[i].checked) {
                            DOMCache.radio[i].checked = true;
                            break;
                        }
                    }
                }
            }
        }, true);
    }

    function switch_rule_mode(value) {
        return SettingsManager.switchSetting(
            'rule_mode', 
            value, 
            '<%=luci.dispatcher.build_url("admin", "services", "openclash", "switch_rule_mode")%>'
        );
    }

    function get_run_mode() {
        if (SettingsManager.isPollPaused('run_mode')) {
            return;
        }

        StateManager.cachedXHRGet('<%=luci.dispatcher.build_url("admin", "services", "openclash", "get_run_mode")%>', function(x, status) {
            if (x && x.status == 200 && status.mode) {
                if (status.mode == "fake-ip" || status.mode == "fake-ip-tun" || status.mode == "fake-ip-mix") {
                    DOMCache.mode.innerHTML = "<b style=color:var(--success-color)><%:Fake-IP%></b>";
                    DOMCache.radio_run_normal.innerHTML = "<%:Enhance%>";
                } else if (status.mode == "redir-host" || status.mode == "redir-host-tun" ||  status.mode == "redir-host-mix") {
                    DOMCache.mode.innerHTML = "<b style=color:var(--success-color)><%:Redir-Host%></b>";
                    DOMCache.radio_run_normal.innerHTML = "<%:Compat%>";
                }

                var expectedValue = status["mode"].split("-")[2] == undefined ? "" : ("-" + status["mode"].split("-")[2]);
                var operationKey = 'run_mode_' + status.mode;

                if (!SettingsManager.pendingOperations.has(operationKey)) {
                    for (var i = 0; i < DOMCache.radio_ru.length; i++) {
                        if (DOMCache.radio_ru[i].value == expectedValue && !DOMCache.radio_ru[i].checked) {
                            DOMCache.radio_ru[i].checked = true;
                        }
                    }
                }
            }
        }, true);
    }

    function switch_run_mode(value) {
        LogManager.startLogDisplay('<%:Saving...%>');
        return SettingsManager.switchSetting(
            'run_mode', 
            value, 
            '<%=luci.dispatcher.build_url("admin", "services", "openclash", "switch_run_mode")%>'
        );
    }

    function ws_tmessage(event) {
        var dataObj = event && event.data !== undefined ? event.data : event;
        var data;
        if (typeof dataObj === 'string') {
            try {
                data = JSON.parse(dataObj);
            } catch (e) {
                data = {};
            }
        } else {
            data = dataObj;
        }
        var uploadElement = document.getElementById("upload_");
        var downloadElement = document.getElementById("download_");
        uploadElement.innerHTML = data.up ? "<font style=\"color:var(--success-color)\">"+bytesToSize(data.up)+"/S</font>" : "<font style=\"color:var(--success-color)\">0 B/S</font>";
        downloadElement.innerHTML = data.down ? "<font style=\"color:var(--success-color)\">"+bytesToSize(data.down)+"/S</font>" : "<font style=\"color:var(--success-color)\">0 B/S</font>";
    }

    function ws_cmessage(event) {
        var dataObj = event && event.data !== undefined ? event.data : event;
        var data;
        if (typeof dataObj === 'string') {
            try {
                data = JSON.parse(dataObj);
            } catch (e) {
                data = {};
            }
        } else {
            data = dataObj;
        }
        var updates = [
            {
                element: document.getElementById("uploadtotal_"),
                content: data.uploadTotal ? "<font style=\"color:var(--success-color)\">"+bytesToSize(data.uploadTotal)+"</font>" : "<font style=\"color:var(--success-color)\">0 KB</font>"
            },
            {
                element: document.getElementById("downloadtotal_"),
                content: data.downloadTotal ? "<font style=\"color:var(--success-color)\">"+bytesToSize(data.downloadTotal)+"</font>" : "<font style=\"color:var(--success-color)\">0 KB</font>"
            },
            {
                element: document.getElementById("connect_t"),
                content: data.connections ? "<font style=\"color:var(--success-color)\">"+Object.keys(data.connections).length+"</font>" : "<font style=\"color:var(--success-color)\">0</font>"
            }
        ];
        StateManager.batchUpdateDOM(updates);
    }

    function ws_mmessage(event) {
        var dataObj = event && event.data !== undefined ? event.data : event;
        var data;
        if (typeof dataObj === 'string') {
            try {
                data = JSON.parse(dataObj);
            } catch (e) {
                data = {};
            }
        } else {
            data = dataObj;
        }
        var memElement = document.getElementById("mem_t");
        memElement.innerHTML = data.inuse ? "<font style=\"color:var(--success-color)\">"+bytesToSize(data.inuse)+"</font>" : "<font style=\"color:var(--success-color)\">0 KB</font>";
    }

    var all_one_key_update = debounceButton(function(btn) {
        btn.value = '<%:Checking...%>';
        select_git_cdn();
        btn.value = '<%:Check Update%>';
        btn.disabled = false;
        return false;
    });

    var b_flush_dns_cache = debounceButton(function(btn) {
        btn.disabled = true;
        btn.value = '<%:Flushing...%> ';
        XHR.get('<%=luci.dispatcher.build_url("admin", "services", "openclash","flush_dns_cache")%>', null, function(x, status) {
            btn.disabled = false;
            if (x && x.status == 200) {
                btn.value = (status.flush_status == "0" || status.flush_status != "") ? '<%:Flush Failed%>' : '<%:Flush Successful%>';
            } else {
                btn.value = '<%:Flush Timeout%>';
            }
        });
        return false;
    });

    var b_reload_firewall = debounceButton(function(btn) {
        btn.disabled = true;
        btn.value = '<%:Reloading...%>';
        LogManager.startLogDisplay('<%:Reloading...%>');
        XHR.get('<%=luci.dispatcher.build_url("admin", "services", "openclash", "reload_firewall")%>', null, function(x, status) {
            btn.disabled = false;
            btn.value = (x && x.status == 200) ? '<%:Reload Firewall%>' : '<%:Firewall Rules Reset Failed%>';
        });
        return false;
    });

    var b_close_all_connection = debounceButton(function(btn) {
        btn.disabled = true;
        btn.value = '<%:Reloading...%>';
        XHR.get('<%=luci.dispatcher.build_url("admin", "services", "openclash", "close_all_connection")%>', null, function(x, status) {
            btn.disabled = false;
            btn.value = (x && x.status == 200) ? '<%:Close Connect%>' : '<%:Close Connect Failed%>';
        });
        return false;
    });

    function net_zashboard(btn) {
        btn.disabled = true;
        btn.value = '<%:Zashboard%>';
        var status = StateManager.current_status;
        if (!status) { setTimeout(function() { net_zashboard(btn); }, 500); return false; }
        winOpen(buildDashboardURL(status, 'zashboard', true));
        btn.disabled = false;
        return false;
    }

    function meta_dashboard(btn) {
        btn.disabled = true;
        btn.value = '<%:Metacubexd%>';
        var status = StateManager.current_status;
        if (!status) { setTimeout(function() { meta_dashboard(btn); }, 500); return false; }
        winOpen(buildDashboardURL(status, 'metacubexd', true));
        btn.disabled = false;
        return false;
    }

    function ycad_dashboard(btn) {
        btn.disabled = true;
        btn.value = '<%:Yacd%>';
        var status = StateManager.current_status;
        if (!status) { setTimeout(function() { ycad_dashboard(btn); }, 500); return false; }
        winOpen(buildDashboardURL(status, 'yacd', false));
        btn.disabled = false;
        return false;
    }

    function net_dashboard(btn) {
        btn.disabled = true;
        btn.value = '<%:Dashboard%>';
        var status = StateManager.current_status;
        if (!status) { setTimeout(function() { net_dashboard(btn); }, 500); return false; }
        winOpen(buildDashboardURL(status, 'dashboard', false));
        btn.disabled = false;
        return false;
    }

    function homepage() {
        winOpen('https://github.com/vernesong/OpenClash');
    }

    function gitbookpage() {
        winOpen('https://wiki.metacubex.one');
    }

    function wikipage() {
        winOpen('https://github.com/vernesong/OpenClash/blob/dev/.github/skills/openclash-user-guide/SKILL.md');
    }

    function telegrampage() {
        winOpen('https://t.me/openclash_group');
    }

    function sponsorpage() {
        winOpen('https://ko-fi.com/vernesong');
    }

    function go_mihomo() {
        winOpen('https://www.github.com/metacubex/mihomo');
    }

    function clashversion_check() {
        function compareVersions(v1, v2) {
            if (!v1 || !v2) return 0;
            var ver1 = v1.replace(/^v/, '').split('.');
            var ver2 = v2.replace(/^v/, '').split('.');

            var maxLen = Math.max(ver1.length, ver2.length);
            while (ver1.length < maxLen) ver1.push('0');
            while (ver2.length < maxLen) ver2.push('0');

            for (var i = 0; i < maxLen; i++) {
                var num1 = parseInt(ver1[i], 10) || 0;
                var num2 = parseInt(ver2[i], 10) || 0;
                if (num1 > num2) return 1;
                if (num1 < num2) return -1;
            }
            return 0;
        }

        StateManager.cachedXHRGet('<%=luci.dispatcher.build_url("admin", "services", "openclash", "update")%>', function(x, status) {
            if (x && x.status == 200) {
                if (status.coremetacv && status.coremetacv !== "0") {
                    var coreVersionText = status.coremetacv;
                    var hasUpdate = false;

                    if (status.corelv && status.corelv !== "" && status.corelv !== "loading..." && status.corelv !== status.coremetacv) {
                        hasUpdate = true;
                    }

                    DOMCache.core_version_text.textContent = coreVersionText;
                    if (DOMCache.core_version_text.scrollWidth > DOMCache.core_version_text.clientWidth) {
                        DOMCache.core_version_text.title = coreVersionText;
                    } else {
                        DOMCache.core_version_text.title = '';
                    }
                    DOMCache.core_version_display.style.display = 'flex';

                    var existingDot = DOMCache.core_version_display.querySelector('.update-dot');
                    if (existingDot) {
                        existingDot.remove();
                    }

                    if (hasUpdate) {
                        var updateDot = document.createElement('span');
                        updateDot.className = 'update-dot';
                        DOMCache.core_version_display.appendChild(updateDot);
                        DOMCache.core_version_display.title = '<%:New version available%>: ' + status.corelv;
                        DOMCache.core_version_text.removeAttribute('title');
                    } else {
                        DOMCache.core_version_display.title = '';
                    }
                } else {
                    DOMCache.core_version_display.style.display = 'none';
                    DOMCache.core_version_display.title = '';
                }

                if (status.opcv && status.opcv !== "0") {
                    var pluginVersionText = status.opcv;
                    var hasUpdate = false;

                    if (status.oplv && status.oplv !== "" && status.oplv !== "loading...") {
                        if (compareVersions(status.oplv, status.opcv) > 0) {
                            hasUpdate = true;
                        }
                    }

                    DOMCache.plugin_version_text.textContent = pluginVersionText;
                    if (DOMCache.plugin_version_text.scrollWidth > DOMCache.plugin_version_text.clientWidth) {
                        DOMCache.plugin_version_text.title = pluginVersionText;
                    } else {
                        DOMCache.plugin_version_text.title = '';
                    }
                    DOMCache.plugin_version_display.style.display = 'flex';

                    var existingDot = DOMCache.plugin_version_display.querySelector('.update-dot');
                    if (existingDot) {
                        existingDot.remove();
                    }

                    if (hasUpdate) {
                        var updateDot = document.createElement('span');
                        updateDot.className = 'update-dot';
                        DOMCache.plugin_version_display.appendChild(updateDot);
                        DOMCache.plugin_version_display.title = '<%:New version available%>: ' + status.oplv;
                        DOMCache.plugin_version_text.removeAttribute('title');
                    } else {
                        DOMCache.plugin_version_display.title = '';
                    }
                } else {
                    DOMCache.plugin_version_display.style.display = 'none';
                    DOMCache.plugin_version_display.title = '';
                }
            }
        });
    }

    function logo_error(imgobj, imgSrc) {
        imgobj.src = imgSrc;
    }

    function imgerrorfuns(imgobj, imgSrc) {
        setTimeout(function() {
            imgobj.src = imgSrc;
            imgobj.loading = "lazy";
        }, 10000);
    }

    function check_core() {
        StateManager.cachedXHRGet('<%=luci.dispatcher.build_url("admin", "services", "openclash", "check_core")%>', function(x, status) {
            if (x && x.status == 200) {
                if (status.core_status != "1") {
                    var r = confirm("<%:You have not installed the core yet, do you want to download and install it now?%>");
                    if (r == true) {
                        return select_git_cdn("core_download");
                    }
                }
            }
        });
    }

    function copyAddress() {
        XHR.get('<%=luci.dispatcher.build_url("admin", "services", "openclash", "dashboard_type")%>', null, function(x, status) {
            if (x && x.status == 200) {
                var panel = status.default_dashboard;
                if (panel && StateManager.current_status[panel]) {
                    var url;
                    var currentStatus = StateManager.current_status;
                    if (currentStatus.daip && window.location.hostname == currentStatus.daip) {
                        url = 'http://' + window.location.hostname + ':' + currentStatus.cn_port + '/ui/' + panel;
                    } else if (currentStatus.daip && window.location.hostname != currentStatus.daip && currentStatus.db_foward_domain && currentStatus.db_foward_port) {
                        var ui_proto = currentStatus.db_forward_ssl == 0 ? 'http://' : 'https://';
                        url = ui_proto + currentStatus.db_foward_domain + ':' + currentStatus.db_foward_port + '/ui/' + panel;
                    } else {
                        url = 'http://' + window.location.hostname + ':' + currentStatus.cn_port + '/ui/' + panel;
                    }

                    if (panel === 'zashboard' || panel === 'metacubexd') {
                        url += '/#/setup?hostname=' + (currentStatus.daip && window.location.hostname == currentStatus.daip ? window.location.hostname : (currentStatus.db_foward_domain || window.location.hostname)) + '&port=' + (currentStatus.daip && window.location.hostname == currentStatus.daip ? currentStatus.cn_port : (currentStatus.db_foward_port || currentStatus.cn_port)) + (currentStatus.dase ? '&secret=' + currentStatus.dase : '');
                    } else if (panel === 'yacd') {
                        url += '/?hostname=' + (currentStatus.daip && window.location.hostname == currentStatus.daip ? window.location.hostname : (currentStatus.db_foward_domain || window.location.hostname)) + '&port=' + (currentStatus.daip && window.location.hostname == currentStatus.daip ? currentStatus.cn_port : (currentStatus.db_foward_port || currentStatus.cn_port)) + (currentStatus.dase ? '&secret=' + currentStatus.dase : '');
                    } else if (panel === 'dashboard') {
                        url += '/#/?host=' + (currentStatus.daip && window.location.hostname == currentStatus.daip ? window.location.hostname : (currentStatus.db_foward_domain || window.location.hostname)) + '&port=' + (currentStatus.daip && window.location.hostname == currentStatus.daip ? currentStatus.cn_port : (currentStatus.db_foward_port || currentStatus.cn_port)) + (currentStatus.dase ? '&secret=' + currentStatus.dase : '');
                    }

                    ocCopyToClipboard(url, DOMCache.copy_address, '<%:Control panel address copied:%> ', '<%:Copy failed, please copy manually:%>');
                } else {
                    var fallbackUrl = 'http://' + (StateManager.current_status.daip || 'unknown') + ':' + (StateManager.current_status.cn_port || '9090') + '/ui/zashboard/#/';
                    ocCopyToClipboard(fallbackUrl, DOMCache.copy_address, '<%:Control panel address copied:%> ', '<%:Copy failed, please copy manually:%>');
                }
            } else {
                var fallbackUrl = 'http://' + (StateManager.current_status.daip || 'unknown') + ':' + (StateManager.current_status.cn_port || '9090') + '/ui/zashboard/#/';
                ocCopyToClipboard(fallbackUrl, DOMCache.copy_address, '<%:Control panel address copied:%> ', '<%:Copy failed, please copy manually:%>');
            }
        });
        return false;
    }

    function copySecret() {
        var secret = StateManager.current_status.dase || '';
        if (secret === '') {
            alert('<%:No control panel secret set%>');
            return false;
        }
        ocCopyToClipboard(secret, DOMCache.copy_secret, '<%:Control panel secret copied:%> ', '<%:Copy failed, please copy manually:%>');
        return false;
    }

    function copyMixAuth() {
        if (StateManager.cached_proxy_info) {
            if (StateManager.cached_proxy_info.auth_user && StateManager.cached_proxy_info.auth_pass) {
                var authText = StateManager.cached_proxy_info.auth_user + ':' + StateManager.cached_proxy_info.auth_pass;
                ocCopyToClipboard(authText, DOMCache.copy_mix_secret, '<%:Proxy auth info copied:%> ', '<%:Copy failed, please copy manually:%>');
            } else {
                alert('<%:No proxy auth info set%>');
            }
        } else {
            alert('<%:Proxy info not available, please try again later%>');
        }
        return false;
    }

    function copyMixAddress() {
        if (StateManager.cached_proxy_info && StateManager.current_status.daip) {
            var mixPort = StateManager.cached_proxy_info.mixed_port || '7893';
            var proxyIp = StateManager.current_status.daip;
            var proxyText = proxyIp + ':' + mixPort;
            if (StateManager.cached_proxy_info.auth_user && StateManager.cached_proxy_info.auth_pass) {
                proxyText = StateManager.cached_proxy_info.auth_user + ':' + StateManager.cached_proxy_info.auth_pass + '@' + proxyText;
            }
            proxyText = 'http://' + proxyText;
            ocCopyToClipboard(proxyText, DOMCache.copy_mix_address, '<%:Mix proxy address copied:%> ', '<%:Copy failed, please copy manually:%>');
        } else {
            alert('<%:Proxy info not available, please try again later%>');
        }
        return false;
    }

    function get_oc_settings() {
        if (SettingsManager.isPollPaused('oc_settings')) {
            return;
        }

        StateManager.cachedXHRGet('<%=luci.dispatcher.build_url("admin", "services", "openclash", "oc_settings")%>', function(x, info) {
            if (x && x.status == 200) {
                if (!SettingsManager.pendingOperations.has('meta_sniffer_' + info.meta_sniffer)) {
                    DOMCache.meta_sniffer_on.checked = info.meta_sniffer == "1";
                    DOMCache.meta_sniffer_off.checked = info.meta_sniffer != "1";
                }

                if (!SettingsManager.pendingOperations.has('respect_rules_' + info.respect_rules)) {
                    DOMCache.respect_rules_on.checked = info.respect_rules == "1";
                    DOMCache.respect_rules_off.checked = info.respect_rules != "1";
                }

                if (!SettingsManager.pendingOperations.has('oversea_' + info.oversea)) {
                    if (info.oversea == "0") {
                        DOMCache.oc_setting_oversea_0.checked = true;
                    } else if (info.oversea == "1") {
                        DOMCache.oc_setting_oversea_1.checked = true;
                    } else if (info.oversea == "2") {
                        DOMCache.oc_setting_oversea_2.checked = true;
                    }
                }

                if (!SettingsManager.pendingOperations.has('stream_unlock_' + info.stream_unlock)) {
                    DOMCache.stream_unlock_on.checked = info.stream_unlock === '1';
                    DOMCache.stream_unlock_off.checked = info.stream_unlock !== '1';
                }
            }
        }, true);
    }

    function switch_oc_setting_oversea(value) {
        LogManager.startLogDisplay('<%:Saving...%>');
        return SettingsManager.switchSetting(
            'oversea', 
            value, 
            '<%=luci.dispatcher.build_url("admin", "services", "openclash", "switch_oc_setting")%>'
        );
    }

    function switch_meta_sniffer(value) {
        return SettingsManager.switchSetting(
            'meta_sniffer', 
            value, 
            '<%=luci.dispatcher.build_url("admin", "services", "openclash", "switch_oc_setting")%>'
        );
    }

    function switch_respect_rules(value) {
        return SettingsManager.switchSetting(
            'respect_rules', 
            value, 
            '<%=luci.dispatcher.build_url("admin", "services", "openclash", "switch_oc_setting")%>'
        );
    }

    function switch_stream_unlock(value) {
        return SettingsManager.switchSetting(
            'stream_unlock',
            value,
            '<%=luci.dispatcher.build_url("admin", "services", "openclash", "switch_oc_setting")%>'
        );
    }

    function generatePacConfig() {
        if (StateManager.current_status.daip) {
            var currentUrl = {
                protocol: window.location.protocol,
                hostname: window.location.hostname,
                host: window.location.host,
                port: window.location.port,
                href: window.location.href
            };

            XHR.get('<%=luci.dispatcher.build_url("admin", "services", "openclash", "generate_pac")%>', {
                client_protocol: currentUrl.protocol.replace(':', ''),
                client_hostname: currentUrl.hostname,
                client_host: currentUrl.host,
                client_port: currentUrl.port || '',
                client_href: currentUrl.href
            }, function(x, data) {
                if (x && x.status == 200 && data.pac_url) {
                    if (data.error && data.error !== "") {
                        alert(data.error);
                    }
                    ocCopyToClipboard(data.pac_url, DOMCache.copy_pac_config, '<%:PAC file URL copied:%> ', '<%:Copy failed, please copy manually:%>');
                } else if (data.error) {
                    errorinfos = {
                        'Proxy service not running': '<%:Proxy service not running%>',
                        'Unable to get proxy IP': '<%:Unable to get proxy IP%>',
                        'Failed to write PAC file': '<%:Failed to write PAC file%>'
                    };
                    var errorMsg = errorinfos[data.error] || data.error;
                    alert('<%:PAC file generation failed%>: ' + errorMsg);
                } else {
                    alert('<%:PAC file generation failed%>');
                }
            });
        } else {
            alert('<%:Proxy service not available, please try again later%>');
        }
        return false;
    }

    function togglePlugin(toggleElement) {
        var isEnabled = toggleElement.checked;

        if (isEnabled) {
            var currentConfig = ConfigFileManager.getCurrentConfig() || ConfigFileManager.getSelectedConfig();
            if (!currentConfig) {
                toggleElement.checked = false;
                alert('<%:Please select a config file first%>');
                return false;
            }
        }

        toggleElement.disabled = true;
        pluginToggleUserAction = true;
        var action = isEnabled ? 'start' : 'stop';
        var initialMessage = isEnabled ? '<%:Starting...%>' : '<%:Stopping...%>';

        LogManager.startLogDisplay(initialMessage);

        var requestParams = { action: action };

        if (isEnabled) {
            var currentConfig = ConfigFileManager.getCurrentConfig();
            var selectedConfig = ConfigFileManager.getSelectedConfig();

            if (!currentConfig && selectedConfig) {
                requestParams.config_file = selectedConfig;
            }
        }

        XHR.get('<%=luci.dispatcher.build_url("admin", "services", "openclash", "action")%>', requestParams, function(x, status) {
            if (x && x.status == 200) {
                setTimeout(function() {
                    pluginToggleUserAction = false;
                    updatePluginToggleState(StateManager.current_status.clash || false);
                }, 2000);
            } else {
                toggleElement.checked = !isEnabled;

                var errorMessage = isEnabled ? 
                    '<%:Failed to start OpenClash%>' : 
                    '<%:Failed to stop OpenClash%>';
                alert(errorMessage);

                if (DOMCache.clash) {
                    DOMCache.clash.innerHTML = '<b style="color:var(--error-color)"><%:Operation Failed%></b>';
                }

                pluginToggleUserAction = false;
            }
            toggleElement.disabled = false;
        });
    }

    function updatePluginToggleState(isRunning) {
        if (pluginToggleUserAction) {
            return;
        }

        var toggleElement = document.getElementById('plugin_toggle');
        if (toggleElement) {
            toggleElement.checked = isRunning;
            toggleElement.disabled = false;

            if (DOMCache.clash && StateManager.current_status) {
                DOMCache.clash.innerHTML = isRunning ? 
                    '<b style=color:var(--success-color)>' + StateManager.current_status.core_type +'&nbsp;<%:Running%></b>' : 
                    '<b style=color:var(--error-color)><%:Not Running%></b>';
            }
        }
    }

    function switchConfig() {
        var currentConfig = ConfigFileManager.getSelectedConfig();
        if (!currentConfig) {
            alert('<%:Please select a config file first%>');
            return false;
        }

        pluginToggleUserAction = true;

        LogManager.startLogDisplay('<%:Switching Config...%>');

        XHR.get('<%=luci.dispatcher.build_url("admin", "services", "openclash", "switch_config")%>', {
            config_file: currentConfig
        }, function(x, status) {
            if (x && x.status == 200 && status.status === 'success') {
                ConfigFileManager.refreshConfigList();
                setTimeout(function() {
                    pluginToggleUserAction = false;
                    updatePluginToggleState(StateManager.current_status.clash || false);
                }, 2000);
            } else {
                alert('<%:Failed to switch config file:%> ' + (status.message || '<%:Unknown error%>'));

                if (DOMCache.oclog) {
                    DOMCache.oclog.innerHTML = '<b style="color:var(--error-color)"><%:Switch Failed%></b>';
                }

                pluginToggleUserAction = false;
            }
        });

        return false;
    }

    function updateConfig() {
        var currentConfig = ConfigFileManager.getSelectedConfig();
        if (!currentConfig) {
            alert('<%:Please select a config file first%>');
            return false;
        }

        var filename = SubscriptionManager.extractFilename(currentConfig);
        if (!filename) {
            alert('<%:Invalid config file selected%>');
            return false;
        }

        pluginToggleUserAction = true;

        LogManager.startLogDisplay('<%:Updating Config...%>');

        XHR.get('<%=luci.dispatcher.build_url("admin", "services", "openclash", "update_config")%>', {
            filename: filename
        }, function(x, status) {
            if (x && x.status == 200) {
                if (status.status === 'success') {
                    setTimeout(function() {

                        refreshSubscriptionInfo();

                        ConfigFileManager.refreshConfigList();

                        pluginToggleUserAction = false;

                    }, 2000);
                } else {
                    pluginToggleUserAction = false;

                    if (DOMCache.oclog) {
                        DOMCache.oclog.innerHTML = '<b style="color:var(--error-color)"><%:Update Failed%></b>';
                    }

                    alert('<%:Failed to update config file:%> ' + (status.message || status.error || '<%:Unknown error%>'));
                }
            } else {
                pluginToggleUserAction = false;

                if (DOMCache.oclog) {
                    DOMCache.oclog.innerHTML = '<b style="color:var(--error-color)"><%:Update Failed%></b>';
                }

                alert('<%:Failed to update config file, please try again later%>');
            }
        });

        return false;
    }

    function restartCore() {
        var currentConfig = ConfigFileManager.getCurrentConfig() || ConfigFileManager.getSelectedConfig();
        if (!currentConfig) {
            alert('<%:Please select a config file first%>');
            return false;
        }

        pluginToggleUserAction = true;

        var toggleElement = document.getElementById('plugin_toggle');
        if (toggleElement) {
            toggleElement.disabled = true;
        }

        LogManager.startLogDisplay('<%:Restarting...%>');

        var requestParams = { action: 'restart' };

        var currentConfigValue = ConfigFileManager.getCurrentConfig();
        var selectedConfig = ConfigFileManager.getSelectedConfig();

        if (!currentConfigValue && selectedConfig) {
            requestParams.config_file = selectedConfig;
        }

        XHR.get('<%=luci.dispatcher.build_url("admin", "services", "openclash", "action")%>', requestParams, function(x, status) {
            if (x && x.status == 200) {
                setTimeout(function() {
                    pluginToggleUserAction = false;
                    updatePluginToggleState(StateManager.current_status.clash || false);
                }, 2000);
                ConfigFileManager.refreshConfigList();
            } else {
                if (toggleElement) {
                    toggleElement.disabled = false;
                }
                alert('<%:Failed to restart core%>');

                pluginToggleUserAction = false;
            }
        });
        return false;
    }

    function refreshSubscriptionInfo() {
        var currentConfig = ConfigFileManager.getSelectedConfig();
        if (!currentConfig) {
            alert('<%:Please select a config file first%>');
            return false;
        }

        SubscriptionManager.currentConfigFile = currentConfig;
        SubscriptionManager.retryCount = 0;

        var filename = SubscriptionManager.extractFilename(currentConfig);
        localStorage.removeItem('sub_info_' + filename);
        SubscriptionManager.getSubscriptionInfo();
        OverwriteSubscribeManager.load(true);
        return false;
    }

    function setSubscriptionUrl() {
        var currentConfig = ConfigFileManager.getSelectedConfig();
        if (!currentConfig) {
            alert('<%:Please select a config file first%>');
            return false;
        }

        var filename = SubscriptionManager.extractFilename(currentConfig);
        if (!filename) {
            alert('<%:Invalid config file selected%>');
            return false;
        }

        SubscriptionUrlSetter.show(filename, "refreshSubscriptionInfo()");
        return false;
    }

    function uploadConfig() {
        if (typeof ConfigUploader !== 'undefined' && ConfigUploader.show) {
            ConfigUploader.show("ConfigFileManager.refreshConfigList()");
        } else {
            setTimeout(function() {
                if (typeof ConfigUploader !== 'undefined' && ConfigUploader.show) {
                    ConfigUploader.show("ConfigFileManager.refreshConfigList()");
                } else {
                    alert('<%:Config editor not ready, please try again%>');
                }
            }, 500);
        }

        return false;
    }

    function editSubscribe() {
        var currentConfig = ConfigFileManager.getSelectedConfig();
        if (!currentConfig) {
            alert('<%:Please select a config file first%>');
            return false;
        }

        var filename = SubscriptionManager.extractFilename(currentConfig);
        if (!filename) {
            alert('<%:Invalid config file selected%>');
            return false;
        }

        XHR.get('<%=luci.dispatcher.build_url("admin", "services", "openclash", "get_subscribe_data")%>', {
            filename: filename
        }, function(x, status) {
            if (x.status == 200) {
                if (ConfigUploader && ConfigUploader.showEditSubscribe) {
                    ConfigUploader.showEditSubscribe(status, filename, "ConfigFileManager.refreshConfigList()");
                } else {
                    setTimeout(function() {
                        if (ConfigUploader && ConfigUploader.showEditSubscribe) {
                            ConfigUploader.showEditSubscribe(status, filename, "ConfigFileManager.refreshConfigList()");
                        } else {
                            alert('<%:Config editor not ready, please try again%>');
                        }
                    }, 500);
                }
            } else {
                alert('<%:Failed to get subscribe data%>');
            }
        });

        return false;
    }

    function editConfig() {
        var currentConfig = ConfigFileManager.getSelectedConfig();
        if (!currentConfig) {
            alert('<%:Please select a config file first%>');
            return false;
        }

        if (ConfigEditor.show) {
            ConfigEditor.show(currentConfig);
        } else {
            setTimeout(function() {
                if (ConfigEditor.show) {
                    ConfigEditor.show(currentConfig);
                } else {
                    alert('<%:Config editor not ready, please try again%>');
                }
            }, 500);
        }

        return false;
    }

    function editOverwrite() {
        if (ConfigEditor.showOverwrite) {
            ConfigEditor.showOverwrite();
        } else {
            setTimeout(function() {
                if (ConfigEditor.showOverwrite) {
                    ConfigEditor.showOverwrite();
                } else {
                    alert('<%:Config editor not ready, please try again%>');
                }
            }, 500);
        }

        return false;
    }

    function switchToPreviousConfig() {
        if (ConfigFileManager.configList.length > 1) {
            var newIndex = ConfigFileManager.currentConfigIndex - 1;
            if (newIndex < 0) {
                newIndex = ConfigFileManager.configList.length - 1;
            }
            ConfigFileManager.switchToConfigByIndex(newIndex);
        }
        return false;
    }

    function switchToNextConfig() {
        if (ConfigFileManager.configList.length > 1) {
            var newIndex = ConfigFileManager.currentConfigIndex + 1;
            if (newIndex >= ConfigFileManager.configList.length) {
                newIndex = 0;
            }
            ConfigFileManager.switchToConfigByIndex(newIndex);
        }
        return false;
    }

    function toggleThemeMode() {
        var themes = ['light', 'dark', 'auto'];
        var currentTheme = localStorage.getItem('oc-theme') || 'auto';
        var currentIndex = themes.indexOf(currentTheme);
        if (currentIndex === -1) currentIndex = 2;
        var newIndex = (currentIndex + 1) % themes.length;
        var newTheme = themes[newIndex];
        localStorage.setItem('oc-theme', newTheme);
        DarkModeDetector.init();
    }
//]]></script>
</html>
