{% extends "base.html" %}
{% from "macros/icons.html" import icon %}

{% block title_extra %} — {{ server.name }}{% endblock %}

{% block head_extra %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/theme/material-darker.min.css">
<style>
    .client-actions {
        display: flex !important;
        flex-direction: row !important;
        flex-wrap: nowrap !important;
        gap: var(--space-xs) !important;
        align-items: center;
    }
    .marketplace-grid {
        display: grid;
        grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
        gap: var(--space-md);
    }
    .marketplace-category {
        margin-bottom: var(--space-lg);
    }
    .marketplace-category-title {
        font-weight: 700;
        margin-bottom: var(--space-sm);
        display: flex;
        align-items: center;
        gap: var(--space-xs);
    }
    .marketplace-app-card {
        border: 1px solid var(--border-color);
        border-radius: var(--radius-lg);
        padding: var(--space-md);
        background: var(--bg-secondary);
        display: flex;
        flex-direction: column;
        gap: var(--space-sm);
    }
    .marketplace-app-top {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: var(--space-sm);
    }
    .marketplace-app-title {
        font-weight: 700;
    }
    .marketplace-app-desc {
        color: var(--text-muted);
        font-size: 0.88rem;
        line-height: 1.35;
        min-height: 2.4em;
    }
    .server-status-row {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: var(--space-md);
        flex-wrap: wrap;
    }
    .server-status-actions {
        display: flex;
        align-items: center;
        gap: var(--space-sm);
        margin-left: auto;
    }
    .installed-app-hidden,
    .promo-block.installed-app-hidden,
    .protocol-cards > .promo-block {
        display: none !important;
    }
    .protocol-card.installed-app-visible {
        display: block !important;
    }
    .apps-loading {
        margin-bottom: var(--space-xl);
        min-height: 140px;
        border: 1px dashed var(--border-color);
        border-radius: var(--radius-lg);
        display: flex;
        align-items: center;
        justify-content: center;
        gap: var(--space-sm);
        color: var(--text-muted);
        background: var(--bg-secondary);
    }
    .config-editor-wrap {
        border: 1px solid var(--border-color);
        border-radius: var(--radius-lg);
        overflow: hidden;
        background: #111827;
    }
    .config-editor-wrap .CodeMirror {
        height: 50vh;
        min-height: 360px;
        font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
        font-size: 13px;
        line-height: 1.55;
    }
    .config-editor-wrap .CodeMirror-gutters {
        border-right: 1px solid rgba(255,255,255,0.08);
    }
    .config-editor-fallback {
        width: 100%;
        height: 50vh;
        min-height: 360px;
        resize: vertical;
        font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
        white-space: pre;
        padding: var(--space-sm);
    }
    .config-editor-hint {
        margin-top: var(--space-xs);
        color: var(--text-muted);
        font-size: 0.78rem;
    }
</style>
{% endblock %}

{% block content %}
<a href="/" class="back-link">{{ _('back_to_servers') }}</a>

<!-- Server Info Header -->
<div class="card" style="margin-bottom: var(--space-xl);">
    <div class="flex items-center justify-between" style="flex-wrap: wrap; gap: var(--space-md);">
        <div class="flex items-center gap-md">
            <div class="server-icon" style="width:56px; height:56px; font-size:1.6rem;">{{ icon('server') }}</div>
            <div>
                <h1 style="font-size:1.4rem; font-weight:700;">{{ server.name }}</h1>
                <div class="font-mono text-muted text-sm">{{ server.host }}:{{ server.ssh_port }} • {{ server.username
                    }}</div>
            </div>
        </div>
        <div class="flex gap-sm items-center">
            <div class="flex gap-sm items-center" id="serverStats" style="display:none;"></div>
            <button class="btn btn-secondary btn-sm" onclick="checkServer()" id="checkBtn">
                <span id="checkBtnText">{{ _('server_check') }}</span>
                <div class="spinner hidden" id="checkSpinner"></div>
            </button>
        </div>
    </div>
    <div id="serverStatus" class="mt-md hidden">
        <div class="divider"></div>
        <div class="server-status-row">
            <div class="flex gap-md" style="flex-wrap:wrap" id="statusBadges"></div>
            <div class="server-status-actions">
                <button class="btn btn-primary btn-sm" onclick="openMarketplaceModal()" id="marketplaceBtn">
                    <span>{{ icon('cart') }}</span> {{ _('marketplace') }}
                </button>
                <button class="btn btn-secondary btn-sm" onclick="openManagementModal()" id="managementBtn">
                    <span>{{ icon('settings') }}</span> {{ _('management') }}
                </button>
            </div>
        </div>
    </div>
</div>

<!-- Installed Applications Section -->

<div class="apps-loading" id="installedAppsLoading">
    <div class="loading-spinner"></div>
    <span>{{ _('checking_server') }}</span>
</div>
<div class="empty-state hidden" id="installedAppsEmpty" style="padding: var(--space-xl) 0; margin-bottom: var(--space-xl);">
    <div class="empty-icon">{{ icon('cart') }}</div>
    <div class="empty-title">{{ _('no_installed_apps') }}</div>
    <div class="empty-desc">{{ _('no_installed_apps_desc') }}</div>
    <button class="btn btn-primary btn-sm" onclick="openMarketplaceModal()" style="margin-top: var(--space-md);">
        {{ _('open_marketplace') }}
    </button>
</div>

<div class="protocol-cards" id="protocols-grid">
    <!-- AWG2 Card (first - new version) -->
    <div class="card card-hover protocol-card protocol-awg" id="proto-awg2">
        <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--space-sm);">
            <div class="protocol-icon">{{ icon('sparkles') }}</div>
            <div class="flex gap-sm" id="awg2-ctrl" style="display:none!important;"></div>
        </div>
        <div class="protocol-name">AmneziaWG 2.0 <span
                style="font-size:0.65rem; background:var(--accent); color:#fff; padding:2px 6px; border-radius:8px; vertical-align:middle;">NEW</span>
        </div>
        <div class="protocol-desc">
            {{ _('awg_desc') }}
        </div>
        <div class="protocol-status" id="awg2-status">
            <span class="badge badge-warn"><span class="badge-dot"></span> {{ _('not_checked') }}</span>
        </div>
        <div id="awg2-info" class="hidden">
            <div class="protocol-info" id="awg2-info-grid"></div>
        </div>
        <div class="flex gap-sm" id="awg2-actions">
            <button class="btn btn-primary btn-sm" onclick="openInstallModal('awg2')" id="awg2-install-btn"
                style="flex:1">
                {{ _('install') }}
            </button>
        </div>
    </div>

    <!-- AWG Card -->
    <div class="card card-hover protocol-card protocol-awg" id="proto-awg">
        <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--space-sm);">
            <div class="protocol-icon">{{ icon('shield') }}</div>
            <div class="flex gap-sm" id="awg-ctrl" style="display:none!important;"></div>
        </div>
        <div class="protocol-name">AmneziaWG</div>
        <div class="protocol-desc">
            {{ _('awg_desc') }}
        </div>
        <div class="protocol-status" id="awg-status">
            <span class="badge badge-warn"><span class="badge-dot"></span> {{ _('not_checked') }}</span>
        </div>
        <div id="awg-info" class="hidden">
            <div class="protocol-info" id="awg-info-grid"></div>
        </div>
        <div class="flex gap-sm" id="awg-actions">
            <button class="btn btn-primary btn-sm" onclick="openInstallModal('awg')" id="awg-install-btn"
                style="flex:1">
                {{ _('install') }}
            </button>
        </div>
    </div>

    <!-- AWG Legacy Card -->
    <div class="card card-hover protocol-card protocol-legacy" id="proto-awg-legacy">
        <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--space-sm);">
            <div class="protocol-icon">{{ icon('radio') }}</div>
            <div class="flex gap-sm" id="awg-legacy-ctrl" style="display:none!important;"></div>
        </div>
        <div class="protocol-name">AmneziaWG Legacy</div>
        <div class="protocol-desc">
            {{ _('awg_legacy_desc') }}
        </div>
        <div class="protocol-status" id="awg-legacy-status">
            <span class="badge badge-warn"><span class="badge-dot"></span> {{ _('not_checked') }}</span>
        </div>
        <div id="awg-legacy-info" class="hidden">
            <div class="protocol-info" id="awg-legacy-info-grid"></div>
        </div>
        <div class="flex gap-sm" id="awg-legacy-actions">
            <button class="btn btn-primary btn-sm" onclick="openInstallModal('awg_legacy')" id="awg-legacy-install-btn"
                style="flex:1">
                {{ _('install') }}
            </button>
        </div>
    </div>

    <!-- Xray Card -->
    <div class="card card-hover protocol-card protocol-xray" id="proto-xray">
        <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--space-sm);">
            <div class="protocol-icon">{{ icon('zap') }}</div>
            <div class="flex gap-sm" id="xray-ctrl" style="display:none!important;"></div>
        </div>
        <div class="protocol-name">Xray (VLESS-Reality)</div>
        <div class="protocol-desc">
            {{ _('xray_desc') }}
        </div>
        <div class="protocol-status" id="xray-status">
            <span class="badge badge-warn"><span class="badge-dot"></span> {{ _('not_checked') }}</span>
        </div>
        <div id="xray-info" class="hidden">
            <div class="protocol-info" id="xray-info-grid"></div>
        </div>
        <div class="flex gap-sm" id="xray-actions">
            <button class="btn btn-primary btn-sm" onclick="openInstallModal('xray')" id="xray-install-btn"
                style="flex:1">
                {{ _('install') }}
            </button>
        </div>
    </div>

    <!-- Telemt Card -->
    <div class="card card-hover protocol-card protocol-telemt" id="proto-telemt">
        <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--space-sm);">
            <div class="protocol-icon">{{ icon('plane') }}</div>
            <div class="flex gap-sm" id="telemt-ctrl" style="display:none!important;"></div>
        </div>
        <div class="protocol-name">Telemt (Telegram Proxy)</div>
        <div class="protocol-desc">
            {{ _('telemt_desc') }}
        </div>
        <div class="protocol-status" id="telemt-status">
            <span class="badge badge-warn"><span class="badge-dot"></span> {{ _('not_checked') }}</span>
        </div>
        <div id="telemt-info" class="hidden">
            <div class="protocol-info" id="telemt-info-grid"></div>
        </div>
        <div class="flex gap-sm" id="telemt-actions">
            <button class="btn btn-primary btn-sm" onclick="openInstallModal('telemt')" id="telemt-install-btn"
                style="flex:1">
                {{ _('install') }}
            </button>
        </div>
    </div>

    <!-- Hysteria Card -->
    <div class="card card-hover protocol-card protocol-hysteria" id="proto-hysteria">
        <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--space-sm);">
            <div class="protocol-icon">{{ icon('refresh') }}</div>
            <div class="flex gap-sm" id="hysteria-ctrl" style="display:none!important;"></div>
        </div>
        <div class="protocol-name">Hysteria 2</div>
        <div class="protocol-desc">
            {{ _('hysteria_desc') }}
        </div>
        <div class="protocol-status" id="hysteria-status">
            <span class="badge badge-warn"><span class="badge-dot"></span> {{ _('not_checked') }}</span>
        </div>
        <div id="hysteria-info" class="hidden">
            <div class="protocol-info" id="hysteria-info-grid"></div>
        </div>
        <div class="flex gap-sm" id="hysteria-actions">
            <button class="btn btn-primary btn-sm" onclick="openInstallModal('hysteria')" id="hysteria-install-btn"
                style="flex:1">
                {{ _('install') }}
            </button>
        </div>
    </div>

    <!-- NaiveProxy Card -->
    <div class="card card-hover protocol-card protocol-naiveproxy" id="proto-naiveproxy">
        <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--space-sm);">
            <div class="protocol-icon">{{ icon('link') }}</div>
            <div class="flex gap-sm" id="naiveproxy-ctrl" style="display:none!important;"></div>
        </div>
            <div class="protocol-name">NaiveProxy <span
                style="font-size:0.65rem; background:var(--success, #16a34a); color:#fff; padding:2px 6px; border-radius:8px; vertical-align:middle;">STABLE</span></div>
            <div class="protocol-desc">
                {{ _('naiveproxy_desc') }}
            </div>
        <div class="protocol-status" id="naiveproxy-status">
            <span class="badge badge-warn"><span class="badge-dot"></span> {{ _('not_checked') }}</span>
        </div>
        <div id="naiveproxy-info" class="hidden">
            <div class="protocol-info" id="naiveproxy-info-grid"></div>
        </div>
        <div class="flex gap-sm" id="naiveproxy-actions">
            <button class="btn btn-primary btn-sm" onclick="openInstallModal('naiveproxy')" id="naiveproxy-install-btn"
                style="flex:1">
                {{ _('install') }}
            </button>
        </div>
    </div>

    <!-- WireGuard Card -->
    <div class="card card-hover protocol-card protocol-wireguard" id="proto-wireguard">
        <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--space-sm);">
            <div class="protocol-icon">{{ icon('lock') }}</div>
            <div class="flex gap-sm" id="wireguard-ctrl" style="display:none!important;"></div>
        </div>
        <div class="protocol-name">WireGuard</div>
        <div class="protocol-desc">
            {{ _('wireguard_desc') }}
        </div>
        <div class="protocol-status" id="wireguard-status">
            <span class="badge badge-warn"><span class="badge-dot"></span> {{ _('not_checked') }}</span>
        </div>
        <div id="wireguard-info" class="hidden">
            <div class="protocol-info" id="wireguard-info-grid"></div>
        </div>
        <div class="flex gap-sm" id="wireguard-actions">
            <button class="btn btn-primary btn-sm" onclick="openInstallModal('wireguard')" id="wireguard-install-btn"
                style="flex:1">
                {{ _('install') }}
            </button>
        </div>
    </div>

    <!-- ===== AIVPN teaser (locked, full grid width) ===== -->
    <div class="promo-block promo-aivpn" aria-disabled="true" role="article">
        <div class="promo-orbs" aria-hidden="true">
            <span class="promo-orb"></span>
            <span class="promo-orb"></span>
            <span class="promo-orb"></span>
        </div>
        <span class="promo-lock-badge">{{ icon('lock') }} {{ _('coming_soon') }}</span>
        <div class="promo-content">
            <div class="promo-icon" aria-hidden="true">{{ icon('brain') }}</div>
            <div class="promo-text">
                <div class="promo-title">AIVPN</div>
                <div class="promo-subtitle">{{ _('aivpn_subtitle') }}</div>
            </div>
            <a class="promo-cta" href="https://github.com/PRVTPRO/Amnezia-Web-Panel" target="_blank" rel="noopener">
                ⭐ <span>{{ _('promo_star_cta') }}</span>
            </a>
        </div>
    </div>

    <!-- DNS Card -->
    <div class="card card-hover protocol-card protocol-dns" id="proto-dns">
        <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--space-sm);">
            <div class="protocol-icon">{{ icon('search') }}</div>
            <div class="flex gap-sm" id="dns-ctrl" style="display:none!important;"></div>
        </div>
        <div class="protocol-name">AmneziaDNS</div>
        <div class="protocol-desc">
            {{ _('dns_desc') }}
        </div>
        <div class="protocol-status" id="dns-status">
            <span class="badge badge-warn"><span class="badge-dot"></span> {{ _('not_checked') }}</span>
        </div>
        <div id="dns-info" class="hidden">
            <div class="protocol-info" id="dns-info-grid"></div>
        </div>
        <div class="flex gap-sm" id="dns-actions">
            <button class="btn btn-primary btn-sm" onclick="openInstallModal('dns')" id="dns-install-btn"
                style="flex:1">
                {{ _('install') }}
            </button>
        </div>
    </div>

    <!-- AdGuard Home Card -->
    <div class="card card-hover protocol-card protocol-adguard" id="proto-adguard">
        <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--space-sm);">
            <div class="protocol-icon">{{ icon('shield-check') }}</div>
            <div class="flex gap-sm" id="adguard-ctrl" style="display:none!important;"></div>
        </div>
        <div class="protocol-name">AdGuard Home</div>
        <div class="protocol-desc">
            {{ _('adguard_desc') }}
        </div>
        <div class="protocol-status" id="adguard-status">
            <span class="badge badge-warn"><span class="badge-dot"></span> {{ _('not_checked') }}</span>
        </div>
        <div id="adguard-info" class="hidden">
            <div class="protocol-info" id="adguard-info-grid"></div>
        </div>
        <div class="flex gap-sm" id="adguard-actions">
            <button class="btn btn-primary btn-sm" onclick="openInstallModal('adguard')" id="adguard-install-btn"
                style="flex:1">
                {{ _('install') }}
            </button>
        </div>
    </div>

    <!-- SOCKS5 Card -->
    <div class="card card-hover protocol-card protocol-socks5" id="proto-socks5">
        <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--space-sm);">
            <div class="protocol-icon">{{ icon('network') }}</div>
            <div class="flex gap-sm" id="socks5-ctrl" style="display:none!important;"></div>
        </div>
        <div class="protocol-name">SOCKS5 Proxy</div>
        <div class="protocol-desc">
            {{ _('socks5_desc') }}
        </div>
        <div class="protocol-status" id="socks5-status">
            <span class="badge badge-warn"><span class="badge-dot"></span> {{ _('not_checked') }}</span>
        </div>
        <div id="socks5-info" class="hidden">
            <div class="protocol-info" id="socks5-info-grid"></div>
        </div>
        <div class="flex gap-sm" id="socks5-actions">
            <button class="btn btn-primary btn-sm" onclick="openInstallModal('socks5')" id="socks5-install-btn"
                style="flex:1">
                {{ _('install') }}
            </button>
        </div>
    </div>


    <!-- NGINX Card -->
    <div class="card card-hover protocol-card protocol-nginx" id="proto-nginx">
        <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--space-sm);">
            <div class="protocol-icon">{{ icon('globe') }}</div>
            <div class="flex gap-sm" id="nginx-ctrl" style="display:none!important;"></div>
        </div>
        <div class="protocol-name">NGINX</div>
        <div class="protocol-desc">
            {{ _('nginx_desc') }}
        </div>
        <div class="protocol-status" id="nginx-status">
            <span class="badge badge-warn"><span class="badge-dot"></span> {{ _('not_checked') }}</span>
        </div>
        <div id="nginx-info" class="hidden">
            <div class="protocol-info" id="nginx-info-grid"></div>
        </div>
        <div class="flex gap-sm" id="nginx-actions">
            <button class="btn btn-primary btn-sm" onclick="openInstallModal('nginx')" id="nginx-install-btn"
                style="flex:1">
                {{ _('install') }}
            </button>
        </div>
    </div>

    <!-- ===== Reverse Proxy teaser (locked, full grid width) ===== -->
    <div class="promo-block promo-revproxy" aria-disabled="true" role="article">
        <div class="promo-grid-bg" aria-hidden="true"></div>
        <div class="promo-orbs" aria-hidden="true">
            <span class="promo-orb"></span>
            <span class="promo-orb"></span>
            <span class="promo-orb"></span>
        </div>
        <span class="promo-lock-badge">{{ icon('lock') }} {{ _('coming_soon') }}</span>
        <div class="promo-content">
            <div class="promo-icon" aria-hidden="true">{{ icon('shield-check') }}</div>
            <div class="promo-text">
                <div class="promo-title">{{ _('revproxy_title') }}</div>
                <div class="promo-subtitle">{{ _('revproxy_subtitle') }}</div>
            </div>
            <a class="promo-cta" href="https://github.com/PRVTPRO/Amnezia-Web-Panel" target="_blank" rel="noopener">
                ⭐ <span>{{ _('promo_star_cta') }}</span>
            </a>
        </div>
    </div>
</div>

<div class="promo-block promo-aivpn" aria-disabled="true" role="article">
    <div class="promo-orbs" aria-hidden="true">
        <span class="promo-orb"></span>
        <span class="promo-orb"></span>
        <span class="promo-orb"></span>
    </div>
    <span class="promo-lock-badge">{{ icon('lock') }} Coming soon</span>
    <div class="promo-content">
        <div class="promo-icon" aria-hidden="true">{{ icon('brain') }}</div>
        <div class="promo-text">
            <div class="promo-title">AIVPN</div>
            <div class="promo-subtitle">AI-driven protocol selection that picks the right tunnel for the moment. Land it sooner — drop a star.</div>
        </div>
        <a class="promo-cta" href="https://github.com/PRVTPRO/Amnezia-Web-Panel" target="_blank" rel="noopener">
            ⭐ <span>Star us on GitHub</span>
        </a>
    </div>
</div>


<!-- Connections Section (renamed from Clients) -->
<div class="clients-section" id="connectionsSection" style="display:none;">
    <div class="clients-header">
        <h2 class="section-title" style="margin-bottom:0;">
            <span class="icon">{{ icon('link') }}</span>
            {{ _('connections') }}
        </h2>
        <div class="flex gap-sm items-center">
            <select class="form-select" id="connProtoSelect" onchange="loadConnections()"
                style="width:auto; min-width:160px;">
                <option value="awg">AmneziaWG</option>
                <option value="awg2">AmneziaWG 2.0</option>
                <option value="awg_legacy">AWG Legacy</option>
                <option value="xray">Xray</option>
                <option value="telemt">Telemt</option>
                <option value="wireguard">WireGuard</option>
            </select>
            <button type="button" class="btn btn-secondary btn-sm hidden" id="moveConnectionsBtn" onclick="openMoveConnectionsModal()">
                <span>{{ icon('share') }}</span> {{ _('move_connections') }}
            </button>
            <button class="btn btn-primary btn-sm" onclick="openAddConnectionModal()">
                <span>{{ icon('plus') }}</span> {{ _('add') }}
            </button>
        </div>
    </div>

    <div id="connectionsBulkBar" class="connections-bulk-bar hidden">
        <label class="connections-select-all">
            <input type="checkbox" id="connSelectAll" onchange="toggleSelectAllConnections(this.checked)">
            <span>{{ _('select_all') }}</span>
        </label>
        <span class="connections-selected-count" id="connSelectedCount">0</span>
    </div>

    <div id="connectionsList">
        <div class="loading-overlay" id="connectionsLoading" style="display:none;">
            <div class="loading-spinner"></div>
            <span>{{ _('loading_connections') }}</span>
        </div>
        <div id="connectionsEmpty" class="empty-state hidden" style="padding: var(--space-xl) 0;">
            <div class="empty-icon">{{ icon('link') }}</div>
            <div class="empty-title">{{ _('no_connections') }}</div>
            <div class="empty-desc">{{ _('no_connections_desc') }}</div>
        </div>
        <div class="clients-list" id="connectionsListInner"></div>
    </div>
</div>

<!-- ===== Marketplace Modal ===== -->
<div class="modal-backdrop" id="marketplaceModal">
    <div class="modal" style="max-width: 980px;">
        <div class="modal-header">
            <div>
                <h2 class="modal-title">{{ icon('cart') }} {{ _('marketplace') }}</h2>
                <div class="text-muted text-sm">{{ _('marketplace_desc') }}</div>
            </div>
            <button class="modal-close" onclick="closeModal('marketplaceModal')">×</button>
        </div>
        <div id="marketplaceContent"></div>
    </div>
</div>

<!-- ===== Management Modal ===== -->
<div class="modal-backdrop" id="managementModal">
    <div class="modal" style="max-width: 520px;">
        <div class="modal-header">
            <div>
                <h2 class="modal-title">{{ icon('settings') }} {{ _('server_management') }}</h2>
                <div class="text-muted text-sm">{{ _('server_management_desc') }}</div>
            </div>
            <button class="modal-close" onclick="closeModal('managementModal')">×</button>
        </div>
        <div id="managementContent"></div>
    </div>
</div>

<!-- ===== Install Protocol Modal ===== -->
<div class="modal-backdrop" id="installModal">
    <div class="modal">
        <div class="modal-header">
            <h2 class="modal-title" id="installModalTitle">{{ _('install_protocol') }}</h2>
            <button class="modal-close" onclick="closeModal('installModal')">×</button>
        </div>

        <div id="installForm">
            <div class="form-group" id="installPortGroup">
                <label class="form-label" id="installPortLabel">{{ _('port') }}</label>
                <input class="form-input" type="number" id="installPort" value="55424" min="1" max="65535">
                <div class="form-hint" id="installPortHint">{{ _('port_default_hint') }}</div>
            </div>

            <div id="telemtOptions"
                style="display:none; padding: var(--space-md); background: rgba(0,0,0,0.03); border-radius: var(--radius-md); margin-bottom: var(--space-md);">
                <div class="form-group">
                    <label class="form-label" style="display:flex; justify-content:space-between; align-items:center;">
                        {{ _('tls_emulation') }}
                        <label class="switch">
                            <input type="checkbox" id="installTelemtTls" checked
                                onchange="document.getElementById('telemtDomainGroup').style.display = this.checked ? 'block' : 'none'">
                            <span class="slider"></span>
                        </label>
                    </label>
                </div>
                <div class="form-group" id="telemtDomainGroup">
                    <label class="form-label">{{ _('tls_domain') }}</label>
                    <input class="form-input" type="text" id="installTelemtDomain" value="petrovich.ru">
                </div>
                <div class="form-group">
                    <label class="form-label">{{ _('max_connections_limit') }}</label>
                    <input class="form-input" type="number" id="installTelemtMaxConns" value="0" min="0">
                    <div class="form-hint">{{ _('max_connections_hint') }}</div>
                </div>
            </div>

            <div id="socks5Options"
                style="display:none; padding: var(--space-md); background: rgba(0,0,0,0.03); border-radius: var(--radius-md); margin-bottom: var(--space-md);">
                <div class="form-group">
                    <label class="form-label">{{ _('socks5_username') }}</label>
                    <input class="form-input" type="text" id="installSocks5User" value="proxy_user">
                </div>
                <div class="form-group">
                    <label class="form-label">{{ _('socks5_password') }}</label>
                    <input class="form-input" type="text" id="installSocks5Pass" placeholder="{{ _('socks5_password_placeholder') }}">
                    <div class="form-hint">{{ _('socks5_password_hint') }}</div>
                </div>
            </div>

            <div id="adguardOptions"
                style="display:none; padding: var(--space-md); background: rgba(0,0,0,0.03); border-radius: var(--radius-md); margin-bottom: var(--space-md);">
                <div class="form-group">
                    <label class="form-label">{{ _('adguard_mode') }}</label>
                    <div style="display:flex; flex-direction:column; gap:var(--space-xs);">
                        <label style="display:flex; align-items:center; gap:var(--space-xs); cursor:pointer;">
                            <input type="radio" name="adguardMode" value="sidebyside" checked>
                            <span><strong>{{ _('adguard_mode_sidebyside') }}</strong> — {{ _('adguard_mode_sidebyside_hint') }}</span>
                        </label>
                        <label style="display:flex; align-items:center; gap:var(--space-xs); cursor:pointer;">
                            <input type="radio" name="adguardMode" value="replace">
                            <span><strong>{{ _('adguard_mode_replace') }}</strong> — {{ _('adguard_mode_replace_hint') }}</span>
                        </label>
                    </div>
                </div>
                <div class="form-row">
                    <div class="form-group">
                        <label class="form-label">{{ _('adguard_web_port') }}</label>
                        <input class="form-input" type="number" id="installAdguardWebPort" value="3000" min="1" max="65535">
                    </div>
                    <div class="form-group" style="display:flex; align-items:flex-end; padding-bottom:8px;">
                        <label style="display:flex; align-items:center; gap:var(--space-xs); cursor:pointer;">
                            <input type="checkbox" id="installAdguardExposeWeb">
                            <span>{{ _('adguard_expose_web') }}</span>
                        </label>
                    </div>
                </div>
                <div class="form-row">
                    <div class="form-group">
                        <label class="form-label">{{ _('adguard_dot_port') }}</label>
                        <input class="form-input" type="number" id="installAdguardDotPort" value="853" min="1" max="65535">
                        <label style="display:flex; align-items:center; gap:var(--space-xs); cursor:pointer; margin-top:4px;">
                            <input type="checkbox" id="installAdguardExposeDot">
                            <span>{{ _('adguard_expose') }}</span>
                        </label>
                    </div>
                    <div class="form-group">
                        <label class="form-label">{{ _('adguard_doh_port') }}</label>
                        <input class="form-input" type="number" id="installAdguardDohPort" value="443" min="1" max="65535">
                        <label style="display:flex; align-items:center; gap:var(--space-xs); cursor:pointer; margin-top:4px;">
                            <input type="checkbox" id="installAdguardExposeDoh">
                            <span>{{ _('adguard_expose') }}</span>
                        </label>
                    </div>
                </div>
                <div class="form-hint">{{ _('adguard_install_hint') }}</div>
            </div>


            <div id="nginxOptions"
                style="display:none; padding: var(--space-md); background: rgba(0,0,0,0.03); border-radius: var(--radius-md); margin-bottom: var(--space-md);">
                <div class="form-group">
                    <label class="form-label">{{ _('nginx_domain') }}</label>
                    <input class="form-input" type="text" id="installNginxDomain" placeholder="example.com" oninput="updateNginxDnsHint()">
                    <div class="form-hint" id="nginxDnsHint"></div>
                </div>
                <div class="form-group">
                    <label class="form-label">{{ _('nginx_email') }}</label>
                    <input class="form-input" type="email" id="installNginxEmail" placeholder="admin@example.com">
                </div>
                <div class="form-hint">{{ _('nginx_install_hint') }}</div>
            </div>

            <div id="hysteriaOptions"
                style="display:none; padding: var(--space-md); background: rgba(0,0,0,0.03); border-radius: var(--radius-md); margin-bottom: var(--space-md);">
                <div class="form-hint" style="margin-bottom: var(--space-md); padding: var(--space-sm) var(--space-md); border-radius: var(--radius-sm); background: rgba(234,179,8,0.12); border: 1px solid rgba(234,179,8,0.35); color: var(--text);">
                    {{ _('hysteria_ports_warning') }}
                </div>
                <div class="form-group">
                    <label class="form-label">{{ _('port') }} (UDP) *</label>
                    <input class="form-input" type="number" id="installHysteriaPort" value="8998" min="1" max="65535">
                    <div class="form-hint">{{ _('hysteria_port_hint') }}</div>
                </div>
                <div class="form-group">
                    <label class="form-label">{{ _('hysteria_domain') }}</label>
                    <input class="form-input" type="text" id="installHysteriaDomain" placeholder="vpn.example.com" oninput="updateHysteriaDnsHint()">
                    <div class="form-hint" id="hysteriaDnsHint"></div>
                </div>
                <div class="form-group">
                    <label class="form-label">{{ _('hysteria_email') }}</label>
                    <input class="form-input" type="email" id="installHysteriaEmail" placeholder="admin@example.com">
                </div>
                <div class="form-hint">{{ _('hysteria_install_hint') }}</div>
            </div>

            <div id="naiveproxyOptions"
                style="display:none; padding: var(--space-md); background: rgba(0,0,0,0.03); border-radius: var(--radius-md); margin-bottom: var(--space-md);">
                <div class="form-hint" style="margin-bottom: var(--space-md); padding: var(--space-sm) var(--space-md); border-radius: var(--radius-sm); background: rgba(234,179,8,0.12); border: 1px solid rgba(234,179,8,0.35); color: var(--text);">
                    {{ _('naiveproxy_ports_warning') }}
                </div>
                <div class="form-group">
                    <label class="form-label">{{ _('naiveproxy_domain') }}</label>
                    <input class="form-input" type="text" id="installNaiveproxyDomain" placeholder="vpn.example.com" oninput="updateNaiveproxyDnsHint()">
                    <div class="form-hint" id="naiveproxyDnsHint"></div>
                </div>
                <div class="form-group">
                    <label class="form-label">{{ _('naiveproxy_email') }}</label>
                    <input class="form-input" type="email" id="installNaiveproxyEmail" placeholder="admin@example.com">
                </div>
                <div class="form-hint">{{ _('naiveproxy_install_hint') }}</div>
                <div class="form-hint" style="margin-top: var(--space-sm);">{{ _('naiveproxy_port_hint') }}</div>
                <div class="form-hint" style="margin-top: var(--space-sm); padding: var(--space-sm) var(--space-md); border-radius: var(--radius-sm); background: rgba(234,179,8,0.12); border: 1px solid rgba(234,179,8,0.35);">{{ _('naiveproxy_client_hint') }}</div>
            </div>

            <div class="modal-footer">
                <button class="btn btn-secondary" onclick="closeModal('installModal')">{{ _('cancel') }}</button>
                <button class="btn btn-primary" onclick="installProtocol()" id="installBtn">
                    <span id="installBtnText">{{ _('install') }}</span>
                    <div class="spinner hidden" id="installSpinner"></div>
                </button>
            </div>
        </div>

        <div id="installProgress" class="hidden">
            <div class="progress-bar">
                <div class="progress-fill" id="installProgressFill" style="width: 0%"></div>
            </div>
            <div class="log-output" id="installLog"></div>
            <div class="mt-md" id="installDone" style="display:none;">
                <button class="btn btn-primary" onclick="closeModal('installModal'); window.location.reload();"
                    style="width:100%">
                    {{ _('done') }}
                </button>
            </div>
        </div>
    </div>
</div>

<!-- ===== SOCKS5 Settings Modal ===== -->
<div class="modal-backdrop" id="socks5SettingsModal">
    <div class="modal">
        <div class="modal-header">
            <h2 class="modal-title">{{ _('socks5_settings_title') }}</h2>
            <button class="modal-close" onclick="closeModal('socks5SettingsModal')">×</button>
        </div>
        <div class="form-group">
            <label class="form-label">{{ _('port') }}</label>
            <input class="form-input" type="number" id="socks5SetPort" min="1" max="65535">
            <div class="form-hint">{{ _('socks5_port_change_hint') }}</div>
        </div>
        <div class="form-group">
            <label class="form-label">{{ _('socks5_username') }}</label>
            <input class="form-input" type="text" id="socks5SetUser">
        </div>
        <div class="form-group">
            <label class="form-label">{{ _('socks5_password') }}</label>
            <input class="form-input" type="text" id="socks5SetPass">
        </div>
        <div class="modal-footer">
            <button class="btn btn-secondary" onclick="closeModal('socks5SettingsModal')">{{ _('cancel') }}</button>
            <button class="btn btn-primary" onclick="saveSocks5Settings()" id="socks5SaveBtn">
                <span id="socks5SaveBtnText">{{ _('save') }}</span>
                <div class="spinner hidden" id="socks5SaveSpinner"></div>
            </button>
        </div>
    </div>
</div>

<!-- ===== Hysteria Settings Modal ===== -->
<div class="modal-backdrop" id="hysteriaSettingsModal">
    <div class="modal">
        <div class="modal-header">
            <h2 class="modal-title" id="hysteriaSettingsTitle">{{ _('hysteria_settings_title') }}</h2>
            <button class="modal-close" onclick="closeModal('hysteriaSettingsModal')">×</button>
        </div>
        <input type="hidden" id="hysteriaSetProto" value="hysteria" />
        <div class="form-hint" style="margin-bottom: var(--space-md); padding: var(--space-sm) var(--space-md); border-radius: var(--radius-sm); background: rgba(234,179,8,0.12); border: 1px solid rgba(234,179,8,0.35);">
            {{ _('hysteria_ports_warning') }}
        </div>
        <div class="form-group">
            <label class="form-label">{{ _('hysteria_domain') }}</label>
            <input class="form-input" type="text" id="hysteriaSetDomain" placeholder="vpn.example.com">
            <div class="form-hint">{{ _('hysteria_ssl_change_hint') }}</div>
        </div>
        <div class="form-group">
            <label class="form-label">{{ _('hysteria_email') }}</label>
            <input class="form-input" type="email" id="hysteriaSetEmail" placeholder="admin@example.com">
        </div>
        <div class="form-group">
            <label class="form-label">{{ _('port') }} (UDP)</label>
            <input class="form-input" type="number" id="hysteriaSetPort" min="1" max="65535" value="8998">
            <div class="form-hint">{{ _('hysteria_port_change_hint') }}</div>
        </div>
        <div class="modal-footer" style="flex-wrap: wrap; gap: var(--space-sm);">
            <button class="btn btn-secondary" onclick="closeModal('hysteriaSettingsModal')">{{ _('cancel') }}</button>
            <button class="btn btn-secondary" onclick="renewHysteriaSsl()" id="hysteriaRenewBtn">
                <span id="hysteriaRenewBtnText">{{ _('hysteria_renew_ssl') }}</span>
                <div class="spinner hidden" id="hysteriaRenewSpinner"></div>
            </button>
            <button class="btn btn-primary" onclick="saveHysteriaSettings()" id="hysteriaSaveBtn">
                <span id="hysteriaSaveBtnText">{{ _('save') }}</span>
                <div class="spinner hidden" id="hysteriaSaveSpinner"></div>
            </button>
        </div>
    </div>
</div>

<!-- ===== NGINX Site Modal ===== -->
<div class="modal-backdrop" id="siteConfigModal">
    <div class="modal" style="max-width:680px;">
        <div class="modal-header">
            <h2 class="modal-title" id="siteConfigModalTitle">{{ _('site_editor') }}</h2>
            <button class="modal-close" onclick="closeModal('siteConfigModal')">×</button>
        </div>
        <div class="config-display" style="margin-top:var(--space-md);">
            <input type="hidden" id="siteConfigProto" value="nginx" />
            <div class="config-editor-wrap">
                <textarea class="form-input config-editor-fallback" id="siteConfigText"></textarea>
            </div>
            <div class="config-editor-hint">HTML editor • Ctrl+S / Cmd+S to save</div>
            <div class="config-actions" style="margin-top:var(--space-sm);">
                <button class="btn btn-secondary btn-sm"
                    onclick="copyToClipboard(getEditorValue('siteConfigText'))" style="flex:1">
                    {{ _('copy') }}
                </button>
                <button class="btn btn-secondary btn-sm"
                    onclick="downloadFile(getEditorValue('siteConfigText'), 'index.html')"
                    style="flex:1">
                    {{ _('download') }}
                </button>
                <button class="btn btn-primary btn-sm" onclick="saveNginxSite()" style="flex:1"
                    id="saveSiteConfigBtn">
                    {{ _('save') }}
                </button>
            </div>
        </div>
    </div>
</div>

<!-- ===== Protocol Backup Modal ===== -->
<div class="modal-backdrop" id="backupModal">
    <div class="modal" style="max-width:720px;">
        <div class="modal-header">
            <h2 class="modal-title" id="backupModalTitle">{{ _('backups') }}</h2>
            <button class="modal-close" onclick="closeModal('backupModal')">×</button>
        </div>
        <input type="hidden" id="backupProto" value="" />
        <div class="form-hint" style="margin-bottom:var(--space-md);">{{ _('backup_desc') }}</div>
        <div class="config-actions" style="margin-bottom:var(--space-md); flex-wrap:wrap;">
            <button class="btn btn-primary btn-sm" onclick="createProtocolBackup()" id="createBackupBtn" style="flex:1">
                {{ _('create_backup') }}
            </button>
            <button class="btn btn-secondary btn-sm hidden" onclick="exportProtocolClients()" id="exportClientsBtn" style="flex:1">
                {{ _('export_client_configs') }}
            </button>
            <button class="btn btn-secondary btn-sm" onclick="loadProtocolBackups()">{{ _('refresh') }}</button>
        </div>
        <div class="form-hint hidden" id="exportClientsHint" style="margin-bottom:var(--space-md);">{{ _('export_client_configs_hint') }}</div>
        <div id="backupBusy" class="hidden" style="display:none; align-items:center; gap:var(--space-sm); margin-bottom:var(--space-md); padding:var(--space-md); border-radius:12px; background:var(--accent-glow); border:1px solid color-mix(in srgb, var(--accent) 35%, transparent);">
            <div class="spinner" style="width:18px;height:18px;border-width:2px;"></div>
            <span id="backupBusyText">{{ _('restoring_protocol_backup') }}</span>
        </div>
        <div id="backupLoading" class="form-hint hidden">{{ _('loading') }}</div>
        <div id="backupEmpty" class="empty-state hidden" style="padding:var(--space-xl);">
            <div class="empty-icon">{{ icon('package') }}</div>
            <div class="empty-title">{{ _('no_backups') }}</div>
            <div class="empty-desc">{{ _('no_backups_desc') }}</div>
        </div>
        <div id="backupList" style="display:flex; flex-direction:column; gap:var(--space-sm);"></div>
    </div>
</div>


<!-- ===== Add Connection Modal ===== -->
<div class="modal-backdrop" id="addConnectionModal">
    <div class="modal">
        <div class="modal-header">
            <h2 class="modal-title" id="addConnectionModalTitle">{{ _('add_connection') }}</h2>
            <button class="modal-close" onclick="closeModal('addConnectionModal')">×</button>
        </div>

        <div class="form-group" id="connectionNameGroup">
            <label class="form-label">{{ _('connection_name') }}</label>
            <input class="form-input" type="text" id="connectionName"
                placeholder="{{ _('connection_name_placeholder') }}">
        </div>

        <div class="form-group">
            <label class="form-label">{{ _('assign_to_user') }}</label>
            <select class="form-select" id="connectionUserId">
                <option value="">{{ _('no_assign') }}</option>
                {% for u in users %}
                <option value="{{ u.id }}">{{ u.username }} ({{ u.role }})</option>
                {% endfor %}
            </select>
        </div>

        <div id="telemtConnOptions"
            style="display:none; padding: var(--space-md); background: rgba(0,0,0,0.03); border-radius: var(--radius-md); margin-bottom: var(--space-md);">
            <div class="form-group">
                <label class="form-label">Secret (Hex 32 chars)</label>
                <input class="form-input" type="text" id="telemtSecret" placeholder="Optional (auto-generated if empty)">
            </div>
            <div class="form-group">
                <label class="form-label">AD Tag (Hex 32 chars)</label>
                <input class="form-input" type="text" id="telemtAdTag" placeholder="Optional">
            </div>
            <div class="form-group">
                <label class="form-label">Max TCP Conns</label>
                <input class="form-input" type="number" id="telemtMaxConns" placeholder="Optional (e.g. 1000)" min="0">
            </div>
            <div class="form-group">
                <label class="form-label">Quota (Bytes)</label>
                <input class="form-input" type="number" id="telemtQuota" placeholder="Optional (e.g. 1073741824)"
                    min="0">
            </div>
            <div class="form-group">
                <label class="form-label">Max Unique IPs</label>
                <input class="form-input" type="number" id="telemtMaxIps" placeholder="Optional (e.g. 16)" min="1">
            </div>
            <div class="form-group">
                <label class="form-label">Expiration Date (ISO 8601)</label>
                <input class="form-input" type="text" id="telemtExpiry" placeholder="e.g. 2026-12-31T23:59:59Z">
            </div>
        </div>

        <div class="modal-footer">
            <button class="btn btn-secondary" onclick="closeModal('addConnectionModal')">{{ _('cancel') }}</button>
            <button class="btn btn-primary" onclick="addConnection()" id="addConnBtn">
                <span id="addConnBtnText">{{ _('create') }}</span>
                <div class="spinner hidden" id="addConnSpinner"></div>
            </button>
        </div>
    </div>
</div>

<!-- ===== Move Connections Modal ===== -->
<div class="modal-backdrop" id="moveConnectionsModal">
    <div class="modal" style="max-width: 520px;">
        <div class="modal-header">
            <h2 class="modal-title">{{ _('move_connections_title') }}</h2>
            <button class="modal-close" onclick="closeModal('moveConnectionsModal')">×</button>
        </div>
        <p class="form-hint">{{ _('move_connections_hint') }}</p>
        <div class="form-group">
            <label class="form-label" for="moveTargetServer">{{ _('move_connections_target') }}</label>
            <select class="form-select" id="moveTargetServer"></select>
        </div>
        <label class="form-check" style="display:flex; align-items:center; gap:var(--space-sm); margin-bottom: var(--space-md);">
            <input type="checkbox" id="moveDeleteSource" checked>
            <span>{{ _('move_connections_delete_source') }}</span>
        </label>
        <div class="form-hint" style="border-left: 3px solid var(--warning); padding-left: var(--space-sm);">
            {{ _('move_connections_warning') }}
        </div>
        <div class="modal-footer">
            <button type="button" class="btn btn-secondary" onclick="closeModal('moveConnectionsModal')">{{ _('cancel') }}</button>
            <button type="button" class="btn btn-primary" onclick="moveSelectedConnections()" id="moveConnectionsSubmitBtn">
                <span id="moveConnectionsSubmitText">{{ _('move_connections') }}</span>
                <div class="spinner hidden" id="moveConnectionsSpinner" style="width:14px; height:14px;"></div>
            </button>
        </div>
    </div>
</div>

<!-- ===== Connection Config Modal (with Tabs) ===== -->
<div class="modal-backdrop" id="configModal">
    <div class="modal" style="max-width: 600px;">
        <div class="modal-header">
            <h2 class="modal-title" id="configModalTitle">{{ _('config') }}</h2>
            <button class="modal-close" onclick="closeModal('configModal')">×</button>
        </div>

        <!-- Tabs -->
        <div class="config-tabs">
            <button class="config-tab active" onclick="switchConfigTab('conf')">{{ _('config_tab') }}</button>
            <button class="config-tab" onclick="switchConfigTab('vpn')">{{ _('vpn_key_tab') }}</button>
            <button class="config-tab" onclick="switchConfigTab('qr')">{{ _('qr_code_tab') }}</button>
        </div>

        <!-- Config Panel -->
        <div class="config-panel active" id="panel-conf">
            <div class="config-display">
                <div class="config-text" id="configText"></div>
                <div class="form-hint hidden" id="configClientHint" style="margin-top: var(--space-sm);"></div>
                <div class="config-actions">
                    <button class="btn btn-secondary btn-sm" onclick="copyToClipboard(currentConfig)" style="flex:1">
                        {{ _('copy') }}
                    </button>
                    <button class="btn btn-primary btn-sm"
                        onclick="downloadFile(currentConfig, currentConfigName + '.conf')" style="flex:1">
                        {{ _('download_conf') }}
                    </button>
                </div>
            </div>
        </div>

        <!-- VPN Key Panel -->
        <div class="config-panel" id="panel-vpn">
            <div class="vpn-link-box" id="vpnLinkText"></div>
            <div class="config-actions" style="margin-top: var(--space-sm);">
                <button class="btn btn-secondary btn-sm" onclick="copyToClipboard(currentVpnLink)" style="flex:1">
                    {{ _('copy_key') }}
                </button>
            </div>
            <div class="form-hint" style="margin-top: var(--space-sm);">
                {{ _('vpn_link_hint') }}
            </div>
        </div>

        <!-- QR Code Panel -->
        <div class="config-panel" id="panel-qr">
            <div class="qr-container">
                <div id="qrCode"></div>
                <div class="qr-hint">{{ _('qr_hint') }}</div>
            </div>
        </div>
    </div>
</div>
<!-- ===== Server Config Modal ===== -->
<div class="modal-backdrop" id="serverConfigModal">
    <div class="modal" style="max-width:680px;">
        <div class="modal-header">
            <h2 class="modal-title" id="serverConfigModalTitle">{{ _('server_config') }}</h2>
            <button class="modal-close" onclick="closeModal('serverConfigModal')">×</button>
        </div>
        <div class="config-display" style="margin-top:var(--space-md);">
            <!-- Hidden input to store current proto -->
            <input type="hidden" id="serverConfigProto" value="" />
            <div class="config-editor-wrap">
                <textarea class="form-input config-editor-fallback" id="serverConfigText"></textarea>
            </div>
            <div class="config-editor-hint">Config editor • Ctrl+S / Cmd+S to save</div>
            <div class="config-actions" style="margin-top:var(--space-sm);">
                <button class="btn btn-secondary btn-sm"
                    onclick="copyToClipboard(getEditorValue('serverConfigText'))" style="flex:1">
                    {{ _('copy') }}
                </button>
                <button class="btn btn-secondary btn-sm"
                    onclick="downloadFile(getEditorValue('serverConfigText'), serverConfigFilename)"
                    style="flex:1">
                    {{ _('download') }}
                </button>
                <button class="btn btn-primary btn-sm" onclick="saveServerConfig()" style="flex:1"
                    id="saveServerConfigBtn">
                    {{ _('save') }}
                </button>
            </div>
        </div>
    </div>
</div>

<!-- ===== Container Logs Modal (live) ===== -->
<div class="modal-backdrop" id="containerLogsModal">
    <div class="modal" style="max-width:820px;">
        <div class="modal-header">
            <h2 class="modal-title" id="containerLogsModalTitle">{{ _('container_logs') }}</h2>
            <button class="modal-close" onclick="closeContainerLogsModal()">×</button>
        </div>
        <input type="hidden" id="containerLogsProto" value="" />
        <div id="containerLogsMeta" class="form-hint" style="margin-bottom:var(--space-sm);"></div>
        <div class="log-output" id="containerLogsOutput"
            style="min-height:280px; max-height:55vh; font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; font-size:0.78rem; white-space:pre-wrap; word-break:break-word;"></div>
        <div class="modal-footer" style="display:flex; gap:var(--space-sm); flex-wrap:wrap;">
            <label style="display:flex; align-items:center; gap:6px; margin-right:auto; cursor:pointer;">
                <input type="checkbox" id="containerLogsLive" checked onchange="toggleContainerLogsLive()">
                <span>{{ _('logs_live') }}</span>
            </label>
            <button class="btn btn-secondary btn-sm" onclick="refreshContainerLogs()">{{ _('refresh') }}</button>
            <button class="btn btn-secondary btn-sm" onclick="copyToClipboard(document.getElementById('containerLogsOutput').textContent)">{{ _('copy') }}</button>
            <button class="btn btn-primary btn-sm" onclick="closeContainerLogsModal()">{{ _('close') }}</button>
        </div>
    </div>
</div>
{% endblock %}

{% block scripts %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/mode/nginx/nginx.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/mode/xml/xml.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/mode/javascript/javascript.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/mode/css/css.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/mode/properties/properties.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/mode/htmlmixed/htmlmixed.min.js"></script>
<script>
    const SERVER_ID = {{ server_id }};
    const ALL_SERVERS = {{ servers_for_move | tojson }};
    const SERVER_HOST = "{{ server.host }}";
    const SERVER_SSL_DOMAIN = {{ ((server.server_info or {}).get('ssl_domain') or '') | tojson }};
    const SERVER_SSL_EMAIL = {{ ((server.server_info or {}).get('ssl_email') or '') | tojson }};
    const MARKETPLACE_APPS = [
        { proto: 'awg2', category: 'protocols', icon: 'sparkles', title: 'AmneziaWG 2.0', descKey: 'awg_desc', badge: 'NEW' },
        { proto: 'awg', category: 'protocols', icon: 'shield', title: 'AmneziaWG', descKey: 'awg_desc' },
        { proto: 'awg_legacy', category: 'protocols', icon: 'radio', title: 'AmneziaWG Legacy', descKey: 'awg_legacy_desc' },
        { proto: 'xray', category: 'protocols', icon: 'zap', title: 'Xray (VLESS-Reality)', descKey: 'xray_desc' },
        { proto: 'telemt', category: 'protocols', icon: 'plane', title: 'Telemt (Telegram Proxy)', descKey: 'telemt_desc' },
        { proto: 'hysteria', category: 'protocols', icon: 'refresh', title: 'Hysteria 2', descKey: 'hysteria_desc' },
        { proto: 'naiveproxy', category: 'protocols', icon: 'link', title: 'NaiveProxy', descKey: 'naiveproxy_desc', badge: 'STABLE' },
        { proto: 'wireguard', category: 'protocols', icon: 'lock', title: 'WireGuard', descKey: 'wireguard_desc' },
        { proto: 'dns', category: 'services', icon: 'search', title: 'AmneziaDNS', descKey: 'dns_desc' },
        { proto: 'adguard', category: 'services', icon: 'shield-check', title: 'AdGuard Home', descKey: 'adguard_desc' },
        { proto: 'socks5', category: 'services', icon: 'network', title: 'SOCKS5 Proxy', descKey: 'socks5_desc' },
        { proto: 'nginx', category: 'web_servers', icon: 'globe', title: 'NGINX', descKey: 'nginx_desc' },
    ];
    const MARKETPLACE_CATEGORIES = [
        { id: 'protocols', icon: 'lock', titleKey: 'protocols' },
        { id: 'services', icon: 'wrench', titleKey: 'services' },
        { id: 'web_servers', icon: 'globe', titleKey: 'web_servers' },
    ];
    let currentInstallProto = 'awg';
    let currentInstallAnother = false;
    let currentConfig = '';
    let currentVpnLink = '';
    let currentConfigName = 'connection';
    let installedProtocols = {};
    let currentProtocolStatus = {};
    let installedAppsLoading = true;
    let currentSocks5Proto = 'socks5';

    function protoBase(proto) {
        return String(proto || 'awg').split('__')[0];
    }

    function protoInstance(proto) {
        const parts = String(proto || '').split('__');
        return parts.length > 1 ? (parseInt(parts[1], 10) || 1) : 1;
    }

    function protoDomKey(proto) {
        return String(proto || '').replace(/_/g, '-');
    }

    function isMultiInstanceBase(proto) {
        return ['awg', 'awg2', 'awg_legacy', 'xray', 'telemt', 'hysteria', 'socks5'].includes(protoBase(proto));
    }

    function nextSuggestedPort(base, fallback) {
        const used = new Set();
        Object.entries(currentProtocolStatus || {}).forEach(([proto, info]) => {
            if (protoBase(proto) === protoBase(base) && info && info.port) {
                const p = parseInt(info.port, 10);
                if (!Number.isNaN(p)) used.add(p);
            }
        });
        let port = parseInt(fallback, 10) || 55424;
        while (used.has(port)) port += 1;
        return String(port);
    }

    function orderedProtocolEntries(protocols) {
        const entries = Object.entries(protocols || {});
        const order = new Map(MARKETPLACE_APPS.map((app, idx) => [app.proto, idx]));
        entries.sort(([a], [b]) => {
            const ao = order.has(protoBase(a)) ? order.get(protoBase(a)) : 999;
            const bo = order.has(protoBase(b)) ? order.get(protoBase(b)) : 999;
            if (ao !== bo) return ao - bo;
            const ab = protoBase(a).localeCompare(protoBase(b));
            if (ab !== 0) return ab;
            return protoInstance(a) - protoInstance(b);
        });
        return entries;
    }

    function baseInstalled(proto) {
        const base = protoBase(proto);
        return Object.keys(installedProtocols).some(key => protoBase(key) === base && installedProtocols[key]);
    }

    function switchCategory(cat) {
        if (cat === 'management') {
            openManagementModal();
            return;
        }
        applyInstalledAppsVisibility();
    }

    function renderManagementModal() {
        const el = document.getElementById('managementContent');
        if (!el) return;
        el.innerHTML = `
            <div class="flex" style="flex-direction: column; gap: var(--space-sm);">
                <button class="btn btn-secondary" onclick="closeModal('managementModal'); checkServer();" style="justify-content: flex-start; padding: 12px 20px;">
                    <span style="font-size: 1.2rem; margin-right: 8px; display:inline-flex;">${uiIcon('refresh')}</span> ${_('check_server_services')}
                </button>
                <button class="btn btn-warning" onclick="closeModal('managementModal'); rebootServer();" style="justify-content: flex-start; padding: 12px 20px;">
                    <span style="font-size: 1.2rem; margin-right: 8px; display:inline-flex;">${uiIcon('refresh')}</span> ${_('reboot_server')}
                </button>
                <button class="btn btn-danger" onclick="closeModal('managementModal'); clearServer();" style="justify-content: flex-start; padding: 12px 20px;">
                    <span style="font-size: 1.2rem; margin-right: 8px; display:inline-flex;">${uiIcon('trash')}</span> ${_('clear_server')}
                </button>
                <button class="btn btn-danger" onclick="closeModal('managementModal'); deleteServer();" style="justify-content: flex-start; padding: 12px 20px;">
                    <span style="font-size: 1.2rem; margin-right: 8px; display:inline-flex;">${uiIcon('trash')}</span> ${_('remove_server')}
                </button>
            </div>
        `;
    }

    function openManagementModal() {
        renderManagementModal();
        openModal('managementModal');
    }

    function isAppInstalled(info) {
        return !!(info && (info.container_running || info.container_exists));
    }

    function marketplaceAppStatus(proto) {
        const installed = baseInstalled(proto);
        if (installed) {
            return `<span class="badge badge-success"><span class="badge-dot"></span> ${_('installed')}</span>`;
        }
        return `<span class="badge badge-warn"><span class="badge-dot"></span> ${_('not_installed')}</span>`;
    }

    function renderMarketplace() {
        const el = document.getElementById('marketplaceContent');
        if (!el) return;
        el.innerHTML = MARKETPLACE_CATEGORIES.map(cat => {
            const apps = MARKETPLACE_APPS.filter(app => app.category === cat.id);
            return `
                <div class="marketplace-category">
                    <div class="marketplace-category-title"><span>${uiIcon(cat.icon)}</span> ${_(cat.titleKey)}</div>
                    <div class="marketplace-grid">
                        ${apps.map(app => {
                            const installed = baseInstalled(app.proto);
                            const canInstallAnother = installed && isMultiInstanceBase(app.proto);
                            return `
                                <div class="marketplace-app-card">
                                    <div class="marketplace-app-top">
                                        <div class="flex items-center gap-sm">
                                            <div class="protocol-icon" style="width:38px;height:38px;font-size:1.2rem;">${uiIcon(app.icon)}</div>
                                            <div>
                                                <div class="marketplace-app-title">${app.title} ${app.badge ? `<span class="badge badge-info" style="font-size:0.65rem;">${app.badge}</span>` : ''}</div>
                                                ${marketplaceAppStatus(app.proto)}
                                            </div>
                                        </div>
                                    </div>
                                    <div class="marketplace-app-desc">${_(app.descKey)}</div>
                                    <button class="btn ${installed && !canInstallAnother ? 'btn-secondary' : 'btn-primary'} btn-sm" ${installed && !canInstallAnother ? 'disabled' : `onclick="installFromMarketplace('${app.proto}', ${canInstallAnother ? 'true' : 'false'})"`}>
                                        ${canInstallAnother ? _('install_another') : (installed ? _('installed') : _('install'))}
                                    </button>
                                </div>
                            `;
                        }).join('')}
                    </div>
                </div>
            `;
        }).join('');
    }

    function openMarketplaceModal() {
        renderMarketplace();
        openModal('marketplaceModal');
    }

    function installFromMarketplace(proto, installAnother = false) {
        closeModal('marketplaceModal');
        openInstallModal(proto, installAnother);
    }

    function setInstalledAppsLoading(isLoading) {
        installedAppsLoading = !!isLoading;
        const loading = document.getElementById('installedAppsLoading');
        const empty = document.getElementById('installedAppsEmpty');
        if (loading) loading.classList.toggle('hidden', !installedAppsLoading);
        if (empty && installedAppsLoading) empty.classList.add('hidden');
    }

    function applyInstalledAppsVisibility() {
        let installedCount = 0;
        const grid = document.getElementById('protocols-grid');
        const order = new Map(MARKETPLACE_APPS.map((app, idx) => [app.proto, idx]));

        Object.entries(currentProtocolStatus || {}).forEach(([proto, info]) => {
            if (isAppInstalled(info)) installedCount++;
        });

        // Hide every static card first. This prevents stale service cards
        // (SOCKS5/DNS/AdGuard/etc.) from staying visible when they are not
        // reported as installed by the backend.
        document.querySelectorAll('.protocol-card[id^="proto-"]').forEach(card => {
            card.classList.add('installed-app-hidden');
            card.classList.remove('installed-app-visible');
        });

        MARKETPLACE_APPS.forEach(app => {
            const card = document.getElementById(`proto-${protoDomKey(app.proto)}`);
            const installed = baseInstalled(app.proto);
            if (card) {
                card.classList.toggle('installed-app-hidden', !installed);
                card.classList.toggle('installed-app-visible', installed);
            }
        });

        orderedProtocolEntries(currentProtocolStatus || {}).forEach(([proto, info]) => {
            if (protoInstance(proto) > 1) {
                ensureProtocolCard(proto);
                const card = document.getElementById(`proto-${protoDomKey(proto)}`);
                const installed = isAppInstalled(info);
                if (card) {
                    card.classList.toggle('installed-app-hidden', !installed);
                    card.classList.toggle('installed-app-visible', installed);
                }
            }
        });

        // Keep all installed cards in one compact ordered grid. Without this,
        // services that were originally in another section (NGINX/SOCKS5/DNS/
        // AdGuard) appear with a visual offset below VPN protocols.
        if (grid) {
            Array.from(document.querySelectorAll('.protocol-card.installed-app-visible[id^="proto-"]'))
                .sort((a, b) => {
                    const ap = (a.id || '').replace(/^proto-/, '').replace(/-/g, '_');
                    const bp = (b.id || '').replace(/^proto-/, '').replace(/-/g, '_');
                    const ao = order.has(protoBase(ap)) ? order.get(protoBase(ap)) : 999;
                    const bo = order.has(protoBase(bp)) ? order.get(protoBase(bp)) : 999;
                    if (ao !== bo) return ao - bo;
                    return protoInstance(ap) - protoInstance(bp);
                })
                .forEach(card => grid.appendChild(card));
        }

        const empty = document.getElementById('installedAppsEmpty');
        if (empty) empty.classList.toggle('hidden', installedAppsLoading || installedCount > 0);
        const loading = document.getElementById('installedAppsLoading');
        if (loading) loading.classList.toggle('hidden', !installedAppsLoading);
    }

    function getProtoTitle(proto) {
        const base = protoBase(proto);
        const idx = protoInstance(proto);
        let title;
        switch (base) {
            case 'awg': title = 'AmneziaWG'; break;
            case 'awg2': title = 'AmneziaWG 2.0'; break;
            case 'awg_legacy': title = 'AmneziaWG Legacy'; break;
            case 'xray': title = 'Xray'; break;
            case 'telemt': title = 'Telemt'; break;
            case 'hysteria': title = 'Hysteria 2'; break;
            case 'naiveproxy': title = 'NaiveProxy'; break;
            case 'wireguard': title = 'WireGuard'; break;
            case 'dns': title = 'AmneziaDNS'; break;
            case 'socks5': title = 'SOCKS5 Proxy'; break;
            case 'adguard': title = 'AdGuard Home'; break;
            case 'nginx': title = 'NGINX'; break;
            default: title = proto;
        }
        return idx > 1 ? `${title} #${idx}` : title;
    }

    // ========== Server Stats ==========
    async function loadServerStats() {
        try {
            const stats = await apiCall(`/api/servers/${SERVER_ID}/stats`, 'POST');
            const el = document.getElementById('serverStats');
            el.style.display = '';
            el.innerHTML = `
                <span class="stat-pill" title="CPU">${uiIcon('zap')} ${stats.cpu || 0}%</span>
                <span class="stat-pill" title="RAM: ${formatBytes(stats.ram_used)} / ${formatBytes(stats.ram_total)}">${uiIcon('brain')} ${stats.ram_percent || 0}%</span>
                <span class="stat-pill" title="Disk: ${formatBytes(stats.disk_used)} / ${formatBytes(stats.disk_total)}">${uiIcon('package')} ${stats.disk_percent || 0}%</span>
                <span class="stat-pill" title="Network">${uiIcon('globe')} ↓${formatBytes(stats.net_rx)} ↑${formatBytes(stats.net_tx)}</span>
            `;
        } catch (err) {
            console.warn('Stats load failed:', err);
        }
    }

    // ========== Server Check ==========
    async function checkServer() {
        setInstalledAppsLoading(true);
        const btn = document.getElementById('checkBtn');
        const text = document.getElementById('checkBtnText');
        const spinner = document.getElementById('checkSpinner');
        btn.disabled = true;
        text.textContent = _('server_checking');
        spinner.classList.remove('hidden');
        try {
            const data = await apiCall(`/api/servers/${SERVER_ID}/check`, 'POST');
            const statusDiv = document.getElementById('serverStatus');
            const badges = document.getElementById('statusBadges');
            statusDiv.classList.remove('hidden');
            let html = '';
            if (data.connection === 'ok') {
                html += `<span class="badge badge-success"><span class="badge-dot"></span> ${_('ssh_ok')}</span>`;
            }
            if (data.docker_installed) {
                html += `<span class="badge badge-success"><span class="badge-dot"></span> ${_('docker_ok')}</span>`;
            } else {
                html += `<span class="badge badge-warn"><span class="badge-dot"></span> ${_('docker_not_installed')}</span>`;
            }
            badges.innerHTML = html;
            if (data.protocols) {
                const protoSelect = document.getElementById('connProtoSelect');
                const currentVal = protoSelect.value;
                protoSelect.innerHTML = '';
                let firstVal = null;

                for (const [proto, info] of orderedProtocolEntries(data.protocols)) {
                    updateProtocolCard(proto, info);
                    const isVPN = ['awg', 'awg2', 'awg_legacy', 'xray', 'telemt', 'hysteria', 'naiveproxy', 'wireguard'].includes(protoBase(proto));
                    if (info.container_running && isVPN) {
                        const opt = document.createElement('option');
                        opt.value = proto;
                        opt.textContent = getProtoTitle(proto);
                        protoSelect.appendChild(opt);
                        if (!firstVal) firstVal = proto;
                    }
                }

                if (protoSelect.innerHTML === '') {
                    const opt = document.createElement('option');
                    opt.textContent = _('no_protocols');
                    opt.disabled = true;
                    protoSelect.appendChild(opt);
                    hideConnectionsSection();
                } else {
                    if (data.protocols[currentVal] && data.protocols[currentVal].container_running) {
                        protoSelect.value = currentVal;
                    } else if (firstVal) {
                        protoSelect.value = firstVal;
                    }
                    loadConnections();
                }
            }
            updateStatusCounts(data.protocols || {});
            showToast(_('server_check_complete'), 'success');
        } catch (err) {
            showToast(_('error') + ': ' + err.message, 'error');
            const statusDiv = document.getElementById('serverStatus');
            const badges = document.getElementById('statusBadges');
            statusDiv.classList.remove('hidden');
            badges.innerHTML = `<span class="badge badge-danger"><span class="badge-dot"></span> ${_('server_connection_error')}</span>`;
        } finally {
            setInstalledAppsLoading(false);
            applyInstalledAppsVisibility();
            btn.disabled = false;
            text.textContent = _('server_check');
            spinner.classList.add('hidden');
        }
    }

    function updateStatusCounts(protocols) {
        currentProtocolStatus = protocols || {};
        setInstalledAppsLoading(false);
        installedProtocols = {};
        MARKETPLACE_APPS.forEach(app => {
            installedProtocols[app.proto] = isAppInstalled(currentProtocolStatus[app.proto]);
        });
        Object.entries(currentProtocolStatus || {}).forEach(([proto, info]) => {
            installedProtocols[proto] = isAppInstalled(info);
            const base = protoBase(proto);
            if (isAppInstalled(info)) installedProtocols[base] = true;
        });
        applyInstalledAppsVisibility();
        renderMarketplace();
    }

    let serverConfigFilename = 'server.conf';

    const configEditors = {};

    function editorModeFor(kind, proto) {
        if (kind === 'site') return 'htmlmixed';
        const base = protoBase(proto);
        if (base === 'nginx') return 'nginx';
        if (base === 'xray') return { name: 'javascript', json: true };
        return 'properties';
    }

    function ensureConfigEditor(textareaId, options = {}) {
        const textarea = document.getElementById(textareaId);
        if (!textarea) return null;
        if (!window.CodeMirror) return null;
        if (configEditors[textareaId]) return configEditors[textareaId];

        const editor = CodeMirror.fromTextArea(textarea, {
            mode: options.mode || 'nginx',
            theme: 'material-darker',
            lineNumbers: true,
            lineWrapping: false,
            indentUnit: 4,
            tabSize: 4,
            indentWithTabs: false,
            smartIndent: true,
            autofocus: false,
            extraKeys: {
                'Ctrl-S': function () {
                    if (textareaId === 'siteConfigText') saveNginxSite();
                    else saveServerConfig();
                },
                'Cmd-S': function () {
                    if (textareaId === 'siteConfigText') saveNginxSite();
                    else saveServerConfig();
                },
                'Tab': function (cm) {
                    if (cm.somethingSelected()) cm.indentSelection('add');
                    else cm.replaceSelection('    ', 'end');
                }
            }
        });
        configEditors[textareaId] = editor;
        return editor;
    }

    function setEditorValue(textareaId, value, options = {}) {
        const textarea = document.getElementById(textareaId);
        if (!textarea) return;
        textarea.value = value || '';
        const editor = ensureConfigEditor(textareaId, options);
        if (editor) {
            if (options.mode) editor.setOption('mode', options.mode);
            editor.setValue(value || '');
            setTimeout(() => editor.refresh(), 80);
        }
    }

    function getEditorValue(textareaId) {
        const editor = configEditors[textareaId];
        if (editor) return editor.getValue();
        const textarea = document.getElementById(textareaId);
        return textarea ? textarea.value : '';
    }

    function refreshConfigEditor(textareaId) {
        const editor = configEditors[textareaId];
        if (editor) setTimeout(() => editor.refresh(), 80);
    }

    // Services that don't have per-user "connections" — they expose a single
    // shared endpoint (DNS/proxy/admin UI) rather than per-client configs.
    const SERVICE_PROTOS = ['dns', 'socks5', 'adguard', 'nginx'];

    function buildServiceInfoGrid(proto, info) {
        // Custom info-grid markup for non-VPN services. Returns HTML for the grid body.
        const base = protoBase(proto);
        if (base === 'dns') {
            return `<div class="protocol-info-item"><span class="protocol-info-label">${_('port')}</span><span class="protocol-info-value">172.29.172.254:53 (Internal)</span></div>`;
        }
        if (base === 'socks5') {
            const user = info.username ? info.username : '—';
            return `
                <div class="protocol-info-item"><span class="protocol-info-label">${_('port')}</span><span class="protocol-info-value">${info.port}/TCP</span></div>
                <div class="protocol-info-item"><span class="protocol-info-label">${_('socks5_username')}</span><span class="protocol-info-value">${user}</span></div>
            `;
        }
        if (base === 'adguard') {
            const mode = info.mode === 'replace' ? _('adguard_mode_replace') : _('adguard_mode_sidebyside');
            const ip = info.internal_ip || '—';
            const webPort = info.web_port || 3000;
            const url = info.web_exposed
                ? `http://${SERVER_HOST}:${webPort}`
                : `http://${ip}:${webPort}`;
            return `
                <div class="protocol-info-item"><span class="protocol-info-label">${_('adguard_mode')}</span><span class="protocol-info-value">${mode}</span></div>
                <div class="protocol-info-item"><span class="protocol-info-label">${_('adguard_internal_ip')}</span><span class="protocol-info-value">${ip}</span></div>
                <div class="protocol-info-item"><span class="protocol-info-label">${_('adguard_web_ui')}</span><span class="protocol-info-value"><a href="${url}" target="_blank" rel="noopener">${url}</a></span></div>
            `;
        }
        if (base === 'nginx') {
            const domain = info.domain || '—';
            const port = info.port || 443;
            const url = info.site_url || (domain !== '—' ? (port == 443 ? `https://${domain}` : `https://${domain}:${port}`) : '—');
            return `
                <div class="protocol-info-item"><span class="protocol-info-label">${_('nginx_domain')}</span><span class="protocol-info-value">${domain}</span></div>
                <div class="protocol-info-item"><span class="protocol-info-label">${_('port')}</span><span class="protocol-info-value">${port}/TCP</span></div>
                <div class="protocol-info-item"><span class="protocol-info-label">${_('site')}</span><span class="protocol-info-value">${url !== '—' ? `<a href="${url}" target="_blank" rel="noopener">${url}</a>` : '—'}</span></div>
            `;
        }
        return '';
    }

    function replaceIdsRecursive(node, oldKey, newKey) {
        if (node.id) node.id = node.id.replace(oldKey, newKey);
        if (node.getAttribute && node.getAttribute('onclick')) {
            node.setAttribute('onclick', node.getAttribute('onclick').replace(new RegExp(oldKey.replace(/-/g, '[-_]'), 'g'), newKey));
        }
        Array.from(node.children || []).forEach(child => replaceIdsRecursive(child, oldKey, newKey));
    }

    function ensureProtocolCard(proto) {
        if (protoInstance(proto) <= 1) return;
        const key = protoDomKey(proto);
        if (document.getElementById(`proto-${key}`)) return;
        const base = protoBase(proto);
        const baseKey = protoDomKey(base);
        const baseCard = document.getElementById(`proto-${baseKey}`);
        if (!baseCard) return;
        const clone = baseCard.cloneNode(true);
        replaceIdsRecursive(clone, baseKey, key);
        clone.id = `proto-${key}`;
        const nameEl = clone.querySelector('.protocol-name');
        if (nameEl) nameEl.textContent = getProtoTitle(proto);
        const installBtn = clone.querySelector(`[id$="-install-btn"]`);
        if (installBtn) installBtn.remove();
        let insertAfter = baseCard;
        const sameBaseCards = Array.from(document.querySelectorAll('.protocol-card')).filter(card => {
            const cardProto = (card.id || '').replace(/^proto-/, '').replace(/-/g, '_').replace(/__(\d+)$/, '__$1');
            return card.id && card.id.startsWith(`proto-${baseKey}`);
        });
        sameBaseCards.forEach(card => { insertAfter = card; });
        insertAfter.insertAdjacentElement('afterend', clone);
    }

    function updateProtocolCard(proto, info) {
        ensureProtocolCard(proto);
        const protoKey = protoDomKey(proto);
        const statusEl = document.getElementById(`${protoKey}-status`);
        const infoEl = document.getElementById(`${protoKey}-info`);
        const infoGrid = document.getElementById(`${protoKey}-info-grid`);
        const actionsEl = document.getElementById(`${protoKey}-actions`);
        const ctrlEl = document.getElementById(`${protoKey}-ctrl`);

        // Some service cards may not exist in the template (e.g. on older builds);
        // skip quietly rather than throwing so a partial UI still renders.
        if (!statusEl || !actionsEl) return;

        const isService = SERVICE_PROTOS.includes(protoBase(proto));

        if (info.container_running) {
            installedProtocols[proto] = true;
            statusEl.innerHTML = `<span class="badge badge-success"><span class="badge-dot"></span> ${_('run')}</span>`;
            if (info.port || info.clients_count !== undefined || isService) {
                infoEl.classList.remove('hidden');
                let grid = '';
                if (isService) {
                    grid = buildServiceInfoGrid(proto, info);
                } else if (info.port) {
                    grid += `<div class="protocol-info-item"><span class="protocol-info-label">${_('port')}</span><span class="protocol-info-value">${info.port}/${(['xray', 'telemt', 'naiveproxy'].includes(protoBase(proto))) ? 'TCP' : 'UDP'}</span></div>`;
                    if (protoBase(proto) === 'hysteria' && info.domain) {
                        grid += `<div class="protocol-info-item"><span class="protocol-info-label">${_('hysteria_domain')}</span><span class="protocol-info-value">${info.domain}</span></div>`;
                    }
                    if (protoBase(proto) === 'naiveproxy' && info.domain) {
                        grid += `<div class="protocol-info-item"><span class="protocol-info-label">${_('naiveproxy_domain')}</span><span class="protocol-info-value">${info.domain}</span></div>`;
                    }
                }
                if (!isService && info.clients_count !== undefined) {
                    grid += `<div class="protocol-info-item"><span class="protocol-info-label">${_('connections')}</span><span class="protocol-info-value">${info.clients_count}</span></div>`;
                }
                infoGrid.innerHTML = grid;
            }
            if (protoBase(proto) === 'socks5') {
                actionsEl.innerHTML = `
                    <button class="btn btn-secondary btn-sm" onclick="openSocks5Settings('${proto}')" style="flex:1">${_('socks5_change_settings')}</button>
                    <button class="btn btn-secondary btn-sm" onclick="showProtocolBackups('${proto}')">${_('backup')}</button>
                    <button class="btn btn-danger btn-sm" onclick="uninstallProtocol('${proto}')">${_('uninstall')}</button>
                `;
            } else if (protoBase(proto) === 'adguard') {
                const webPort = info.web_port || 3000;
                const ip = info.internal_ip || '172.29.172.253';
                const url = info.web_exposed
                    ? `http://${SERVER_HOST}:${webPort}`
                    : `http://${ip}:${webPort}`;
                actionsEl.innerHTML = `
                    <a class="btn btn-secondary btn-sm" href="${url}" target="_blank" rel="noopener" style="flex:1">${_('adguard_open_ui')}</a>
                    <button class="btn btn-secondary btn-sm" onclick="showProtocolBackups('${proto}')">${_('backup')}</button>
                    <button class="btn btn-danger btn-sm" onclick="uninstallProtocol('${proto}')">${_('uninstall')}</button>
                `;
            } else if (protoBase(proto) === 'nginx') {
                actionsEl.innerHTML = `
                    <button class="btn btn-secondary btn-sm" onclick="showNginxSite('${proto}')" style="flex:1">${_('site')}</button>
                    <button class="btn btn-secondary btn-sm" onclick="showProtocolBackups('${proto}')">${_('backup')}</button>
                    <button class="btn btn-danger btn-sm" onclick="uninstallProtocol('${proto}')">${_('uninstall')}</button>
                `;
            } else if (protoBase(proto) === 'dns') {
                actionsEl.innerHTML = `
                    <button class="btn btn-secondary btn-sm" onclick="showProtocolBackups('${proto}')" style="flex:1">${_('backup')}</button>
                    <button class="btn btn-danger btn-sm" onclick="uninstallProtocol('${proto}')">${_('uninstall')}</button>
                `;
            } else if (protoBase(proto) === 'hysteria') {
                actionsEl.innerHTML = `
                    <button class="btn btn-secondary btn-sm" onclick="selectProtocolForConns('${proto}')" style="flex:1">${_('connections')}</button>
                    <button class="btn btn-secondary btn-sm" onclick="openHysteriaSettings('${proto}')">${_('hysteria_change_port')}</button>
                    <button class="btn btn-secondary btn-sm" onclick="showProtocolBackups('${proto}')">${_('backup')}</button>
                    <button class="btn btn-danger btn-sm" onclick="uninstallProtocol('${proto}')">${_('uninstall')}</button>
                `;
                showConnectionsSection();
            } else if (protoBase(proto) === 'naiveproxy') {
                actionsEl.innerHTML = `
                    <button class="btn btn-secondary btn-sm" onclick="selectProtocolForConns('${proto}')" style="flex:1">${_('connections')}</button>
                    <button class="btn btn-secondary btn-sm" onclick="showProtocolBackups('${proto}')">${_('backup')}</button>
                    <button class="btn btn-danger btn-sm" onclick="uninstallProtocol('${proto}')">${_('uninstall')}</button>
                `;
                showConnectionsSection();
            } else {
                actionsEl.innerHTML = `
                    <button class="btn btn-secondary btn-sm" onclick="selectProtocolForConns('${proto}')" style="flex:1">${_('connections')}</button>
                    <button class="btn btn-secondary btn-sm" onclick="showProtocolBackups('${proto}')">${_('backup')}</button>
                    <button class="btn btn-danger btn-sm" onclick="uninstallProtocol('${proto}')">${_('uninstall')}</button>
                `;
                showConnectionsSection();
            }
            // Show control buttons (stop + optional config). Services use the
            // container's own config UI (AdGuard web wizard / our SOCKS5 modal /
            // unbound's static config), so we don't expose a raw editor for them.
            if (ctrlEl) {
                ctrlEl.style.cssText = '';
                const logsBtn = `<button class="btn btn-secondary btn-sm" onclick="showContainerLogs('${proto}')" title="${_('container_logs')}">${_('logs_btn')}</button>`;
                if (protoBase(proto) === 'nginx') {
                    ctrlEl.innerHTML = `
                        ${logsBtn}
                        <button class="btn btn-secondary btn-sm" onclick="showServerConfig('${proto}')" title="${_('server_config')}">${_('config_btn')}</button>
                        <button class="btn btn-danger btn-sm" onclick="toggleContainer('${proto}', true)" title="${_('stop_container_confirm').replace('{}', '')}">${_('stop_btn')}</button>
                    `;
                } else if (isService) {
                    ctrlEl.innerHTML = `
                        ${logsBtn}
                        <button class="btn btn-danger btn-sm" onclick="toggleContainer('${proto}', true)" title="${_('stop_container_confirm').replace('{}', '')}">${_('stop_btn')}</button>
                    `;
                } else {
                    ctrlEl.innerHTML = `
                        ${logsBtn}
                        <button class="btn btn-secondary btn-sm" onclick="showServerConfig('${proto}')" title="${_('server_config')}">${_('config_btn')}</button>
                        <button class="btn btn-danger btn-sm" onclick="toggleContainer('${proto}', true)" title="${_('stop_container_confirm').replace('{}', '')}">${_('stop_btn')}</button>
                    `;
                }
            }
        } else if (info.container_exists) {
            installedProtocols[proto] = true;
            const errText = String(info.error || '').trim();
            const portBusy = !!info.port_busy || /address already in use|UDP port \d+ already in use|already in use/i.test(errText);
            statusEl.innerHTML = errText
                ? `<span class="badge badge-danger" style="max-width:100%; white-space:normal; text-align:left; line-height:1.35;"><span class="badge-dot"></span> ${_('stop')}: ${escapeHtml(errText.slice(0, 120))}</span>`
                : `<span class="badge badge-danger"><span class="badge-dot"></span> ${_('stop')}</span>`;
            if (infoEl && infoGrid) {
                infoEl.classList.remove('hidden');
                let grid = '';
                if (info.port) {
                    grid += `<div class="protocol-info-item"><span class="protocol-info-label">${_('port')}</span><span class="protocol-info-value">${info.port}/UDP</span></div>`;
                }
                if (info.container_status) {
                    grid += `<div class="protocol-info-item"><span class="protocol-info-label">${_('container_status')}</span><span class="protocol-info-value">${escapeHtml(String(info.container_status))}</span></div>`;
                }
                if (info.exit_code !== undefined && info.exit_code !== null) {
                    grid += `<div class="protocol-info-item"><span class="protocol-info-label">${_('exit_code')}</span><span class="protocol-info-value">${info.exit_code}</span></div>`;
                }
                if (errText) {
                    grid += `<div class="protocol-info-item" style="grid-column:1/-1;"><span class="protocol-info-label">${_('error')}</span><span class="protocol-info-value" style="color:var(--danger); word-break:break-word;">${escapeHtml(errText)}</span></div>`;
                }
                if (protoBase(proto) === 'hysteria') {
                    const curPort = parseInt(info.port, 10) || 443;
                    const suggest = (curPort === 443 || portBusy) ? 8998 : curPort;
                    grid += `
                        <div class="protocol-info-item" style="grid-column:1/-1; display:block; padding-top:8px;">
                            <span class="protocol-info-label" style="display:block; margin-bottom:6px;">${_('hysteria_change_port')} (UDP)</span>
                            <div style="display:flex; gap:8px; flex-wrap:wrap; align-items:center;">
                                <input class="form-input" type="number" id="hysteriaInlinePort-${protoDomKey(proto)}"
                                    value="${suggest}" min="1" max="65535" style="width:120px; flex:0 0 auto;">
                                <button class="btn btn-primary btn-sm" type="button"
                                    onclick="applyHysteriaPort('${proto}', document.getElementById('hysteriaInlinePort-${protoDomKey(proto)}').value)">
                                    ${_('save')} &amp; ${_('start_btn')}
                                </button>
                                <button class="btn btn-secondary btn-sm" type="button" onclick="openHysteriaSettings('${proto}')">${_('hysteria_settings_title')}</button>
                            </div>
                            <div class="form-hint" style="margin-top:6px;">${_('hysteria_port_change_hint')}</div>
                        </div>
                    `;
                }
                infoGrid.innerHTML = grid;
            }
            if (isService) {
                actionsEl.innerHTML = `
                    <button class="btn btn-primary btn-sm" onclick="openInstallModal('${proto}')" style="flex:1">${_('reinstall')}</button>
                    <button class="btn btn-secondary btn-sm" onclick="showContainerLogs('${proto}')">${_('logs_btn')}</button>
                    <button class="btn btn-secondary btn-sm" onclick="showProtocolBackups('${proto}')">${_('backup')}</button>
                    <button class="btn btn-danger btn-sm" onclick="uninstallProtocol('${proto}')">${_('uninstall')}</button>
                `;
            } else if (protoBase(proto) === 'hysteria') {
                actionsEl.innerHTML = `
                    <button class="btn btn-primary btn-sm" onclick="openHysteriaSettings('${proto}')" style="flex:1">${_('hysteria_change_port')}</button>
                    <button class="btn btn-secondary btn-sm" onclick="showContainerLogs('${proto}')">${_('logs_btn')}</button>
                    <button class="btn btn-secondary btn-sm" onclick="openInstallModal('${proto}')">${_('reinstall')}</button>
                    <button class="btn btn-danger btn-sm" onclick="uninstallProtocol('${proto}')">${_('uninstall')}</button>
                `;
            } else {
                actionsEl.innerHTML = `
                    <button class="btn btn-primary btn-sm" onclick="openInstallModal('${proto}')" style="flex:1">${_('reinstall')}</button>
                    <button class="btn btn-secondary btn-sm" onclick="showContainerLogs('${proto}')">${_('logs_btn')}</button>
                    <button class="btn btn-secondary btn-sm" onclick="showProtocolBackups('${proto}')">${_('backup')}</button>
                    <button class="btn btn-danger btn-sm" onclick="uninstallProtocol('${proto}')">${_('uninstall')}</button>
                `;
            }
            if (ctrlEl) {
                ctrlEl.style.cssText = '';
                const logsBtn = `<button class="btn btn-secondary btn-sm" onclick="showContainerLogs('${proto}')" title="${_('container_logs')}">${_('logs_btn')}</button>`;
                if (protoBase(proto) === 'nginx') {
                    ctrlEl.innerHTML = `
                        ${logsBtn}
                        <button class="btn btn-secondary btn-sm" onclick="showServerConfig('${proto}')" title="${_('server_config')}">${_('config_btn')}</button>
                        <button class="btn btn-primary btn-sm" onclick="toggleContainer('${proto}', false)" title="${_('start_container_confirm').replace('{}', '')}">${_('start_btn')}</button>
                    `;
                } else if (isService) {
                    ctrlEl.innerHTML = `
                        ${logsBtn}
                        <button class="btn btn-primary btn-sm" onclick="toggleContainer('${proto}', false)" title="${_('start_container_confirm').replace('{}', '')}">${_('start_btn')}</button>
                    `;
                } else {
                    ctrlEl.innerHTML = `
                        ${logsBtn}
                        <button class="btn btn-secondary btn-sm" onclick="showServerConfig('${proto}')" title="${_('server_config')}">${_('config_btn')}</button>
                        <button class="btn btn-primary btn-sm" onclick="toggleContainer('${proto}', false)" title="${_('start_container_confirm').replace('{}', '')}">${_('start_btn')}</button>
                    `;
                }
            }
        } else {
            installedProtocols[proto] = false;
            statusEl.innerHTML = `<span class="badge badge-warn"><span class="badge-dot"></span> ${_('not_installed')}</span>`;
            actionsEl.innerHTML = `
                <button class="btn btn-primary btn-sm" onclick="openInstallModal('${proto}')" style="flex:1">${_('install')}</button>
            `;
            if (ctrlEl) {
                ctrlEl.style.cssText = 'display:none!important;';
                ctrlEl.innerHTML = '';
            }
        }
    }

    // Toggle Docker container start/stop
    async function toggleContainer(proto, isRunning) {
        const name = getProtoTitle(proto);
        const action = isRunning ? _('stopping') : _('starting');
        if (!confirm(`${isRunning ? _('stop_container_confirm') : _('start_container_confirm')}`.replace('{}', name))) return;
        try {
            showToast(`${action} ${name}...`, 'info');
            const res = await apiCall(`/api/servers/${SERVER_ID}/container/toggle`, 'POST', { protocol: proto });
            showToast(`${name} ${res.action === 'stopped' ? _('stopped') : _('started')}`, 'success');
            setTimeout(() => checkServer(), 1500);
        } catch (err) {
            showToast(_('error') + ': ' + err.message, 'error');
            // Show live logs so admin can see why start failed
            showContainerLogs(proto);
            setTimeout(() => checkServer(), 800);
        }
    }

    // ===== Live container logs =====
    let containerLogsES = null;
    let containerLogsProto = '';

    function renderContainerLogsText(text, stateStr) {
        const out = document.getElementById('containerLogsOutput');
        const meta = document.getElementById('containerLogsMeta');
        if (!out) return;
        const atBottom = out.scrollHeight - out.scrollTop - out.clientHeight < 40;
        out.textContent = text || _('no_logs');
        if (atBottom) out.scrollTop = out.scrollHeight;
        if (meta) {
            const parts = String(stateStr || '').split('|');
            const status = parts[0] || '';
            const running = String(parts[1] || '').toLowerCase() === 'true';
            const exitCode = parts[2] || '';
            const err = parts[3] || '';
            let line = running
                ? `<span class="badge badge-success"><span class="badge-dot"></span> ${_('run')}</span>`
                : `<span class="badge badge-danger"><span class="badge-dot"></span> ${_('stop')}</span>`;
            if (status) line += ` · ${escapeHtml(status)}`;
            if (exitCode !== '' && exitCode !== '0') line += ` · ${_('exit_code')}: ${escapeHtml(exitCode)}`;
            if (err) line += ` · <span style="color:var(--danger)">${escapeHtml(err)}</span>`;
            meta.innerHTML = line;
        }
    }

    function stopContainerLogsStream() {
        if (containerLogsES) {
            try { containerLogsES.close(); } catch (_) {}
            containerLogsES = null;
        }
    }

    function startContainerLogsStream(proto) {
        stopContainerLogsStream();
        const live = document.getElementById('containerLogsLive');
        if (live && !live.checked) return;
        const url = `/api/servers/${SERVER_ID}/container/logs/stream?protocol=${encodeURIComponent(proto)}&tail=200`;
        containerLogsES = new EventSource(url);
        containerLogsES.onmessage = (ev) => {
            try {
                const data = JSON.parse(ev.data);
                if (data.error) {
                    renderContainerLogsText(data.error, '');
                    return;
                }
                renderContainerLogsText(data.logs || '', data.state || '');
            } catch (_) {}
        };
        containerLogsES.onerror = () => {
            // Browser will retry; show a soft hint once
            const meta = document.getElementById('containerLogsMeta');
            if (meta && !meta.dataset.streamErr) {
                meta.dataset.streamErr = '1';
                meta.innerHTML += ` · <span class="form-hint">${_('logs_reconnect')}</span>`;
            }
        };
    }

    async function refreshContainerLogs() {
        const proto = document.getElementById('containerLogsProto').value || containerLogsProto;
        if (!proto) return;
        try {
            const res = await apiCall(`/api/servers/${SERVER_ID}/container/logs`, 'POST', {
                protocol: proto, tail: 200,
            });
            const state = [
                res.container_status || '',
                res.running ? 'true' : 'false',
                res.exit_code != null ? String(res.exit_code) : '',
                res.error || '',
            ].join('|');
            renderContainerLogsText(res.logs || '', state);
        } catch (err) {
            renderContainerLogsText(_('error') + ': ' + err.message, '');
        }
    }

    function toggleContainerLogsLive() {
        const live = document.getElementById('containerLogsLive');
        if (live && live.checked) {
            startContainerLogsStream(containerLogsProto);
        } else {
            stopContainerLogsStream();
        }
    }

    async function showContainerLogs(proto) {
        containerLogsProto = proto;
        document.getElementById('containerLogsProto').value = proto;
        document.getElementById('containerLogsModalTitle').textContent =
            `${_('container_logs')} — ${getProtoTitle(proto)}`;
        document.getElementById('containerLogsOutput').textContent = _('loading');
        document.getElementById('containerLogsMeta').innerHTML = '';
        document.getElementById('containerLogsMeta').dataset.streamErr = '';
        const live = document.getElementById('containerLogsLive');
        if (live) live.checked = true;
        openModal('containerLogsModal');
        await refreshContainerLogs();
        startContainerLogsStream(proto);
    }

    function closeContainerLogsModal() {
        stopContainerLogsStream();
        closeModal('containerLogsModal');
    }

    // Stop live stream if modal is closed via backdrop / Escape
    document.addEventListener('click', (e) => {
        if (e.target && e.target.id === 'containerLogsModal') stopContainerLogsStream();
    });
    document.addEventListener('keydown', (e) => {
        if (e.key === 'Escape') stopContainerLogsStream();
    });

    // View server-side WireGuard/Xray config
    async function showServerConfig(proto) {
        const name = getProtoTitle(proto);
        try {
            showToast(_('loading'), 'info');
            const res = await apiCall(`/api/servers/${SERVER_ID}/server_config`, 'POST', { protocol: proto });
            document.getElementById('serverConfigModalTitle').textContent = `${_('server_config')} — ${name}`;
            document.getElementById('serverConfigProto').value = proto;
            setEditorValue('serverConfigText', res.config || '', { mode: editorModeFor('server', proto) });
            serverConfigFilename = `server_${proto}.conf`;
            openModal('serverConfigModal');
            refreshConfigEditor('serverConfigText');
        } catch (err) {
            showToast(_('error') + ': ' + err.message, 'error');
        }
    }

    // Save server config changes
    async function saveServerConfig() {
        const proto = document.getElementById('serverConfigProto').value;
        const newConfig = getEditorValue('serverConfigText');
        const btn = document.getElementById('saveServerConfigBtn');
        const oldText = btn.textContent;
        btn.disabled = true;
        btn.textContent = _('saving') || 'Saving...';

        try {
            await apiCall(`/api/servers/${SERVER_ID}/server_config/save`, 'POST', {
                protocol: proto,
                config: newConfig
            });
            showToast(_('success') || 'Saved successfully', 'success');
            closeModal('serverConfigModal');
        } catch (err) {
            showToast((_('error') || 'Error') + ': ' + err.message, 'error');
        } finally {
            btn.disabled = false;
            btn.textContent = oldText;
        }
    }


    async function showNginxSite(proto = 'nginx') {
        try {
            showToast(_('loading'), 'info');
            const res = await apiCall(`/api/servers/${SERVER_ID}/nginx/site`, 'POST', { protocol: proto });
            document.getElementById('siteConfigModalTitle').textContent = `${_('site_editor')} — ${getProtoTitle(proto)}`;
            document.getElementById('siteConfigProto').value = proto;
            setEditorValue('siteConfigText', res.html || '', { mode: editorModeFor('site', proto) });
            openModal('siteConfigModal');
            refreshConfigEditor('siteConfigText');
        } catch (err) {
            showToast(_('error') + ': ' + err.message, 'error');
        }
    }

    async function saveNginxSite() {
        const proto = document.getElementById('siteConfigProto').value || 'nginx';
        const html = getEditorValue('siteConfigText');
        const btn = document.getElementById('saveSiteConfigBtn');
        const oldText = btn.textContent;
        btn.disabled = true;
        btn.textContent = _('saving') || 'Saving...';
        try {
            await apiCall(`/api/servers/${SERVER_ID}/nginx/site/save`, 'POST', { protocol: proto, html });
            showToast(_('site_saved') || _('success'), 'success');
            closeModal('siteConfigModal');
        } catch (err) {
            showToast((_('error') || 'Error') + ': ' + err.message, 'error');
        } finally {
            btn.disabled = false;
            btn.textContent = oldText;
        }
    }


    function showConnectionsSection() {
        const section = document.getElementById('connectionsSection');
        if (section) section.style.display = 'block';
    }

    function hideConnectionsSection() {
        const section = document.getElementById('connectionsSection');
        if (section) section.style.display = 'none';
        const select = document.getElementById('connProtoSelect');
        if (select) {
            select.innerHTML = '';
            const opt = document.createElement('option');
            opt.textContent = _('no_protocols');
            opt.disabled = true;
            select.appendChild(opt);
        }
    }

    // ========== Protocol Installation ==========
    function updateNginxDnsHint() {
        const input = document.getElementById('installNginxDomain');
        const hint = document.getElementById('nginxDnsHint');
        if (!input || !hint) return;
        const domain = (input.value || '').trim() || 'example.com';
        hint.innerHTML = `${_('nginx_dns_hint')} <code>A&nbsp;&nbsp;${domain}&nbsp;&nbsp;${SERVER_HOST}</code>`;
    }

    function updateHysteriaDnsHint() {
        const input = document.getElementById('installHysteriaDomain');
        const hint = document.getElementById('hysteriaDnsHint');
        if (!input || !hint) return;
        const domain = (input.value || '').trim() || 'vpn.example.com';
        hint.innerHTML = `${_('hysteria_dns_hint')} <code>A&nbsp;&nbsp;${domain}&nbsp;&nbsp;${SERVER_HOST}</code>`;
    }

    function updateNaiveproxyDnsHint() {
        const input = document.getElementById('installNaiveproxyDomain');
        const hint = document.getElementById('naiveproxyDnsHint');
        if (!input || !hint) return;
        const domain = (input.value || '').trim() || 'vpn.example.com';
        hint.innerHTML = `${_('naiveproxy_dns_hint')} <code>A&nbsp;&nbsp;${domain}&nbsp;&nbsp;${SERVER_HOST}</code>`;
    }

    function openInstallModal(proto, installAnother = false) {
        const base = protoBase(proto);
        currentInstallProto = installAnother ? base : proto;
        currentInstallAnother = !!installAnother;
        const name = getProtoTitle(proto) + (currentInstallAnother ? ` #${_('new_instance')}` : '');
        document.getElementById('installModalTitle').textContent = `${_('install')} ${name}`;

        const portInput = document.getElementById('installPort');
        const portLabel = document.getElementById('installPortLabel');
        const portHint = document.getElementById('installPortHint');
        const portGroup = document.getElementById('installPortGroup');
        const telemtOpts = document.getElementById('telemtOptions');
        const socks5Opts = document.getElementById('socks5Options');
        const adguardOpts = document.getElementById('adguardOptions');
        const nginxOpts = document.getElementById('nginxOptions');
        const hysteriaOpts = document.getElementById('hysteriaOptions');
        const naiveproxyOpts = document.getElementById('naiveproxyOptions');

        telemtOpts.style.display = 'none';
        socks5Opts.style.display = 'none';
        adguardOpts.style.display = 'none';
        nginxOpts.style.display = 'none';
        hysteriaOpts.style.display = 'none';
        if (naiveproxyOpts) naiveproxyOpts.style.display = 'none';
        if (portGroup) portGroup.style.display = '';

        if (base === 'dns') {
            portLabel.textContent = _('port') + ' (Internal)';
            portInput.value = '53';
            portInput.disabled = true;
            portHint.textContent = _('dns_internal_hint');
        } else if (base === 'xray') {
            portLabel.textContent = _('port') + ' (TCP)';
            portInput.disabled = false;
            portInput.value = currentInstallAnother ? nextSuggestedPort(currentInstallProto, 443) : '443';
            portHint.textContent = currentInstallAnother ? _('port_next_instance_hint') : _('port_xray_hint');
        } else if (base === 'telemt') {
            portLabel.textContent = _('port') + ' (TCP)';
            portInput.value = currentInstallAnother ? nextSuggestedPort(currentInstallProto, 443) : '443';
            portInput.disabled = false;
            portHint.textContent = currentInstallAnother ? _('port_next_instance_hint') : _('telemt_port_hint');
            telemtOpts.style.display = 'block';
        } else if (base === 'socks5') {
            portLabel.textContent = _('port') + ' (TCP)';
            portInput.value = currentInstallAnother ? nextSuggestedPort(currentInstallProto, 38080) : '38080';
            portInput.disabled = false;
            portHint.textContent = currentInstallAnother ? _('port_next_instance_hint') : _('socks5_port_hint');
            socks5Opts.style.display = 'block';
        } else if (base === 'adguard') {
            portLabel.textContent = _('adguard_dns_port');
            portInput.value = '53';
            portInput.disabled = false;
            portHint.textContent = _('adguard_dns_port_hint');
            adguardOpts.style.display = 'block';
        } else if (base === 'nginx') {
            portLabel.textContent = _('port') + ' (HTTPS/TCP)';
            portInput.value = '443';
            portInput.disabled = false;
            portHint.textContent = _('nginx_port_hint');
            nginxOpts.style.display = 'block';
            const nxDomain = document.getElementById('installNginxDomain');
            const nxEmail = document.getElementById('installNginxEmail');
            if (nxDomain && !nxDomain.value && SERVER_SSL_DOMAIN) nxDomain.value = SERVER_SSL_DOMAIN;
            if (nxEmail && !nxEmail.value && SERVER_SSL_EMAIL) nxEmail.value = SERVER_SSL_EMAIL;
            updateNginxDnsHint();
        } else if (base === 'hysteria') {
            if (portGroup) portGroup.style.display = 'none';
            const suggested = currentInstallAnother ? nextSuggestedPort(currentInstallProto, 8998) : '8998';
            portInput.value = suggested;
            portInput.disabled = false;
            hysteriaOpts.style.display = 'block';
            const hyPort = document.getElementById('installHysteriaPort');
            if (hyPort) {
                hyPort.value = suggested;
                hyPort.oninput = () => { portInput.value = hyPort.value; };
            }
            const hyDomain = document.getElementById('installHysteriaDomain');
            const hyEmail = document.getElementById('installHysteriaEmail');
            if (hyDomain && !hyDomain.value && SERVER_SSL_DOMAIN) hyDomain.value = SERVER_SSL_DOMAIN;
            if (hyEmail && !hyEmail.value && SERVER_SSL_EMAIL) hyEmail.value = SERVER_SSL_EMAIL;
            updateHysteriaDnsHint();
        } else if (base === 'naiveproxy') {
            if (portGroup) portGroup.style.display = 'none';
            portInput.value = '443';
            portInput.disabled = false;
            if (naiveproxyOpts) naiveproxyOpts.style.display = 'block';
            const npDomain = document.getElementById('installNaiveproxyDomain');
            const npEmail = document.getElementById('installNaiveproxyEmail');
            if (npDomain && !npDomain.value && SERVER_SSL_DOMAIN) npDomain.value = SERVER_SSL_DOMAIN;
            if (npEmail && !npEmail.value && SERVER_SSL_EMAIL) npEmail.value = SERVER_SSL_EMAIL;
            updateNaiveproxyDnsHint();
        } else {
            portLabel.textContent = _('port') + ' (UDP)';
            portInput.value = currentInstallAnother ? nextSuggestedPort(currentInstallProto, 55424) : '55424';
            portInput.disabled = false;
            portHint.textContent = currentInstallAnother ? _('port_next_instance_hint') : _('port_default_hint');
        }

        document.getElementById('installForm').classList.remove('hidden');
        document.getElementById('installProgress').classList.add('hidden');
        document.getElementById('installDone').style.display = 'none';
        openModal('installModal');
    }

    async function installProtocol() {
        const port = document.getElementById('installPort').value;
        const btn = document.getElementById('installBtn');
        const text = document.getElementById('installBtnText');
        const spinner = document.getElementById('installSpinner');
        btn.disabled = true;
        text.textContent = _('installing');
        spinner.classList.remove('hidden');
        document.getElementById('installForm').classList.add('hidden');
        const progressDiv = document.getElementById('installProgress');
        progressDiv.classList.remove('hidden');
        const logEl = document.getElementById('installLog');
        const fillEl = document.getElementById('installProgressFill');
        logEl.innerHTML = `<div class="log-line info">${_('install_starting')}</div>`;
        fillEl.style.width = '10%';
        fillEl.style.background = '';
        try {
            const progressSteps = [
                { pct: 20, msg: _('check_docker') },
                { pct: 40, msg: _('prepare_host') },
                { pct: 50, msg: _('build_container') },
            ];
            let stepIdx = 0;
            const progressInterval = setInterval(() => {
                if (stepIdx < progressSteps.length) {
                    fillEl.style.width = progressSteps[stepIdx].pct + '%';
                    logEl.innerHTML += `<div class="log-line info">${progressSteps[stepIdx].msg}</div>`;
                    logEl.scrollTop = logEl.scrollHeight;
                    stepIdx++;
                }
            }, 2000);
            const params = {
                protocol: currentInstallProto, port: port, install_another: currentInstallAnother,
            };
            if (protoBase(currentInstallProto) === 'telemt') {
                params.tls_emulation = document.getElementById('installTelemtTls').checked;
                params.tls_domain = document.getElementById('installTelemtDomain').value;
                params.max_connections = parseInt(document.getElementById('installTelemtMaxConns').value) || 0;
            } else if (protoBase(currentInstallProto) === 'socks5') {
                params.socks5_username = document.getElementById('installSocks5User').value || 'proxy_user';
                params.socks5_password = document.getElementById('installSocks5Pass').value || '';
            } else if (protoBase(currentInstallProto) === 'adguard') {
                const modeRadio = document.querySelector('input[name="adguardMode"]:checked');
                params.adguard_mode = modeRadio ? modeRadio.value : 'sidebyside';
                params.adguard_web_port = parseInt(document.getElementById('installAdguardWebPort').value) || 3000;
                params.adguard_expose_web = document.getElementById('installAdguardExposeWeb').checked;
                params.adguard_dot_port = parseInt(document.getElementById('installAdguardDotPort').value) || 853;
                params.adguard_doh_port = parseInt(document.getElementById('installAdguardDohPort').value) || 443;
                params.adguard_expose_dot = document.getElementById('installAdguardExposeDot').checked;
                params.adguard_expose_doh = document.getElementById('installAdguardExposeDoh').checked;
            } else if (protoBase(currentInstallProto) === 'nginx') {
                params.nginx_domain = document.getElementById('installNginxDomain').value.trim();
                params.nginx_email = document.getElementById('installNginxEmail').value.trim();
            } else if (protoBase(currentInstallProto) === 'hysteria') {
                const hyPortEl = document.getElementById('installHysteriaPort');
                if (hyPortEl && hyPortEl.value) {
                    params.port = hyPortEl.value;
                }
                params.hysteria_domain = document.getElementById('installHysteriaDomain').value.trim();
                params.hysteria_email = document.getElementById('installHysteriaEmail').value.trim();
            } else if (protoBase(currentInstallProto) === 'naiveproxy') {
                params.port = '443';
                params.naiveproxy_domain = document.getElementById('installNaiveproxyDomain').value.trim();
                params.naiveproxy_email = document.getElementById('installNaiveproxyEmail').value.trim();
            }
            const result = await apiCall(`/api/servers/${SERVER_ID}/install`, 'POST', params);
            clearInterval(progressInterval);
            fillEl.style.width = '100%';
            if (result.log) {
                result.log.forEach(line => {
                    logEl.innerHTML += `<div class="log-line success">✓ ${line}</div>`;
                });
            }
            logEl.scrollTop = logEl.scrollHeight;
            showToast(_('install_success'), 'success');
            document.getElementById('installDone').style.display = '';
            setTimeout(() => checkServer(), 800);
        } catch (err) {
            fillEl.style.width = '100%';
            fillEl.style.background = 'linear-gradient(135deg, #ef4444, #dc2626)';
            logEl.innerHTML += `<div class="log-line error">✕ ${_('error')}: ${err.message}</div>`;
            logEl.scrollTop = logEl.scrollHeight;
            showToast(_('install_error') + err.message, 'error');
            document.getElementById('installDone').style.display = '';
        }
    }

    function formatBackupTime(mtime) {
        if (!mtime) return '—';
        try { return new Date(mtime * 1000).toLocaleString(); } catch (_) { return '—'; }
    }

    function isWgBackupFamily(proto) {
        return ['awg', 'awg2', 'awg_legacy', 'wireguard'].includes(protoBase(proto));
    }

    function backupDownloadUrl(proto, filename) {
        return `/api/servers/${SERVER_ID}/backups/download`;
    }

    async function showProtocolBackups(proto) {
        document.getElementById('backupProto').value = proto;
        document.getElementById('backupModalTitle').textContent = `${_('backups')} — ${getProtoTitle(proto)}`;
        const wg = isWgBackupFamily(proto);
        document.getElementById('exportClientsBtn').classList.toggle('hidden', !wg);
        document.getElementById('exportClientsHint').classList.toggle('hidden', !wg);
        openModal('backupModal');
        await loadProtocolBackups();
    }

    async function loadProtocolBackups() {
        const proto = document.getElementById('backupProto').value;
        const list = document.getElementById('backupList');
        const empty = document.getElementById('backupEmpty');
        const loading = document.getElementById('backupLoading');
        list.innerHTML = '';
        empty.classList.add('hidden');
        loading.classList.remove('hidden');
        try {
            const res = await apiCall(`/api/servers/${SERVER_ID}/backups`, 'POST', { protocol: proto });
            const backups = res.backups || [];
            if (!backups.length) {
                empty.classList.remove('hidden');
                return;
            }
            list.innerHTML = backups.map(b => `
                <div class="protocol-info-item" style="align-items:center; gap:var(--space-sm); flex-wrap:wrap;">
                    <div style="min-width:0; flex:1;">
                        <div class="protocol-info-value" style="word-break:break-all;">${b.name}</div>
                        <div class="protocol-info-label">${formatBytes(b.size)} • ${formatBackupTime(b.mtime)}</div>
                    </div>
                    <button class="btn btn-secondary btn-sm" onclick="downloadProtocolBackup('${proto}', '${b.name}')">${_('download')}</button>
                    <button class="btn btn-danger btn-sm" onclick="restoreProtocolBackup('${proto}', '${b.name}')">${_('restore_protocol_backup')}</button>
                </div>
            `).join('');
        } catch (err) {
            showToast(_('error') + ': ' + err.message, 'error');
        } finally {
            loading.classList.add('hidden');
        }
    }

    async function createProtocolBackup() {
        const proto = document.getElementById('backupProto').value;
        const btn = document.getElementById('createBackupBtn');
        const oldText = btn.textContent;
        btn.disabled = true;
        btn.textContent = _('creating_backup') || 'Creating backup...';
        try {
            await apiCall(`/api/servers/${SERVER_ID}/backups/create`, 'POST', { protocol: proto });
            showToast(_('backup_created'), 'success');
            await loadProtocolBackups();
        } catch (err) {
            showToast(_('error') + ': ' + err.message, 'error');
        } finally {
            btn.disabled = false;
            btn.textContent = oldText;
        }
    }

    async function exportProtocolClients() {
        const proto = document.getElementById('backupProto').value;
        const btn = document.getElementById('exportClientsBtn');
        const oldText = btn.textContent;
        btn.disabled = true;
        btn.textContent = _('exporting_client_configs');
        try {
            const res = await fetch(`/api/servers/${SERVER_ID}/backups/export-clients`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ protocol: proto })
            });
            if (!res.ok) {
                let message = 'Export failed';
                try {
                    const data = await res.json();
                    message = data.error || message;
                } catch (_) {}
                throw new Error(message);
            }
            const blob = await res.blob();
            const cd = res.headers.get('content-disposition') || '';
            const match = cd.match(/filename="?([^"]+)"?/i);
            const filename = match ? match[1] : `${proto}-clients.zip`;
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = filename;
            document.body.appendChild(a);
            a.click();
            a.remove();
            URL.revokeObjectURL(url);
            showToast(_('export_client_configs_done'), 'success');
        } catch (err) {
            showToast(_('error') + ': ' + err.message, 'error');
        } finally {
            btn.disabled = false;
            btn.textContent = oldText;
        }
    }

    async function restoreProtocolBackup(proto, filename) {
        if (!confirm(_('restore_protocol_backup_confirm'))) return;

        const busy = document.getElementById('backupBusy');
        const busyText = document.getElementById('backupBusyText');
        const createBtn = document.getElementById('createBackupBtn');
        const exportBtn = document.getElementById('exportClientsBtn');
        const list = document.getElementById('backupList');
        const restoreButtons = list ? Array.from(list.querySelectorAll('button')) : [];

        if (busyText) busyText.textContent = _('restoring_protocol_backup');
        if (busy) {
            busy.classList.remove('hidden');
            busy.style.display = 'flex';
        }
        if (createBtn) createBtn.disabled = true;
        if (exportBtn) exportBtn.disabled = true;
        restoreButtons.forEach(b => { b.disabled = true; });
        showToast(_('restoring_protocol_backup'), 'info');

        try {
            await apiCall(`/api/servers/${SERVER_ID}/backups/restore`, 'POST', { protocol: proto, filename });
            showToast(_('restore_protocol_backup_done'), 'success');
            await loadProtocolBackups();
        } catch (err) {
            showToast(_('error') + ': ' + err.message, 'error');
            await loadProtocolBackups();
        } finally {
            if (busy) {
                busy.classList.add('hidden');
                busy.style.display = 'none';
            }
            if (createBtn) createBtn.disabled = false;
            if (exportBtn) exportBtn.disabled = false;
        }
    }

    async function downloadProtocolBackup(proto, filename) {
        try {
            const res = await fetch(`/api/servers/${SERVER_ID}/backups/download`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ protocol: proto, filename })
            });
            if (!res.ok) {
                let message = 'Download failed';
                try {
                    const data = await res.json();
                    message = data.error || message;
                } catch (_) {}
                throw new Error(message);
            }
            const blob = await res.blob();
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = filename;
            document.body.appendChild(a);
            a.click();
            a.remove();
            URL.revokeObjectURL(url);
        } catch (err) {
            showToast(_('error') + ': ' + err.message, 'error');
        }
    }

    async function uninstallProtocol(proto) {
        const name = getProtoTitle(proto);
        if (!confirm(_('uninstall_confirm').replace('{}', name))) return;
        try {
            await apiCall(`/api/servers/${SERVER_ID}/uninstall`, 'POST', { protocol: proto });
            showToast(`${name} ${_('stopped')}`, 'success');
            setTimeout(() => window.location.reload(), 800);
        } catch (err) {
            showToast(_('error') + ': ' + err.message, 'error');
        }
    }

    // ========== Hysteria Settings (port) ==========
    async function applyHysteriaPort(proto, portValue) {
        const port = parseInt(portValue, 10);
        if (!port || port < 1 || port > 65535) {
            showToast(_('error') + ': invalid port', 'error');
            return;
        }
        if (port === 80) {
            showToast(_('error') + ': port 80 reserved', 'error');
            return;
        }
        try {
            showToast(_('saving') || 'Saving...', 'info');
            const res = await apiCall(`/api/servers/${SERVER_ID}/hysteria/settings`, 'POST', {
                protocol: proto || 'hysteria', port,
            });
            showToast(res.message || _('hysteria_settings_saved'), 'success');
            setTimeout(() => checkServer(), 800);
        } catch (err) {
            showToast(_('error') + ': ' + err.message, 'error');
            openHysteriaSettings(proto);
        }
    }

    async function openHysteriaSettings(proto = 'hysteria') {
        const live = currentProtocolStatus[proto] || {};
        const livePort = live.port || 443;
        const suggest = (parseInt(livePort, 10) === 443) ? 8998 : (livePort || 8998);
        document.getElementById('hysteriaSetProto').value = proto;
        document.getElementById('hysteriaSettingsTitle').textContent =
            `${_('hysteria_settings_title')} — ${getProtoTitle(proto)}`;
        document.getElementById('hysteriaSetDomain').value = live.domain || SERVER_SSL_DOMAIN || '';
        document.getElementById('hysteriaSetEmail').value = live.email || SERVER_SSL_EMAIL || '';
        document.getElementById('hysteriaSetPort').value = suggest;
        openModal('hysteriaSettingsModal');
        try {
            const data = await apiCall(`/api/servers/${SERVER_ID}/hysteria/settings?protocol=${encodeURIComponent(proto)}`);
            if (data.domain) document.getElementById('hysteriaSetDomain').value = data.domain;
            if (data.email) document.getElementById('hysteriaSetEmail').value = data.email;
            const p = parseInt(data.port, 10);
            if (p && p !== 443) document.getElementById('hysteriaSetPort').value = p;
            else if (p === 443) document.getElementById('hysteriaSetPort').value = 8998;
        } catch (_) { /* modal already open with local values */ }
    }

    async function saveHysteriaSettings(opts = {}) {
        const btn = document.getElementById('hysteriaSaveBtn');
        const text = document.getElementById('hysteriaSaveBtnText');
        const spinner = document.getElementById('hysteriaSaveSpinner');
        const renewBtn = document.getElementById('hysteriaRenewBtn');
        const renewText = document.getElementById('hysteriaRenewBtnText');
        const renewSpinner = document.getElementById('hysteriaRenewSpinner');
        const proto = document.getElementById('hysteriaSetProto').value || 'hysteria';
        const port = parseInt(document.getElementById('hysteriaSetPort').value, 10);
        const domain = document.getElementById('hysteriaSetDomain').value.trim();
        const email = document.getElementById('hysteriaSetEmail').value.trim();
        const renewSsl = !!opts.renew_ssl;
        if (!port || port < 1 || port > 65535) {
            showToast(_('error') + ': invalid port', 'error');
            return;
        }
        if (port === 80) {
            showToast(_('error') + ': port 80 reserved', 'error');
            return;
        }
        if ((renewSsl || domain) && (!domain || !email)) {
            showToast(_('error') + ': ' + _('hysteria_ssl_required'), 'error');
            return;
        }
        btn.disabled = true;
        if (renewBtn) renewBtn.disabled = true;
        if (renewSsl) {
            renewText.textContent = _('saving') || 'Saving...';
            renewSpinner.classList.remove('hidden');
        } else {
            text.textContent = _('saving') || 'Saving...';
            spinner.classList.remove('hidden');
        }
        try {
            const res = await apiCall(`/api/servers/${SERVER_ID}/hysteria/settings`, 'POST', {
                protocol: proto, port, domain, email, renew_ssl: renewSsl,
            });
            showToast(res.message || _('hysteria_settings_saved'), 'success');
            closeModal('hysteriaSettingsModal');
            setTimeout(() => checkServer(), 800);
        } catch (err) {
            showToast(_('error') + ': ' + err.message, 'error');
        } finally {
            btn.disabled = false;
            if (renewBtn) renewBtn.disabled = false;
            text.textContent = _('save');
            spinner.classList.add('hidden');
            if (renewText) renewText.textContent = _('hysteria_renew_ssl');
            if (renewSpinner) renewSpinner.classList.add('hidden');
        }
    }

    async function renewHysteriaSsl() {
        return saveHysteriaSettings({ renew_ssl: true });
    }

    // ========== SOCKS5 Settings ==========
    async function openSocks5Settings(proto = 'socks5') {
        currentSocks5Proto = proto || 'socks5';
        try {
            const data = await apiCall(`/api/servers/${SERVER_ID}/socks5/credentials?protocol=${encodeURIComponent(currentSocks5Proto)}`);
            document.getElementById('socks5SetPort').value = data.port || 38080;
            document.getElementById('socks5SetUser').value = data.username || 'proxy_user';
            document.getElementById('socks5SetPass').value = data.password || '';
            openModal('socks5SettingsModal');
        } catch (err) {
            showToast(_('error') + ': ' + err.message, 'error');
        }
    }

    async function saveSocks5Settings() {
        const btn = document.getElementById('socks5SaveBtn');
        const text = document.getElementById('socks5SaveBtnText');
        const spinner = document.getElementById('socks5SaveSpinner');
        const port = parseInt(document.getElementById('socks5SetPort').value) || 38080;
        const username = document.getElementById('socks5SetUser').value.trim();
        const password = document.getElementById('socks5SetPass').value.trim();
        btn.disabled = true;
        text.textContent = _('saving') || 'Saving...';
        spinner.classList.remove('hidden');
        try {
            const res = await apiCall(`/api/servers/${SERVER_ID}/socks5/credentials`, 'POST', {
                protocol: currentSocks5Proto, port, username, password
            });
            if (res.status === 'success') {
                showToast(_('socks5_settings_saved'), 'success');
                closeModal('socks5SettingsModal');
                setTimeout(() => checkServer(), 500);
            } else {
                throw new Error(res.message || res.error || 'Unknown error');
            }
        } catch (err) {
            showToast(_('error') + ': ' + err.message, 'error');
        } finally {
            btn.disabled = false;
            text.textContent = _('save');
            spinner.classList.add('hidden');
        }
    }

    // ========== Connection Management ==========
    function selectProtocolForConns(proto) {
        document.getElementById('connectionsSection').style.display = '';
        document.getElementById('connProtoSelect').value = proto;
        loadConnections();
        document.getElementById('connectionsSection').scrollIntoView({ behavior: 'smooth' });
    }

    const selectedConnectionIds = new Set();

    function updateMoveSelection() {
        selectedConnectionIds.clear();
        document.querySelectorAll('.conn-select:checked').forEach((el) => {
            if (el.dataset.clientId) selectedConnectionIds.add(el.dataset.clientId);
        });
        const count = selectedConnectionIds.size;
        const bulkBar = document.getElementById('connectionsBulkBar');
        const moveBtn = document.getElementById('moveConnectionsBtn');
        const countEl = document.getElementById('connSelectedCount');
        const selectAll = document.getElementById('connSelectAll');
        const boxes = document.querySelectorAll('.conn-select');
        if (bulkBar) bulkBar.classList.toggle('hidden', boxes.length === 0);
        if (moveBtn) moveBtn.classList.toggle('hidden', boxes.length === 0);
        if (countEl) countEl.textContent = _('connections_selected_count').replace('{}', String(count));
        if (selectAll) {
            selectAll.indeterminate = count > 0 && count < boxes.length;
            selectAll.checked = boxes.length > 0 && count === boxes.length;
        }
    }

    function toggleSelectAllConnections(checked) {
        document.querySelectorAll('.conn-select').forEach((el) => { el.checked = checked; });
        updateMoveSelection();
    }

    function openMoveConnectionsModal() {
        if (!selectedConnectionIds.size) {
            showToast(_('move_connections_none_selected'), 'error');
            return;
        }
        const select = document.getElementById('moveTargetServer');
        select.innerHTML = '';
        (ALL_SERVERS || []).forEach((srv) => {
            if (srv.id === SERVER_ID) return;
            const opt = document.createElement('option');
            opt.value = String(srv.id);
            opt.textContent = `${srv.name} (${srv.host})`;
            select.appendChild(opt);
        });
        if (!select.options.length) {
            showToast(_('move_connections_no_targets'), 'error');
            return;
        }
        openModal('moveConnectionsModal');
    }

    async function moveSelectedConnections() {
        const targetServerId = parseInt(document.getElementById('moveTargetServer').value, 10);
        const deleteSource = document.getElementById('moveDeleteSource').checked;
        const proto = document.getElementById('connProtoSelect').value;
        const clientIds = Array.from(selectedConnectionIds);
        if (!clientIds.length || Number.isNaN(targetServerId)) {
            showToast(_('move_connections_none_selected'), 'error');
            return;
        }
        if (!confirm(_('move_connections_confirm').replace('{}', String(clientIds.length)))) return;

        const btn = document.getElementById('moveConnectionsSubmitBtn');
        const text = document.getElementById('moveConnectionsSubmitText');
        const spinner = document.getElementById('moveConnectionsSpinner');
        btn.disabled = true;
        spinner.classList.remove('hidden');
        text.textContent = _('loading');
        try {
            const data = await apiCall(`/api/servers/${SERVER_ID}/connections/move`, 'POST', {
                protocol: proto,
                target_server_id: targetServerId,
                client_ids: clientIds,
                delete_source: deleteSource,
            });
            const moved = (data.moved || []).length;
            const failed = (data.failed || []).length;
            let msg = _('move_connections_success').replace('{}', String(moved));
            if (failed) msg += '. ' + _('move_connections_partial_fail').replace('{}', String(failed));
            showToast(msg, failed && !moved ? 'error' : 'success');
            closeModal('moveConnectionsModal');
            selectedConnectionIds.clear();
            loadConnections();
        } catch (err) {
            showToast(_('error') + ': ' + err.message, 'error');
        } finally {
            btn.disabled = false;
            spinner.classList.add('hidden');
            text.textContent = _('move_connections');
        }
    }

    async function loadConnections() {
        const proto = document.getElementById('connProtoSelect').value;
        const loading = document.getElementById('connectionsLoading');
        const emptyEl = document.getElementById('connectionsEmpty');
        const listEl = document.getElementById('connectionsListInner');

        if (!proto) {
            loading.style.display = 'none';
            return;
        }

        loading.style.display = '';
        emptyEl.classList.add('hidden');
        listEl.innerHTML = '';
        selectedConnectionIds.clear();
        updateMoveSelection();
        try {
            const data = await apiCall(`/api/servers/${SERVER_ID}/connections?protocol=${proto}`);
            loading.style.display = 'none';
            if (!data.clients || data.clients.length === 0) {
                emptyEl.classList.remove('hidden');
                updateMoveSelection();
                return;
            }
            window.connectionsStore = {};
            data.clients.forEach((client, idx) => {
                const userData = client.userData || {};
                window.connectionsStore[client.clientId] = userData;
                const name = userData.clientName || `Connection ${idx}`;
                const created = userData.creationDate || '';
                const handshake = userData.latestHandshake || '';
                const received = userData.dataReceived || '';
                const sent = userData.dataSent || '';
                const initial = name.charAt(0).toUpperCase();
                const enabled = (client.enabled !== undefined) ? client.enabled : (userData.enabled !== false);
                const hasPrivKey = !!userData.clientPrivateKey || proto === 'xray' || proto === 'telemt' || protoBase(proto) === 'hysteria' || protoBase(proto) === 'naiveproxy';
                const assignedUser = client.assigned_user || '';

                let metaHtml = '';
                if (assignedUser) metaHtml += `<span class="badge badge-info" style="font-size:0.65rem;">👤 ${escapeHtml(assignedUser)}</span>`;
                if (created) metaHtml += `<span>📅 ${formatDate(created)}</span>`;

                if (proto === 'telemt') {
                    if (userData.total_octets) metaHtml += `<span class="traffic-badge">📊 ${formatBytes(userData.total_octets)}</span>`;
                    if (userData.current_connections !== undefined) metaHtml += `<span>🔌 ${userData.current_connections} conns</span>`;
                    if (userData.active_ips !== undefined) metaHtml += `<span>📍 ${userData.active_ips} IPs</span>`;
                    if (userData.quota) metaHtml += `<span class="badge" style="font-size:0.65rem;">Quota: ${formatBytes(userData.quota)}</span>`;
                    if (userData.expiry) metaHtml += `<span class="badge" style="font-size:0.65rem;">⌛ ${formatDate(userData.expiry)}</span>`;
                } else {
                    if (handshake) metaHtml += `<span>🤝 ${handshake}</span>`;
                    if (received) metaHtml += `<span class="traffic-badge traffic-down">↓ ${received}</span>`;
                    if (sent) metaHtml += `<span class="traffic-badge traffic-up">↑ ${sent}</span>`;
                }

                if (!enabled) metaHtml += `<span class="badge badge-danger" style="font-size:0.65rem">${_('stop')}</span>`;

                const disabledStyle = enabled ? '' : 'opacity:0.5;';
                const toggleIcon = enabled ? uiIcon('pause') : uiIcon('play');
                const toggleTitle = enabled ? _('stop') : _('starting');
                // Show config button for all WireGuard-based clients.
                // If clientPrivateKey is missing (created via native app), we show a warning instead.
                const canShowConfig = proto === 'xray' || true; // always show config button

                listEl.innerHTML += `
                    <div class="client-item" style="${disabledStyle}">
                        <label class="conn-select-wrap">
                            <input type="checkbox" class="conn-select" data-client-id="${escapeJs(client.clientId)}" onchange="updateMoveSelection()">
                        </label>
                        <div class="client-info">
                            <div class="client-avatar">${initial}</div>
                            <div>
                                <div class="client-name">${escapeHtml(name)}</div>
                                <div class="client-meta">${metaHtml || `<span class="text-muted">${_('no_data')}</span>`}</div>
                            </div>
                        </div>
                        <div class="client-actions">
                            <button class="btn btn-secondary btn-sm btn-icon" onclick="showConnectionConfig('${escapeJs(client.clientId)}', '${escapeJs(name)}', ${!!userData.clientPrivateKey || proto === 'xray' || proto === 'telemt' || protoBase(proto) === 'hysteria' || protoBase(proto) === 'naiveproxy'})" title="${_('config')}">${uiIcon('file')}</button>
                            ${proto === 'telemt' ? `<button class="btn btn-secondary btn-sm btn-icon" onclick="editConnection('${escapeJs(client.clientId)}')" title="${_('edit')}">${uiIcon('pencil')}</button>` : ''}
                            <button class="btn btn-secondary btn-sm btn-icon" onclick="toggleConnection('${escapeJs(client.clientId)}', ${!enabled})" title="${toggleTitle}">${toggleIcon}</button>
                            <button class="btn btn-danger btn-sm btn-icon" onclick="removeConnection('${escapeJs(client.clientId)}')" title="${_('delete')}">${uiIcon('trash')}</button>
                        </div>
                    </div>
                `;
            });
            updateMoveSelection();
        } catch (err) {
            loading.style.display = 'none';
            showToast(_('connections_load_error') + ': ' + err.message, 'error');
        }
    }

    async function addConnection() {
        const proto = document.getElementById('connProtoSelect').value;
        const connName = document.getElementById('connectionName').value.trim() || 'Connection';
        const userId = document.getElementById('connectionUserId').value || null;
        const btn = document.getElementById('addConnBtn');
        const text = document.getElementById('addConnBtnText');
        const spinner = document.getElementById('addConnSpinner');
        btn.disabled = true;
        text.textContent = _('creating');
        spinner.classList.remove('hidden');
        try {
            const params = {
                protocol: proto, name: connName, user_id: userId,
            };
            if (proto === 'telemt') {
                params.telemt_quota = document.getElementById('telemtQuota').value || null;
                params.telemt_max_ips = parseInt(document.getElementById('telemtMaxIps').value) || null;
                params.telemt_expiry = document.getElementById('telemtExpiry').value || null;
                params.telemt_secret = document.getElementById('telemtSecret').value || null;
                params.telemt_ad_tag = document.getElementById('telemtAdTag').value || null;
                params.telemt_max_conns = parseInt(document.getElementById('telemtMaxConns').value) || null;
                if (isEditingConn) params.client_id = editingClientId;
            }
            const result = await apiCall(isEditingConn ? `/api/servers/${SERVER_ID}/connections/edit` : `/api/servers/${SERVER_ID}/connections/add`, 'POST', params);
            if (isEditingConn) {
                showToast(_('connection_updated'), 'success');
                closeModal('addConnectionModal');
            } else {
                showToast(_('connection_created').replace('{}', connName), 'success');
                closeModal('addConnectionModal');
                document.getElementById('connectionName').value = '';
                if (result.config) {
                    currentConfig = result.config;
                    currentVpnLink = result.vpn_link || '';
                    currentConfigName = connName.replace(/\s+/g, '_');
                    document.getElementById('configModalTitle').textContent = `${_('config')} — ${connName}`;
                    document.getElementById('configText').textContent = result.config;
                    document.getElementById('vpnLinkText').textContent = currentVpnLink;
                    document.getElementById('panel-vpn').style.display = (proto === 'telemt' || protoBase(proto) === 'hysteria' || protoBase(proto) === 'naiveproxy' ? 'none' : '');
                    document.querySelectorAll('.config-tab')[1].style.display = (proto === 'telemt' || protoBase(proto) === 'hysteria' || protoBase(proto) === 'naiveproxy' ? 'none' : '');
                    switchConfigTab('conf');
                    openModal('configModal');
                    generateQR(result.config);
                }
            }
            loadConnections();
        } catch (err) {
            showToast(_('error') + ': ' + err.message, 'error');
        } finally {
            btn.disabled = false;
            text.textContent = _('create');
            spinner.classList.add('hidden');
        }
    }

    let isEditingConn = false;
    let editingClientId = null;

    function openAddConnectionModal(isEdit = false, clientData = null) {
        isEditingConn = isEdit;
        const proto = document.getElementById('connProtoSelect').value;
        const telemtOpts = document.getElementById('telemtConnOptions');

        document.getElementById('addConnectionModalTitle').textContent = isEdit ? _('edit_connection') : _('add_connection');
        document.getElementById('addConnBtnText').textContent = isEdit ? _('save') : _('create');
        document.getElementById('connectionNameGroup').style.display = isEdit ? 'none' : 'block';

        if (proto === 'telemt') {
            telemtOpts.style.display = 'block';
            if (isEdit && clientData) {
                editingClientId = clientData.clientName;
                document.getElementById('telemtQuota').value = clientData.quota || '';
                document.getElementById('telemtMaxIps').value = ''; 
                document.getElementById('telemtExpiry').value = clientData.expiry || '';
                document.getElementById('telemtSecret').value = clientData.token || '';
                document.getElementById('telemtAdTag').value = clientData.user_ad_tag || '';
                document.getElementById('telemtMaxConns').value = clientData.max_tcp_conns || '';
            } else {
                document.getElementById('telemtQuota').value = '';
                document.getElementById('telemtMaxIps').value = '';
                document.getElementById('telemtExpiry').value = '';
                document.getElementById('telemtSecret').value = '';
                document.getElementById('telemtAdTag').value = '';
                document.getElementById('telemtMaxConns').value = '';
            }
        } else {
            telemtOpts.style.display = 'none';
        }
        openModal('addConnectionModal');
    }

    function editConnection(clientId) {
        const userData = window.connectionsStore[clientId];
        if (userData) {
            userData.clientName = clientId;
            openAddConnectionModal(true, userData);
        }
    }

    async function removeConnection(clientId) {
        if (!confirm(_('delete_connection_confirm'))) return;
        const proto = document.getElementById('connProtoSelect').value;
        try {
            await apiCall(`/api/servers/${SERVER_ID}/connections/remove`, 'POST', {
                protocol: proto, client_id: clientId,
            });
            showToast(_('connection_deleted'), 'success');
            loadConnections();
        } catch (err) {
            showToast(_('error') + ': ' + err.message, 'error');
        }
    }

    async function showConnectionConfig(clientId, connName, hasKey) {
        const proto = document.getElementById('connProtoSelect').value;

        // Restore tabs visibility first
        document.getElementById('panel-vpn').style.display = (proto === 'telemt' || protoBase(proto) === 'hysteria' || protoBase(proto) === 'naiveproxy' ? 'none' : '');
        document.getElementById('panel-qr').style.display = '';
        document.querySelectorAll('.config-tab')[1].style.display = (proto === 'telemt' || protoBase(proto) === 'hysteria' || protoBase(proto) === 'naiveproxy' ? 'none' : '');
        document.querySelectorAll('.config-tab')[2].style.display = '';

        // Client was created via native Amnezia app — private key is not stored server-side
        if (!hasKey) {
            document.getElementById('configModalTitle').textContent = `${_('config')} — ${connName}`;
            currentConfig = '';
            currentVpnLink = '';
            currentConfigName = connName.replace(/\s+/g, '_');
            document.getElementById('configText').innerHTML = `<div style="padding:var(--space-md);background:var(--bg-primary);border-radius:var(--radius-md);border:1px solid var(--border-color);"><div style="font-size:1.4rem;margin-bottom:var(--space-sm);">⚠️</div><div style="font-weight:600;margin-bottom:var(--space-sm);">${_('config_unavailable')}</div><div style="font-size:0.85rem;color:var(--text-muted);line-height:1.6;">${_('config_unavailable_desc').replace(/\\n/g, '<br>')}<br><br><strong>${_('client_public_key')}</strong><br><code style="word-break:break-all;font-size:0.78rem;">${escapeHtml(clientId)}</code></div></div>`;
            document.getElementById('vpnLinkText').textContent = '';
            document.getElementById('panel-vpn').style.display = 'none';
            document.getElementById('panel-qr').style.display = 'none';
            document.querySelectorAll('.config-tab')[1].style.display = 'none';
            document.querySelectorAll('.config-tab')[2].style.display = 'none';
            switchConfigTab('conf');
            openModal('configModal');
            return;
        }

        try {
            showToast(_('loading'), 'info');
            const result = await apiCall(`/api/servers/${SERVER_ID}/connections/config`, 'POST', {
                protocol: proto, client_id: clientId,
            });
            if (result.config) {
                currentConfig = result.config;
                currentVpnLink = result.vpn_link || '';
                currentConfigName = connName.replace(/\s+/g, '_');
                document.getElementById('configModalTitle').textContent = `${_('config')} — ${connName}`;
                document.getElementById('configText').textContent = result.config;
                document.getElementById('vpnLinkText').textContent = currentVpnLink;
                const hintEl = document.getElementById('configClientHint');
                if (hintEl) {
                    if (protoBase(proto) === 'naiveproxy') {
                        hintEl.textContent = _('naiveproxy_client_hint');
                        hintEl.classList.remove('hidden');
                    } else {
                        hintEl.textContent = '';
                        hintEl.classList.add('hidden');
                    }
                }
                switchConfigTab('conf');
                openModal('configModal');
                generateQR(result.config);
            }
        } catch (err) {
            showToast(_('error') + ': ' + err.message, 'error');
        }
    }

    async function toggleConnection(clientId, enable) {
        const proto = document.getElementById('connProtoSelect').value;
        const action = enable ? _('enable_connection_progress') : _('disable_connection_progress');
        try {
            showToast(action, 'info');
            await apiCall(`/api/servers/${SERVER_ID}/connections/toggle`, 'POST', {
                protocol: proto, client_id: clientId, enable: enable,
            });
            showToast(enable ? _('connection_enabled') : _('connection_disabled'), 'success');
            loadConnections();
        } catch (err) {
            showToast(_('error') + ': ' + err.message, 'error');
        }
    }

    // ========== Config Tabs ==========
    function switchConfigTab(tab) {
        document.querySelectorAll('.config-tab').forEach(t => t.classList.remove('active'));
        document.querySelectorAll('.config-panel').forEach(p => p.classList.remove('active'));
        const tabs = document.querySelectorAll('.config-tab');
        const panels = { conf: 'panel-conf', vpn: 'panel-vpn', qr: 'panel-qr' };
        const tabIdx = { conf: 0, vpn: 1, qr: 2 };
        tabs[tabIdx[tab]].classList.add('active');
        document.getElementById(panels[tab]).classList.add('active');
    }

    function generateQR(text) {
        const container = document.getElementById('qrCode');
        container.innerHTML = '';
        try {
            new QRCode(container, {
                text: text, width: 280, height: 280,
                colorDark: '#000000', colorLight: '#ffffff',
                correctLevel: QRCode.CorrectLevel.L
            });
        } catch (e) {
            console.error('QR lib error:', e);
            container.innerHTML = `<div style="color:var(--text-muted);font-size:0.85rem;">${_('qr_error')}</div>`;
        }
    }

    // ========== Utilities ==========
    function formatDate(dateStr) {
        try {
            const d = new Date(dateStr);
            if (isNaN(d)) return dateStr;
            return d.toLocaleDateString('ru-RU', { day: '2-digit', month: '2-digit', year: 'numeric' });
        } catch { return dateStr; }
    }

    function formatBytes(bytes) {
        if (!bytes || bytes === 0) return '0 B';
        const k = 1024;
        const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
        const i = Math.floor(Math.log(bytes) / Math.log(k));
        return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
    }

    function escapeHtml(str) {
        const div = document.createElement('div');
        div.textContent = str;
        return div.innerHTML;
    }

    function escapeJs(str) {
        return str.replace(/'/g, "\\'").replace(/"/g, '\\"');
    }

    // ========== Management ==========
    async function rebootServer() {
        if (!confirm('Are you sure you want to reboot the server? Disconnections will occur.')) return;
        try {
            showToast('Rebooting server...', 'info');
            await apiCall(`/api/servers/${SERVER_ID}/reboot`, 'POST');
            showToast('Server is rebooting', 'success');
        } catch (err) {
            showToast('Error: ' + err.message, 'error');
        }
    }

    async function clearServer() {
        if (!confirm("⚠️ WARNING: This will completely delete all Amnezia configurations, containers, and data from /opt/amnezia on the server! Are you absolutely sure?")) return;
        try {
            showToast('Clearing server from Amnezia software...', 'info');
            await apiCall(`/api/servers/${SERVER_ID}/clear`, 'POST');
            showToast('Server cleared successfully!', 'success');
            setTimeout(() => location.reload(), 1500);
        } catch (err) {
            showToast('Error: ' + err.message, 'error');
        }
    }

    async function removeServer() {
        if (!confirm("Are you sure you want to remove this server from the panel? (The server itself will not be erased).")) return;
        try {
            await apiCall(`/api/servers/${SERVER_ID}/delete`, 'POST');
            window.location.href = '/';
        } catch (err) {
            showToast('Error: ' + err.message, 'error');
        }
    }



    // ========== Init ==========
    applyInstalledAppsVisibility();
    checkServer();
    loadServerStats();
</script>
{% endblock %}
