// C2 模块前端逻辑 - 完整实现
// 支持: xterm 终端、文件管理、监听器/会话/任务/事件/Payload/Profile 管理

(function() {
    'use strict';

    // C2 模块命名空间
    const C2 = {
        currentPage: '',
        listeners: [],
        sessions: [],
        tasks: [],
        tasksPage: 1,
        tasksPageSize: 10,
        tasksTotal: 0,
        tasksPendingQueuedCount: null,
        events: [],
        eventsPage: 1,
        eventsPageSize: 10,
        eventsTotal: 0,
        profiles: [],
        selectedSessionId: null,
        selectedListenerId: null,
        eventSource: null,
        // xterm 相关
        terminalInstance: null,
        terminalFitAddon: null,
        terminalResizeObserver: null,
        terminalContainer: null,
        terminalSessionId: 'main',
        // 文件管理
        currentPath: '/',
        fileList: [],
        // 任务轮询
        taskPollInterval: null,
    };

    // API 基础路径
    const API_BASE = '/api/c2';

    window.__c2DownloadPayload = function(filename) {
        const url = `${API_BASE}/payloads/${filename}/download`;
        const fetchFn = (typeof apiFetch === 'function') ? apiFetch : fetch;
        fetchFn(url).then(resp => {
            if (!resp.ok) throw new Error('download failed: ' + resp.status);
            return resp.blob();
        }).then(blob => {
            const a = document.createElement('a');
            a.href = URL.createObjectURL(blob);
            a.download = filename;
            document.body.appendChild(a);
            a.click();
            a.remove();
            URL.revokeObjectURL(a.href);
        }).catch(err => {
            if (window.showToast) window.showToast(err.message, 'error');
        });
    };

    function c2t(key, opts) {
        try {
            if (typeof window.t === 'function') return window.t(key, opts || {});
        } catch (e) {}
        return key;
    }

    function listenerTypeLabel(type) {
        if (!type) return '';
        const k = 'c2.listeners.typeLabels.' + String(type).toLowerCase();
        const tr = c2t(k);
        if (tr !== k) return tr;
        return String(type).replace(/_/g, ' ');
    }

    function sessionStatusLabel(status) {
        const s = String(status || '').toLowerCase();
        if (!s) return '';
        const k = 'c2.sessions.' + s;
        const tr = c2t(k);
        if (tr !== k) return tr;
        return status;
    }

    function taskStatusLabel(status) {
        const s = String(status || '').toLowerCase();
        if (!s) return '';
        const k = 'c2.tasks.' + s;
        const tr = c2t(k);
        if (tr !== k) return tr;
        return status;
    }

    // ============================================================================
    // 工具函数
    // ============================================================================

    function apiRequest(method, url, data) {
        const options = {
            method: method,
            headers: { 'Content-Type': 'application/json' }
        };
        if (data && (method === 'POST' || method === 'PUT' || method === 'PATCH' || method === 'DELETE')) {
            options.body = JSON.stringify(data);
        }
        if (typeof apiFetch === 'function') {
            return apiFetch(url, options).then(r => r.json());
        }
        return fetch(url, options).then(r => r.json());
    }

    function showToast(message, type = 'info') {
        if (window.showToast) {
            window.showToast(message, type);
            return;
        }
        const container = document.getElementById('c2-toast-container') || (() => {
            const div = document.createElement('div');
            div.id = 'c2-toast-container';
            div.style.cssText = 'position:fixed;top:20px;right:20px;z-index:10000;display:flex;flex-direction:column;gap:8px;';
            document.body.appendChild(div);
            return div;
        })();
        const toast = document.createElement('div');
        const colors = { error: '#e53e3e', success: '#38a169', info: '#3182ce', warn: '#d69e2e' };
        toast.style.cssText = `background:${colors[type] || colors.info};color:#fff;padding:10px 18px;border-radius:6px;font-size:0.875rem;box-shadow:0 4px 12px rgba(0,0,0,0.2);opacity:0;transition:opacity .3s;max-width:400px;word-break:break-word;`;
        toast.textContent = message;
        container.appendChild(toast);
        requestAnimationFrame(() => { toast.style.opacity = '1'; });
        setTimeout(() => {
            toast.style.opacity = '0';
            setTimeout(() => toast.remove(), 300);
        }, 3500);
    }

    function formatTime(dateStr) {
        if (!dateStr) return '-';
        return new Date(dateStr).toLocaleString();
    }

    function formatDuration(ms) {
        if (!ms || ms <= 0) return '-';
        if (ms < 1000) return c2t('c2.fmt.durationMs', { n: ms });
        if (ms < 60000) return c2t('c2.fmt.durationSec', { n: (ms / 1000).toFixed(1) });
        return c2t('c2.fmt.durationMin', { n: (ms / 60000).toFixed(1) });
    }

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

    /** 任务列表操作按钮（查看/取消/删除）— 事件委托 */
    function bindC2TaskActionDelegation() {
        if (document.documentElement.dataset.c2TaskActionsBound === '1') return;
        document.documentElement.dataset.c2TaskActionsBound = '1';
        document.addEventListener('click', function(e) {
            const btn = e.target.closest('[data-c2-task-action]');
            if (!btn) return;
            e.preventDefault();
            e.stopPropagation();
            const action = btn.getAttribute('data-c2-task-action');
            const id = btn.getAttribute('data-task-id');
            if (!id) return;
            if (action === 'view') C2.viewTask(id);
            else if (action === 'cancel') C2.cancelTask(id);
            else if (action === 'delete') C2.deleteTaskById(id);
        });
    }
    bindC2TaskActionDelegation();

    /** 监听器表单：Malleable Profile 下拉选项 HTML（value / 文本已转义） */
    function listenerProfileSelectHtml(selectedProfileId) {
        const sel = selectedProfileId ? String(selectedProfileId) : '';
        let opts = `<option value="">${escapeHtml(c2t('c2.listeners.malleableProfileNone'))}</option>`;
        for (const p of (C2.profiles || [])) {
            if (!p) continue;
            const pid = p.id || p.ID;
            if (!pid) continue;
            const idEsc = escapeHtml(String(pid));
            const nameEsc = escapeHtml(p.name || pid);
            const selected = sel && String(pid) === sel ? ' selected' : '';
            opts += `<option value="${idEsc}"${selected}>${nameEsc}</option>`;
        }
        return opts;
    }

    function listenerResolvedProfileId(l) {
        if (!l) return '';
        const v = l.profileId != null && l.profileId !== '' ? l.profileId : l.profile_id;
        return v != null ? String(v).trim() : '';
    }

    /** 监听器卡片展示用 Profile 名称（依赖 C2.profiles，由 loadListeners 一并拉取） */
    function listenerProfileDisplayName(l) {
        const pid = listenerResolvedProfileId(l);
        if (!pid) return '';
        const list = C2.profiles || [];
        for (let i = 0; i < list.length; i++) {
            const p = list[i];
            if (p && (p.id === pid || p.ID === pid)) return String(p.name || p.id || pid).trim() || pid;
        }
        return pid.length > 18 ? pid.substring(0, 16) + '…' : pid;
    }

    function listenerTypeVisualClass(type) {
        const t = String(type || '').toLowerCase();
        if (t === 'https_beacon') return 'c2-ltype-mark--https';
        if (t === 'http_beacon') return 'c2-ltype-mark--http';
        if (t === 'tcp_reverse') return 'c2-ltype-mark--tcp';
        if (t === 'websocket') return 'c2-ltype-mark--ws';
        return 'c2-ltype-mark--def';
    }

    function listenerTypeShortLabel(type) {
        const t = String(type || '').toLowerCase();
        if (t === 'https_beacon') return 'HTTPS';
        if (t === 'http_beacon') return 'HTTP';
        if (t === 'tcp_reverse') return 'TCP';
        if (t === 'websocket') return 'WS';
        return '?';
    }

    function listenerCardStatusPillLabel(status) {
        const s = String(status || '').toLowerCase();
        if (s === 'running') return c2t('c2.listeners.running');
        if (s === 'stopped') return c2t('c2.listeners.stopped');
        if (s === 'error') return c2t('c2.listeners.statusError');
        return c2t('c2.listeners.stopped');
    }

    /** 避免 i18n 插值把日期里的「/」转成 &#x2F;，与 formatTime 拼接后整体转义 */
    function formatListenerStartedHtml(dateStr) {
        if (!dateStr) return '';
        const prefix = c2t('c2.listeners.startedAtPrefix');
        const time = formatTime(dateStr);
        return '<div class="c2-listener-meta-row"><span class="c2-listener-meta-label">' + escapeHtml(prefix) + '</span> <span class="c2-listener-meta-time">' + escapeHtml(time) + '</span></div>';
    }

    function copyToClipboard(text) {
        if (navigator.clipboard) {
            navigator.clipboard.writeText(text).then(() => showToast(c2t('c2.clipboardCopied'), 'success'));
        } else {
            const ta = document.createElement('textarea');
            ta.value = text;
            document.body.appendChild(ta);
            ta.select();
            document.execCommand('copy');
            document.body.removeChild(ta);
            showToast(c2t('c2.clipboardCopied'), 'success');
        }
    }

    // ============================================================================
    // 页面初始化
    // ============================================================================

    C2.init = function() {
        const pageId = window.currentPageId || '';
        
        if (pageId.startsWith('c2')) {
            C2.connectEventStream();
        }

        switch(pageId) {
            case 'c2':
            case 'c2-listeners':
                C2.loadListeners();
                break;
            case 'c2-sessions':
                C2.loadSessions();
                break;
            case 'c2-tasks':
                C2.loadTasks();
                break;
            case 'c2-payloads':
                C2.loadListenersForPayload();
                break;
            case 'c2-events':
                C2.loadEvents();
                break;
            case 'c2-profiles':
                C2.loadProfiles();
                break;
        }
    };

    // ============================================================================
    // 监听器管理
    // ============================================================================

    C2.loadListeners = function() {
        Promise.all([
            apiRequest('GET', `${API_BASE}/listeners`),
            apiRequest('GET', `${API_BASE}/profiles`).catch(function() { return {}; })
        ]).then(function(results) {
            var ldata = results[0];
            var pdata = results[1];
            C2.listeners = (ldata && ldata.listeners) || [];
            if (pdata && pdata.profiles && !pdata.error) {
                C2.profiles = pdata.profiles;
            }
            C2.renderListeners();
            C2.updateDashboardStats();
        });
    };

    /** 拉取 Profile 列表（监听器表单用）；失败时置空列表不阻断弹窗 */
    C2.ensureProfilesLoaded = function() {
        return apiRequest('GET', `${API_BASE}/profiles`).then(data => {
            if (data && data.error) {
                C2.profiles = [];
                return C2.profiles;
            }
            C2.profiles = (data && data.profiles) || [];
            return C2.profiles;
        });
    };

    C2.renderListeners = function() {
        const container = document.getElementById('c2-listener-grid');
        if (!container) return;

        if (C2.listeners.length === 0) {
            container.innerHTML = `
                <div class="c2-empty">
                    <svg width="56" height="56" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="1.2" style="margin-bottom:16px;opacity:0.6;">
                        <path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"></path>
                        <path d="M12 15l-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"></path>
                        <path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0"></path>
                        <path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"></path>
                    </svg>
                    <h3 style="margin-bottom:8px;font-size:18px;font-weight:700;">${escapeHtml(c2t('c2.listeners.emptyTitle'))}</h3>
                    <p style="font-size:14px;">${escapeHtml(c2t('c2.listeners.emptyHint'))}</p>
                    <button class="btn-primary" onclick="C2.showCreateListenerModal()" style="margin-top:20px;">
                        ${escapeHtml(c2t('c2.listeners.headerCreateBtn'))}
                    </button>
                </div>`;
            return;
        }

        container.innerHTML = C2.listeners.map(function(l) {
            const st = String(l.status || 'stopped').toLowerCase();
            const stUi = st === 'running' || st === 'stopped' || st === 'error' ? st : 'stopped';
            const profilePid = listenerResolvedProfileId(l);
            const profileName = listenerProfileDisplayName(l);
            const profileBadge = profilePid
                ? '<div class="c2-listener-profile-badge" title="' + escapeHtml(c2t('c2.listeners.profileBadgeTitle')) + '"><span class="c2-listener-profile-dot" aria-hidden="true"></span><span>' + escapeHtml(profileName) + '</span></div>'
                : '';
            const cb = C2.getListenerCallbackHost(l);
            const cbRow = cb
                ? '<div class="c2-listener-kv"><span class="c2-listener-kv-label">' + escapeHtml(c2t('c2.listeners.callbackShort')) + '</span><span class="c2-listener-kv-val c2-listener-mono">' + escapeHtml(cb) + '</span></div>'
                : '';
            const remarkRow = l.remark ? '<div class="c2-listener-remark">' + escapeHtml(l.remark) + '</div>' : '';
            const startedHtml = formatListenerStartedHtml(l.startedAt);
            const pillLabel = escapeHtml(listenerCardStatusPillLabel(st));
            const typeMark = escapeHtml(listenerTypeShortLabel(l.type));
            const typeVis = listenerTypeVisualClass(l.type);
            const fullType = escapeHtml(listenerTypeLabel(l.type));
            const bindVal = escapeHtml(String(l.bindHost)) + ':' + escapeHtml(String(l.bindPort));

            return `
            <article class="c2-listener-card c2-listener-card--${stUi}" data-listener-id="${escapeHtml(l.id)}">
                <div class="c2-listener-card-head">
                    <div class="c2-ltype-mark ${typeVis}" title="${fullType}"><span>${typeMark}</span></div>
                    <div class="c2-listener-card-head-main">
                        <div class="c2-listener-card-title-row">
                            <h3 class="c2-listener-name">${escapeHtml(l.name)}</h3>
                            <span class="c2-listener-pill c2-listener-pill--${stUi}">${pillLabel}</span>
                        </div>
                        <div class="c2-listener-id-row">
                            <code class="c2-listener-id-full" title="${escapeHtml(l.id)}">${escapeHtml(l.id)}</code>
                        </div>
                    </div>
                </div>
                <div class="c2-listener-card-body">
                    <div class="c2-listener-kv">
                        <span class="c2-listener-kv-label">${escapeHtml(c2t('c2.listeners.bindEndpoint'))}</span>
                        <span class="c2-listener-kv-val c2-listener-mono"><span class="c2-status-dot ${escapeHtml(st)}"></span>${bindVal}</span>
                    </div>
                    ${cbRow}
                    ${profileBadge}
                    ${remarkRow}
                    ${startedHtml}
                </div>
                <div class="c2-listener-card-actions">
                    ${l.status === 'stopped'
                        ? `<button type="button" class="btn-primary btn-sm" onclick="C2.startListener('${l.id}')">▶ ${escapeHtml(c2t('c2.listeners.start'))}</button>`
                        : `<button type="button" class="btn-secondary btn-sm" onclick="C2.stopListener('${l.id}')">⏹ ${escapeHtml(c2t('c2.listeners.stop'))}</button>`
                    }
                    <button type="button" class="btn-secondary btn-sm" onclick="C2.editListener('${l.id}')">${escapeHtml(c2t('c2.listeners.edit'))}</button>
                    <button type="button" class="btn-danger btn-sm" onclick="C2.deleteListener('${l.id}')">${escapeHtml(c2t('c2.listeners.delete'))}</button>
                </div>
            </article>`;
        }).join('');
    };

    C2.getListenerCallbackHost = function(l) {
        if (!l) return '';
        try {
            var raw = l.configJson != null ? l.configJson : '{}';
            var j = typeof raw === 'string' ? JSON.parse(raw || '{}') : (raw || {});
            return String(j.callback_host || '').trim();
        } catch (e) {
            return '';
        }
    };

    C2.showCreateListenerModal = function() {
        const modal = document.getElementById('c2-modal');
        const content = document.getElementById('c2-modal-content');
        if (!content || !modal) return;

        modal.style.display = 'flex';
        content.innerHTML = `
            <div class="c2-modal-header">
                <h3>${escapeHtml(c2t('c2.listeners.modalCreateTitle'))}</h3>
                <button class="c2-modal-close" onclick="C2.closeModal()">&times;</button>
            </div>
            <div class="c2-modal-body">
                <p class="form-hint" style="margin-top:0;">${escapeHtml(c2t('c2.listeners.loadingProfiles'))}</p>
            </div>
        `;

        C2.ensureProfilesLoaded().then(() => {
            const profileOpts = listenerProfileSelectHtml('');
            const emptyProfHintCreate = (C2.profiles && C2.profiles.length > 0)
                ? ''
                : `<div class="form-hint" style="margin-bottom:6px;color:#b45309;">${escapeHtml(c2t('c2.listeners.malleableProfileEmptyListHint'))}</div>`;
            content.innerHTML = `
            <div class="c2-modal-header">
                <h3>${escapeHtml(c2t('c2.listeners.modalCreateTitle'))}</h3>
                <button class="c2-modal-close" onclick="C2.closeModal()">&times;</button>
            </div>
            <div class="c2-modal-body">
                <div class="c2-form-row">
                    <div class="c2-form-group">
                        <label>${escapeHtml(c2t('c2.listeners.name'))}</label>
                        <input type="text" id="c2-listener-name" class="form-control" placeholder="${escapeHtml(c2t('c2.listeners.placeholderNameExample'))}">
                    </div>
                    <div class="c2-form-group">
                        <label>${escapeHtml(c2t('c2.listeners.type'))}</label>
                        <select id="c2-listener-type" class="form-control c2-native-select" onchange="C2.syncListenerProfileRowForType()">
                            <option value="http_beacon">HTTP Beacon</option>
                            <option value="https_beacon">HTTPS Beacon</option>
                            <option value="tcp_reverse">TCP Reverse</option>
                            <option value="websocket">WebSocket</option>
                        </select>
                    </div>
                </div>
                <div class="c2-form-row">
                    <div class="c2-form-group">
                        <label>${escapeHtml(c2t('c2.listeners.bindHost'))}</label>
                        <input type="text" id="c2-listener-host" class="form-control" value="127.0.0.1">
                        <div class="form-hint">${escapeHtml(c2t('c2.listeners.bindHintExternal'))}</div>
                    </div>
                    <div class="c2-form-group">
                        <label>${escapeHtml(c2t('c2.listeners.bindPort'))}</label>
                        <input type="number" id="c2-listener-port" class="form-control" placeholder="8443">
                    </div>
                </div>
                <div class="c2-form-group" id="c2-listener-profile-group">
                    <label>${escapeHtml(c2t('c2.listeners.malleableProfile'))}</label>
                    ${emptyProfHintCreate}
                    <select id="c2-listener-profile-id" class="form-control c2-native-select">${profileOpts}</select>
                    <div class="form-hint">${escapeHtml(c2t('c2.listeners.malleableProfileHint'))}</div>
                </div>
                <div class="c2-form-group">
                    <label>${escapeHtml(c2t('c2.listeners.callbackHost'))}</label>
                    <input type="text" id="c2-listener-callback-host" class="form-control" placeholder="">
                    <div class="form-hint">${escapeHtml(c2t('c2.listeners.callbackHostHint'))}</div>
                </div>
                <div class="c2-form-group">
                    <label>${escapeHtml(c2t('c2.listeners.remark'))}</label>
                    <input type="text" id="c2-listener-remark" class="form-control" placeholder="${escapeHtml(c2t('c2.listeners.placeholderRemarkLong'))}">
                </div>
            </div>
            <div class="c2-modal-footer">
                <button class="btn-secondary" onclick="C2.closeModal()">${escapeHtml(c2t('common.cancel'))}</button>
                <button class="btn-primary" onclick="C2.createListener()">${escapeHtml(c2t('c2.listeners.submitCreate'))}</button>
            </div>
        `;
            C2.syncListenerProfileRowForType();
        }).catch(() => {
            showToast(c2t('c2.listeners.toastProfilesLoadFailed'), 'error');
            C2.closeModal();
        });
    };

    /** 非 HTTP/HTTPS Beacon 时隐藏 Profile 行（避免误以为 TCP 等也会用） */
    C2.syncListenerProfileRowForType = function() {
        const typeEl = document.getElementById('c2-listener-type');
        const row = document.getElementById('c2-listener-profile-group');
        if (!typeEl || !row) return;
        const t = String(typeEl.value || '').toLowerCase();
        const show = t === 'http_beacon' || t === 'https_beacon';
        row.style.display = show ? '' : 'none';
        if (!show) {
            const sel = document.getElementById('c2-listener-profile-id');
            if (sel) sel.value = '';
        }
    };

    C2.createListener = function() {
        const name = document.getElementById('c2-listener-name')?.value.trim();
        const type = document.getElementById('c2-listener-type')?.value;
        const bindHost = document.getElementById('c2-listener-host')?.value || '127.0.0.1';
        const bindPort = parseInt(document.getElementById('c2-listener-port')?.value);
        const callbackHost = document.getElementById('c2-listener-callback-host')?.value?.trim() || '';
        const remark = document.getElementById('c2-listener-remark')?.value;

        if (!name || !type || !bindPort) {
            showToast(c2t('c2.listeners.toastFillRequired'), 'error');
            return;
        }

        const profileId = (document.getElementById('c2-listener-profile-id')?.value || '').trim();

        apiRequest('POST', `${API_BASE}/listeners`, {
            name, type, bind_host: bindHost, bind_port: bindPort, remark,
            callback_host: callbackHost,
            profile_id: profileId
        }).then(data => {
            if (data.error) {
                showToast(data.error, 'error');
            } else {
                showToast(c2t('c2.listeners.toastCreated'), 'success');
                C2.closeModal();
                C2.loadListeners();
            }
        });
    };

    C2.startListener = function(id) {
        apiRequest('POST', `${API_BASE}/listeners/${id}/start`, {}).then(data => {
            if (data.error) showToast(data.error, 'error');
            else {
                showToast(c2t('c2.listeners.toastStarted'), 'success');
                C2.loadListeners();
            }
        });
    };

    C2.stopListener = function(id) {
        apiRequest('POST', `${API_BASE}/listeners/${id}/stop`, {}).then(data => {
            if (data.error) showToast(data.error, 'error');
            else {
                showToast(c2t('c2.listeners.toastStopped'), 'success');
                C2.loadListeners();
            }
        });
    };

    C2.deleteListener = function(id) {
        if (!confirm(c2t('c2.listeners.confirmDelete'))) return;
        apiRequest('DELETE', `${API_BASE}/listeners/${id}`, {}).then(data => {
            showToast(c2t('c2.listeners.toastDeleted'), 'success');
            C2.loadListeners();
        });
    };

    C2.editListener = function(id) {
        const l = C2.listeners.find(x => x.id === id);
        if (!l) return;

        const cbHost = C2.getListenerCallbackHost(l);
        const modal = document.getElementById('c2-modal');
        const content = document.getElementById('c2-modal-content');
        if (!content || !modal) return;

        modal.style.display = 'flex';
        content.innerHTML = `
            <div class="c2-modal-header">
                <h3>${escapeHtml(c2t('c2.listeners.editTitle'))}</h3>
                <button class="c2-modal-close" onclick="C2.closeModal()">&times;</button>
            </div>
            <div class="c2-modal-body">
                <p class="form-hint" style="margin-top:0;">${escapeHtml(c2t('c2.listeners.loadingProfiles'))}</p>
            </div>
        `;

        C2.ensureProfilesLoaded().then(() => {
            const resolvedPid = listenerResolvedProfileId(l);
            const profileOpts = listenerProfileSelectHtml(resolvedPid);
            const lt = String(l.type || '').toLowerCase();
            const httpHint = (lt === 'http_beacon' || lt === 'https_beacon')
                ? ''
                : `<div class="form-hint" style="margin-bottom:6px;">${escapeHtml(c2t('c2.listeners.malleableProfileNonHttpHint'))}</div>`;
            const emptyProfHint = (C2.profiles && C2.profiles.length > 0)
                ? ''
                : `<div class="form-hint" style="margin-bottom:6px;color:#b45309;">${escapeHtml(c2t('c2.listeners.malleableProfileEmptyListHint'))}</div>`;
            content.innerHTML = `
            <div class="c2-modal-header">
                <h3>${escapeHtml(c2t('c2.listeners.editTitle'))}</h3>
                <button class="c2-modal-close" onclick="C2.closeModal()">&times;</button>
            </div>
            <div class="c2-modal-body">
                <div class="c2-form-group">
                    <label>${escapeHtml(c2t('c2.listeners.name'))}</label>
                    <input type="text" id="c2-listener-name" class="form-control" value="${escapeHtml(l.name)}">
                </div>
                <div class="c2-form-row">
                    <div class="c2-form-group">
                        <label>${escapeHtml(c2t('c2.listeners.bindHost'))}</label>
                        <input type="text" id="c2-listener-host" class="form-control" value="${escapeHtml(String(l.bindHost))}">
                    </div>
                    <div class="c2-form-group">
                        <label>${escapeHtml(c2t('c2.listeners.bindPort'))}</label>
                        <input type="number" id="c2-listener-port" class="form-control" value="${l.bindPort}">
                    </div>
                </div>
                <div class="c2-form-group" id="c2-listener-profile-group">
                    <label>${escapeHtml(c2t('c2.listeners.malleableProfile'))}</label>
                    ${httpHint}${emptyProfHint}
                    <select id="c2-listener-profile-id" class="form-control c2-native-select">${profileOpts}</select>
                    <div class="form-hint">${escapeHtml(c2t('c2.listeners.malleableProfileHint'))}</div>
                </div>
                <div class="c2-form-group">
                    <label>${escapeHtml(c2t('c2.listeners.callbackHost'))}</label>
                    <input type="text" id="c2-listener-callback-host" class="form-control" value="${escapeHtml(cbHost)}">
                    <div class="form-hint">${escapeHtml(c2t('c2.listeners.callbackHostHint'))}</div>
                </div>
                <div class="c2-form-group">
                    <label>${escapeHtml(c2t('c2.listeners.remark'))}</label>
                    <input type="text" id="c2-listener-remark" class="form-control" value="${escapeHtml(l.remark || '')}">
                </div>
            </div>
            <div class="c2-modal-footer">
                <button class="btn-secondary" onclick="C2.closeModal()">${escapeHtml(c2t('common.cancel'))}</button>
                <button class="btn-primary" onclick="C2.saveListener('${l.id}')">${escapeHtml(c2t('common.save'))}</button>
            </div>
        `;
        }).catch(() => {
            showToast(c2t('c2.listeners.toastProfilesLoadFailed'), 'error');
            C2.closeModal();
        });
    };

    C2.saveListener = function(id) {
        const name = document.getElementById('c2-listener-name')?.value.trim();
        const bindHost = document.getElementById('c2-listener-host')?.value;
        const bindPort = parseInt(document.getElementById('c2-listener-port')?.value);
        const callbackHost = document.getElementById('c2-listener-callback-host')?.value?.trim() ?? '';
        const remark = document.getElementById('c2-listener-remark')?.value;
        const profileEl = document.getElementById('c2-listener-profile-id');
        const profileId = profileEl ? String(profileEl.value || '').trim() : '';

        apiRequest('PUT', `${API_BASE}/listeners/${id}`, {
            name, bind_host: bindHost, bind_port: bindPort, remark,
            callback_host: callbackHost,
            profile_id: profileId
        }).then(data => {
            if (data.error) showToast(data.error, 'error');
            else {
                showToast(c2t('c2.listeners.toastUpdated'), 'success');
                C2.closeModal();
                C2.loadListeners();
            }
        });
    };

    // ============================================================================
    // 会话管理
    // ============================================================================

    C2.loadSessions = function() {
        return apiRequest('GET', `${API_BASE}/sessions`).then(data => {
            C2.sessions = data.sessions || [];
            C2.renderSessions();
            C2.updateDashboardStats();
        });
    };

    C2.renderSessions = function() {
        const list = document.getElementById('c2-session-list');
        const main = document.getElementById('c2-session-main');
        if (!list) return;

        if (C2.sessions.length === 0) {
            list.innerHTML = `
                <div class="c2-empty" style="padding:40px 20px;">
                    <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="1.2" style="margin-bottom:16px;opacity:0.5;">
                        <rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
                        <line x1="8" y1="21" x2="16" y2="21"></line>
                        <line x1="12" y1="17" x2="12" y2="21"></line>
                    </svg>
                    <h3 style="font-size:16px;font-weight:700;margin-bottom:6px;">${escapeHtml(c2t('c2.sessions.emptyTitle'))}</h3>
                    <p style="font-size:13px;">${escapeHtml(c2t('c2.sessions.emptyHint'))}</p>
                </div>`;
            if (main) main.innerHTML = '';
            return;
        }

        list.innerHTML = C2.sessions.map(s => `
            <div class="c2-session-item ${s.id === C2.selectedSessionId ? 'active' : ''}" 
                 onclick="C2.selectSession('${s.id}')">
                <div class="c2-session-header">
                    <span class="c2-session-host">${escapeHtml(s.hostname || c2t('c2.sessions.unknownHost'))}</span>
                    <span class="c2-session-status ${s.status}">${escapeHtml(sessionStatusLabel(s.status))}</span>
                </div>
                <div class="c2-session-meta">
                    ${escapeHtml(s.username)} · ${s.os}/${s.arch}
                    ${s.isAdmin ? '<span style="color:#f59e0b;font-weight:700;margin-left:4px;">' + escapeHtml(c2t('c2.sessions.rootBadge')) + '</span>' : ''}
                </div>
                <div class="c2-session-meta" style="font-size:11px;margin-top:2px;">
                    ${s.internalIp || '-'} · PID ${s.pid}
                </div>
                <div class="c2-session-item-footer">
                    <span class="c2-session-meta c2-session-item-time">${formatTime(s.lastCheckIn)}</span>
                    <button type="button" class="c2-session-card-delete" onclick="event.stopPropagation(); C2.deleteSessionRecord('${s.id}');">${escapeHtml(c2t('c2.sessions.cardDeleteSession'))}</button>
                </div>
            </div>
        `).join('');

        if (C2.selectedSessionId && !C2.sessions.find(s => s.id === C2.selectedSessionId)) {
            C2.selectedSessionId = null;
        }
        if (!C2.selectedSessionId && C2.sessions.length > 0) {
            C2.selectSession(C2.sessions[0].id);
        }
    };

    C2.selectSession = function(id) {
        C2.selectedSessionId = id;
        C2.renderSessions();
        C2.renderSessionDetail(id);
        C2.initTerminal();
    };

    C2.renderSessionDetail = function(id) {
        const container = document.getElementById('c2-session-main');
        if (!container) return;

        const s = C2.sessions.find(x => x.id === id);
        if (!s) return;

        const adminVal = s.isAdmin ? c2t('c2.sessions.adminYes') : c2t('c2.sessions.adminNo');
        const sleepLine = c2t('c2.sessions.infoSleepLine', { sec: s.sleepSeconds, jitter: s.jitterPercent });
        container.innerHTML = `
            <div class="c2-session-detail">
                <div class="c2-session-header-bar">
                    <div class="c2-session-title">
                        <h3>${escapeHtml(s.hostname)} <span class="c2-session-badge ${s.status}">${escapeHtml(sessionStatusLabel(s.status))}</span></h3>
                        <div class="c2-session-subtitle">${s.id} | ${escapeHtml(s.username)}@${s.os}/${s.arch}</div>
                    </div>
                    <div class="c2-session-actions">
                        <button class="btn-secondary btn-sm" onclick="C2.setSessionSleep('${s.id}')">${escapeHtml(c2t('c2.sessions.btnSleep'))}</button>
                        <button class="btn-danger btn-sm" onclick="C2.killSession('${s.id}')">${escapeHtml(c2t('c2.sessions.kill'))}</button>
                    </div>
                </div>
                
                <div class="c2-session-tabs">
                    <div class="c2-session-tab active" data-tab="terminal" onclick="C2.switchTab('terminal')">${escapeHtml(c2t('c2.sessions.terminal'))}</div>
                    <div class="c2-session-tab" data-tab="files" onclick="C2.switchTab('files')">${escapeHtml(c2t('c2.sessions.files'))}</div>
                    <div class="c2-session-tab" data-tab="tasks" onclick="C2.switchTab('tasks')">${escapeHtml(c2t('c2.sessions.tasks'))}</div>
                    <div class="c2-session-tab" data-tab="info" onclick="C2.switchTab('info')">${escapeHtml(c2t('c2.sessions.info'))}</div>
                </div>
                
                <div class="c2-session-tab-content">
                    <div id="c2-tab-terminal" class="c2-tab-panel active">
                        <div id="c2-terminal-container" class="c2-terminal-container"></div>
                        <div class="c2-terminal-toolbar">
                            <button class="btn-ghost btn-sm" onclick="C2.clearTerminal()">${escapeHtml(c2t('c2.sessions.clearTerminal'))}</button>
                            <button class="btn-ghost btn-sm" onclick="C2.copyTerminal()">${escapeHtml(c2t('common.copy'))}</button>
                            <span class="c2-terminal-status" id="c2-terminal-status">${escapeHtml(c2t('c2.sessions.termStatusReady'))}</span>
                        </div>
                    </div>
                    <div id="c2-tab-files" class="c2-tab-panel" style="display:none;">
                        <div class="c2-file-toolbar">
                            <button class="btn-ghost btn-sm" onclick="C2.loadFileList('..')">⬆ ${escapeHtml(c2t('c2.files.parent'))}</button>
                            <button class="btn-ghost btn-sm" onclick="C2.refreshFiles()">${escapeHtml(c2t('c2.files.refresh'))}</button>
                            <span id="c2-current-path" class="c2-path-breadcrumb">/</span>
                        </div>
                        <div id="c2-file-list" class="c2-file-list"></div>
                    </div>
                    <div id="c2-tab-tasks" class="c2-tab-panel" style="display:none;">
                        <div id="c2-session-tasks-list" class="c2-task-list-compact"></div>
                    </div>
                    <div id="c2-tab-info" class="c2-tab-panel" style="display:none;">
                        <div class="c2-info-grid">
                            <div><strong>${escapeHtml(c2t('c2.sessions.infoSessionId'))}:</strong> ${s.id}</div>
                            <div><strong>${escapeHtml(c2t('c2.sessions.infoImplantUuid'))}:</strong> ${s.implantUuid}</div>
                            <div><strong>${escapeHtml(c2t('c2.sessions.infoHostname'))}:</strong> ${escapeHtml(s.hostname)}</div>
                            <div><strong>${escapeHtml(c2t('c2.sessions.infoUsername'))}:</strong> ${escapeHtml(s.username)}</div>
                            <div><strong>${escapeHtml(c2t('c2.sessions.infoOs'))}:</strong> ${s.os}</div>
                            <div><strong>${escapeHtml(c2t('c2.sessions.infoArch'))}:</strong> ${s.arch}</div>
                            <div><strong>${escapeHtml(c2t('c2.sessions.infoPid'))}:</strong> ${s.pid}</div>
                            <div><strong>${escapeHtml(c2t('c2.sessions.infoProcess'))}:</strong> ${escapeHtml(s.processName || '-')}</div>
                            <div><strong>${escapeHtml(c2t('c2.sessions.infoAdmin'))}:</strong> ${escapeHtml(adminVal)}</div>
                            <div><strong>${escapeHtml(c2t('c2.sessions.infoInternalIp'))}:</strong> ${s.internalIp || '-'}</div>
                            <div><strong>${escapeHtml(c2t('c2.sessions.infoSleep'))}:</strong> ${escapeHtml(sleepLine)}</div>
                            <div><strong>${escapeHtml(c2t('c2.sessions.infoFirstSeen'))}:</strong> ${formatTime(s.firstSeenAt)}</div>
                            <div><strong>${escapeHtml(c2t('c2.sessions.infoLastCheckin'))}:</strong> ${formatTime(s.lastCheckIn)}</div>
                            <div><strong>${escapeHtml(c2t('c2.sessions.infoNote'))}:</strong> ${escapeHtml(s.note || '-')}</div>
                        </div>
                    </div>
                </div>
            </div>
        `;

        var isCurlBeacon = s.implantUuid && s.implantUuid.startsWith('curl_');
        if (isCurlBeacon) {
            var termContainer = container.querySelector('#c2-terminal-container');
            if (termContainer) {
                termContainer.innerHTML =
                    '<div style="padding:24px;color:#94a3b8;text-align:center;line-height:1.8;">' +
                    '<div style="font-size:32px;margin-bottom:12px;">📡</div>' +
                    '<div style="font-size:14px;font-weight:600;color:#e2e8f0;margin-bottom:8px;">' + escapeHtml(c2t('c2.sessions.curlBeaconTitle')) + '</div>' +
                    '<div style="font-size:12px;">' + c2t('c2.sessions.curlBeaconBody').split('\n').map(function (ln) { return escapeHtml(ln); }).join('<br>') + '</div>' +
                    '</div>';
            }
        }
        setTimeout(() => {
            if (!isCurlBeacon) C2.initTerminal();
            C2.loadFileList(s.id, '.');
            C2.loadSessionTasks(s.id);
        }, 50);
    };

    C2.switchTab = function(tab) {
        document.querySelectorAll('.c2-session-tab').forEach(el => el.classList.remove('active'));
        document.querySelectorAll('.c2-tab-panel').forEach(el => el.style.display = 'none');
        
        const tabEl = document.querySelector(`.c2-session-tab[data-tab="${tab}"]`);
        if (tabEl) tabEl.classList.add('active');
        
        const panel = document.getElementById(`c2-tab-${tab}`);
        if (panel) panel.style.display = 'block';

        if (tab === 'terminal') {
            setTimeout(() => C2.fitTerminal(), 50);
        }
    };

    C2.setSessionSleep = function(id) {
        const sleep = prompt(c2t('c2.sessions.promptSleepSeconds'), '5');
        if (!sleep) return;
        const jitter = prompt(c2t('c2.sessions.promptJitterPercent'), '0') || '0';
        
        apiRequest('PUT', `${API_BASE}/sessions/${id}/sleep`, {
            sleep_seconds: parseInt(sleep),
            jitter_percent: parseInt(jitter)
        }).then(data => {
            if (data.error) showToast(data.error, 'error');
            else showToast(c2t('c2.sessions.toastSleepUpdated'), 'success');
        });
    };

    C2.killSession = function(id) {
        if (!confirm(c2t('c2.sessions.confirmExitSession'))) return;
        apiRequest('POST', `${API_BASE}/tasks`, {
            session_id: id,
            task_type: 'exit',
            payload: {}
        }).then(data => {
            showToast(c2t('c2.sessions.toastExitSent'), 'success');
        });
    };

    C2.deleteSessionRecord = function(id) {
        if (!confirm(c2t('c2.sessions.confirmDeleteSession'))) return;
        apiRequest('DELETE', `${API_BASE}/sessions/${id}`, {}).then(data => {
            if (data.error) {
                showToast(data.error, 'error');
                return;
            }
            showToast(c2t('c2.sessions.toastSessionDeleted'), 'success');
            if (C2.selectedSessionId === id) C2.selectedSessionId = null;
            C2.loadSessions();
        });
    };

    // ============================================================================
    // xterm 终端
    // ============================================================================

    C2.initTerminal = function() {
        const container = document.getElementById('c2-terminal-container');
        if (!container || typeof Terminal === 'undefined') return;

        if (C2.terminalInstance) {
            C2.terminalInstance.dispose();
        }

        const term = new Terminal({
            cursorBlink: true,
            cursorStyle: 'block',
            fontSize: 14,
            fontFamily: 'Menlo, Monaco, "Courier New", monospace',
            lineHeight: 1.3,
            scrollback: 5000,
            theme: {
                background: '#0d1117',
                foreground: '#e6edf3',
                cursor: '#58a6ff',
                selection: 'rgba(88, 166, 255, 0.3)'
            }
        });

        if (typeof FitAddon !== 'undefined') {
            const FitCtor = FitAddon.FitAddon || FitAddon;
            C2.terminalFitAddon = new FitCtor();
            term.loadAddon(C2.terminalFitAddon);
        }

        term.open(container);
        
        try {
            if (C2.terminalFitAddon) C2.terminalFitAddon.fit();
        } catch (e) {}

        let lineBuffer = '';
        const prompt = '$ ';
        
        term.writeln('\x1b[36m' + c2t('c2.sessions.terminalWelcome') + '\x1b[0m');
        term.writeln('');
        term.write(prompt);

        term.onData(e => {
            const code = e.charCodeAt(0);
            if (code === 13) { // Enter
                term.writeln('');
                const cmd = lineBuffer.trim();
                lineBuffer = '';
                if (cmd) {
                    C2.executeInTerminal(cmd, term);
                } else {
                    term.write(prompt);
                }
            } else if (code === 127) { // Backspace
                if (lineBuffer.length > 0) {
                    lineBuffer = lineBuffer.slice(0, -1);
                    term.write('\b \b');
                }
            } else if (code >= 32) { // Printable
                lineBuffer += e;
                term.write(e);
            }
        });

        C2.terminalInstance = term;

        // Resize observer
        if (C2.terminalResizeObserver) {
            C2.terminalResizeObserver.disconnect();
        }
        C2.terminalResizeObserver = new ResizeObserver(() => {
            C2.fitTerminal();
        });
        C2.terminalResizeObserver.observe(container);
    };

    C2.fitTerminal = function() {
        if (C2.terminalFitAddon && C2.terminalInstance) {
            try {
                C2.terminalFitAddon.fit();
            } catch (e) {}
        }
    };

    C2.executeInTerminal = function(cmd, term) {
        if (!C2.selectedSessionId) {
            term.writeln('\x1b[31m' + c2t('c2.sessions.termNoSession') + '\x1b[0m');
            term.write('$ ');
            return;
        }

        const statusEl = document.getElementById('c2-terminal-status');
        if (statusEl) statusEl.textContent = c2t('c2.sessions.termStatusExec');

        apiRequest('POST', `${API_BASE}/tasks`, {
            session_id: C2.selectedSessionId,
            task_type: 'shell',
            payload: { command: cmd, timeout_seconds: 60 }
        }).then(data => {
            if (data.error) {
                term.writeln(`\x1b[31mError: ${data.error}\x1b[0m`);
                term.write('$ ');
                if (statusEl) statusEl.textContent = c2t('c2.sessions.termStatusErr');
            } else {
                C2.waitForTaskResult(data.task?.id || data.task_id, term);
            }
        });
    };

    C2.waitForTaskResult = function(taskId, term) {
        let attempts = 0;
        const maxAttempts = 60;
        let delay = 500;
        const maxDelay = 5000;
        const check = () => {
            if (++attempts > maxAttempts) {
                term.writeln('\x1b[33m' + c2t('c2.sessions.termWaitTimeout') + '\x1b[0m');
                term.write('$ ');
                const statusEl = document.getElementById('c2-terminal-status');
                if (statusEl) statusEl.textContent = c2t('c2.sessions.termStatusTimeout');
                return;
            }
            apiRequest('GET', `${API_BASE}/tasks/${taskId}`).then(data => {
                const task = data.task;
                if (task && (task.status === 'success' || task.status === 'failed')) {
                    if (task.resultText) {
                        const lines = task.resultText.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n');
                        lines.forEach(line => term.writeln(line));
                    }
                    if (task.error) {
                        term.writeln(`\x1b[31m${task.error}\x1b[0m`);
                    }
                    term.write('$ ');
                    const statusEl = document.getElementById('c2-terminal-status');
                    if (statusEl) statusEl.textContent = c2t('c2.sessions.termStatusReady');
                } else {
                    delay = Math.min(delay * 1.5, maxDelay);
                    setTimeout(check, delay);
                }
            });
        };
        check();
    };

    C2.clearTerminal = function() {
        if (C2.terminalInstance) {
            C2.terminalInstance.clear();
            C2.terminalInstance.writeln('\x1b[36m' + c2t('c2.sessions.termCleared') + '\x1b[0m');
            C2.terminalInstance.write('$ ');
        }
    };

    C2.copyTerminal = function() {
        if (!C2.terminalInstance) return;
        const text = C2.terminalInstance.getSelection();
        if (text) copyToClipboard(text);
        else showToast(c2t('c2.sessions.termNoSelection'), 'warning');
    };

    // ============================================================================
    // 文件管理
    // ============================================================================

    C2.loadFileList = function(sessionId, path) {
        if (!sessionId) sessionId = C2.selectedSessionId;
        if (!sessionId) return;
        if (!path) path = C2.currentPath || '.';

        const container = document.getElementById('c2-file-list');
        const breadcrumb = document.getElementById('c2-current-path');
        
        if (container) container.innerHTML = '<div class="c2-loading">' + escapeHtml(c2t('c2.files.loading')) + '</div>';

        apiRequest('POST', `${API_BASE}/tasks`, {
            session_id: sessionId,
            task_type: 'ls',
            payload: { path: path }
        }).then(data => {
            if (data.error) {
                if (container) container.innerHTML = `<div class="c2-error">${data.error}</div>`;
                return;
            }
            C2.waitForFileList(data.task?.id || data.task_id, sessionId, path);
        });
    };

    C2.waitForFileList = function(taskId, sessionId, path) {
        let attempts = 0;
        const container = document.getElementById('c2-file-list');
        const check = () => {
            if (++attempts > 60) {
                if (container) container.innerHTML = '<div class="c2-error">' + escapeHtml(c2t('c2.files.timeout')) + '</div>';
                return;
            }
            apiRequest('GET', `${API_BASE}/tasks/${taskId}`).then(data => {
                const task = data.task;
                if (task && task.status === 'success') {
                    C2.currentPath = path;
                    const breadcrumb = document.getElementById('c2-current-path');
                    if (breadcrumb) breadcrumb.textContent = path;
                    C2.renderFileList(task.resultText || '');
                } else if (task && task.status === 'failed') {
                    if (container) container.innerHTML = `<div class="c2-error">${escapeHtml(task.error || c2t('c2.files.failed'))}</div>`;
                } else {
                    setTimeout(check, 500);
                }
            });
        };
        check();
    };

    C2.renderFileList = function(output) {
        const container = document.getElementById('c2-file-list');
        if (!container) return;

        const lines = output.split('\n').filter(l => l.trim());
        if (lines.length === 0) {
            container.innerHTML = '<div class="c2-empty">' + escapeHtml(c2t('c2.files.emptyDir')) + '</div>';
            return;
        }

        container.innerHTML = `
            <table class="c2-file-table">
                <thead>
                    <tr>
                        <th>${escapeHtml(c2t('c2.files.colName'))}</th>
                        <th>${escapeHtml(c2t('c2.files.colSize'))}</th>
                        <th>${escapeHtml(c2t('c2.files.colMode'))}</th>
                        <th>${escapeHtml(c2t('c2.files.colActions'))}</th>
                    </tr>
                </thead>
                <tbody>
                    ${lines.map(line => {
                        const parts = line.split(/\s+/);
                        const name = parts[parts.length - 1] || line;
                        const isDir = line.startsWith('d') || parts[0]?.startsWith?.('d');
                        return `
                            <tr>
                                <td class="c2-file-name">
                                    <span class="c2-file-icon">${isDir ? '📁' : '📄'}</span>
                                    ${escapeHtml(name)}
                                </td>
                                <td>${parts[parts.length - 5] || '-'}</td>
                                <td>${parts[parts.length - 4] || '-'}</td>
                                <td>
                                    ${isDir 
                                        ? `<button class="btn-ghost btn-sm" onclick="C2.loadFileList(null, '${escapeHtml(name)}')">${escapeHtml(c2t('c2.files.open'))}</button>`
                                        : `<button class="btn-ghost btn-sm" onclick="C2.downloadFile('${escapeHtml(name)}')">${escapeHtml(c2t('c2.files.download'))}</button>`
                                    }
                                </td>
                            </tr>
                        `;
                    }).join('')}
                </tbody>
            </table>
        `;
    };

    C2.refreshFiles = function() {
        C2.loadFileList(null, C2.currentPath);
    };

    C2.downloadFile = function(filename) {
        if (!C2.selectedSessionId) return;
        const remotePath = C2.currentPath === '/' ? '/' + filename : C2.currentPath + '/' + filename;
        
        apiRequest('POST', `${API_BASE}/tasks`, {
            session_id: C2.selectedSessionId,
            task_type: 'download',
            payload: { remote_path: remotePath }
        }).then(data => {
            if (data.error) showToast(data.error, 'error');
            else showToast(c2t('c2.payloads.toastDownloadQueued'), 'success');
        });
    };

    // ============================================================================
    // 任务管理
    // ============================================================================

    C2.loadTasks = function(page) {
        const p = page != null ? page : (C2.tasksPage || 1);
        C2.tasksPage = p;
        const ps = C2.tasksPageSize || 10;
        apiRequest('GET', `${API_BASE}/tasks?page=${encodeURIComponent(String(p))}&page_size=${encodeURIComponent(String(ps))}`).then(data => {
            if (data.error) {
                showToast(String(data.error), 'error');
                return;
            }
            C2.tasks = data.tasks || [];
            C2.tasksTotal = typeof data.total === 'number' ? data.total : (C2.tasks.length || 0);
            if (typeof data.pending_queued_count === 'number') {
                C2.tasksPendingQueuedCount = data.pending_queued_count;
            }
            const maxPage = Math.max(1, Math.ceil(C2.tasksTotal / ps));
            if (p > maxPage) {
                C2.loadTasks(maxPage);
                return;
            }
            C2.renderTasks();
            C2.renderTasksPagination();
            C2.syncTasksToolbar();
            C2.updateDashboardStats();
        }).catch(err => {
            showToast(err.message || String(err), 'error');
        });
    };

    C2.goTasksPage = function(targetPage) {
        const totalPages = Math.max(1, Math.ceil((C2.tasksTotal || 0) / (C2.tasksPageSize || 10)));
        if (targetPage < 1 || targetPage > totalPages) return;
        C2.loadTasks(targetPage);
        const list = document.getElementById('c2-task-list');
        if (list) list.scrollIntoView({ behavior: 'smooth', block: 'start' });
    };

    C2.changeTasksPageSize = function() {
        const sel = document.getElementById('c2-tasks-page-size-pagination');
        if (!sel) return;
        const n = parseInt(sel.value, 10);
        if (n > 0) {
            C2.tasksPageSize = n;
            C2.loadTasks(1);
        }
    };

    C2.renderTasksPagination = function() {
        const paginationContainer = document.getElementById('c2-tasks-pagination');
        if (!paginationContainer) return;
        const total = C2.tasksTotal || 0;
        const currentPage = C2.tasksPage || 1;
        const pageSize = C2.tasksPageSize || 10;
        const totalPages = Math.max(1, Math.ceil(total / pageSize));
        if (total === 0) {
            paginationContainer.innerHTML = '';
            return;
        }
        const start = total === 0 ? 0 : (currentPage - 1) * pageSize + 1;
        const end = Math.min(currentPage * pageSize, total);
        let html = '<div class="monitor-pagination">';
        html += `
            <div class="pagination-info">
                <span>${escapeHtml(c2t('c2.tasks.paginationShow', { start, end, total }))}</span>
                <label class="pagination-page-size">
                    ${escapeHtml(c2t('c2.tasks.paginationPerPage'))}
                    <select id="c2-tasks-page-size-pagination" onchange="C2.changeTasksPageSize()">
                        <option value="10" ${pageSize === 10 ? 'selected' : ''}>10</option>
                        <option value="20" ${pageSize === 20 ? 'selected' : ''}>20</option>
                        <option value="50" ${pageSize === 50 ? 'selected' : ''}>50</option>
                        <option value="100" ${pageSize === 100 ? 'selected' : ''}>100</option>
                    </select>
                </label>
            </div>
            <div class="pagination-controls">
                <button type="button" class="btn-secondary" onclick="C2.goTasksPage(1)" ${currentPage === 1 ? 'disabled' : ''}>${escapeHtml(c2t('c2.tasks.paginationFirst'))}</button>
                <button type="button" class="btn-secondary" onclick="C2.goTasksPage(${currentPage - 1})" ${currentPage === 1 ? 'disabled' : ''}>${escapeHtml(c2t('c2.tasks.paginationPrev'))}</button>
                <span class="pagination-page">${escapeHtml(c2t('c2.tasks.paginationPage', { current: currentPage, total: totalPages }))}</span>
                <button type="button" class="btn-secondary" onclick="C2.goTasksPage(${currentPage + 1})" ${currentPage >= totalPages ? 'disabled' : ''}>${escapeHtml(c2t('c2.tasks.paginationNext'))}</button>
                <button type="button" class="btn-secondary" onclick="C2.goTasksPage(${totalPages})" ${currentPage >= totalPages ? 'disabled' : ''}>${escapeHtml(c2t('c2.tasks.paginationLast'))}</button>
            </div>
        `;
        html += '</div>';
        paginationContainer.innerHTML = html;
        if (typeof applyTranslations === 'function') applyTranslations(paginationContainer);
    };

    C2.collectCheckedTaskIds = function() {
        return Array.from(document.querySelectorAll('.c2-task-row-check:checked')).map(cb => cb.getAttribute('data-id')).filter(Boolean);
    };

    C2.syncTasksToolbar = function() {
        const batchBtn = document.getElementById('c2-tasks-batch-delete');
        const ids = C2.collectCheckedTaskIds();
        if (batchBtn) batchBtn.disabled = ids.length === 0;
        const all = document.querySelectorAll('.c2-task-row-check');
        const selAll = document.getElementById('c2-tasks-select-all');
        if (selAll && all.length) {
            const nChecked = document.querySelectorAll('.c2-task-row-check:checked').length;
            selAll.checked = nChecked === all.length;
            selAll.indeterminate = nChecked > 0 && nChecked < all.length;
        } else if (selAll) {
            selAll.checked = false;
            selAll.indeterminate = false;
        }
    };

    C2.onTasksSelectAll = function(checked) {
        document.querySelectorAll('.c2-task-row-check').forEach(cb => { cb.checked = checked; });
        C2.syncTasksToolbar();
    };

    C2.deleteTaskById = function(id) {
        if (!id) return;
        if (!confirm(c2t('c2.tasks.confirmDeleteOne'))) return;
        apiRequest('DELETE', `${API_BASE}/tasks`, { ids: [id] }).then(data => {
            if (data.error) {
                showToast(String(data.error), 'error');
                return;
            }
            showToast(c2t('c2.tasks.toastDeleted', { n: data.deleted != null ? data.deleted : 1 }), 'success');
            C2.loadTasks(C2.tasksPage || 1);
        }).catch(err => showToast(err.message || String(err), 'error'));
    };

    C2.deleteSelectedTasks = function() {
        const ids = C2.collectCheckedTaskIds();
        if (!ids.length) {
            showToast(c2t('c2.tasks.toastSelectFirst'), 'warn');
            return;
        }
        if (!confirm(c2t('c2.tasks.confirmBatchDelete', { n: ids.length }))) return;
        apiRequest('DELETE', `${API_BASE}/tasks`, { ids }).then(data => {
            if (data.error) {
                showToast(String(data.error), 'error');
                return;
            }
            const deleted = data.deleted != null ? data.deleted : ids.length;
            showToast(c2t('c2.tasks.toastDeleted', { n: deleted }), 'success');
            C2.loadTasks(C2.tasksPage || 1);
        }).catch(err => showToast(err.message || String(err), 'error'));
    };

    C2.loadSessionTasks = function(sessionId) {
        apiRequest('GET', `${API_BASE}/tasks?session_id=${encodeURIComponent(sessionId)}&limit=50`).then(data => {
            const container = document.getElementById('c2-session-tasks-list');
            const tasks = data.tasks || [];
            if (typeof data.pending_queued_count === 'number') {
                C2.tasksPendingQueuedCount = data.pending_queued_count;
                C2.updateDashboardStats();
            }
            
            if (!container) return;
            if (tasks.length === 0) {
                container.innerHTML = '<div class="c2-empty">' + escapeHtml(c2t('c2.tasks.emptySession')) + '</div>';
                return;
            }
            
            container.innerHTML = tasks.map(t => {
                const rawId = t.id || '';
                return `
                <div class="c2-task-item-compact">
                    <span class="c2-task-status-dot ${escapeHtml(t.status || '')}"></span>
                    <span class="c2-task-type">${escapeHtml(t.taskType || '')}</span>
                    <span class="c2-task-meta">${escapeHtml(taskStatusLabel(t.status))} | ${formatDuration(t.durationMs)}</span>
                    <button type="button" class="btn-secondary btn-small" data-c2-task-action="view" data-task-id="${escapeHtml(rawId)}">${escapeHtml(c2t('c2.tasks.view'))}</button>
                </div>
            `;
            }).join('');
        });
    };

    C2.renderTasks = function() {
        const container = document.getElementById('c2-task-list');
        if (!container) return;

        const selAll = document.getElementById('c2-tasks-select-all');
        if (selAll) {
            selAll.checked = false;
            selAll.indeterminate = false;
        }

        if (C2.tasks.length === 0) {
            container.innerHTML = '<div class="c2-empty">' + escapeHtml(c2t('c2.tasks.emptyAll')) + '</div>';
            if (selAll) selAll.disabled = true;
            C2.syncTasksToolbar();
            return;
        }
        if (selAll) selAll.disabled = false;

        const delTitle = escapeHtml(c2t('c2.tasks.deleteOne'));
        container.innerHTML = `
            <table class="c2-task-table">
                <thead>
                    <tr>
                        <th class="c2-task-table-col-check"></th>
                        <th>${escapeHtml(c2t('c2.tasks.colTask'))}</th>
                        <th>${escapeHtml(c2t('c2.tasks.colSession'))}</th>
                        <th>${escapeHtml(c2t('c2.tasks.colType'))}</th>
                        <th>${escapeHtml(c2t('c2.tasks.colStatus'))}</th>
                        <th>${escapeHtml(c2t('c2.tasks.colDuration'))}</th>
                        <th>${escapeHtml(c2t('c2.tasks.colCreated'))}</th>
                        <th class="c2-task-table-col-actions">${escapeHtml(c2t('c2.tasks.colActions'))}</th>
                    </tr>
                </thead>
                <tbody>
                    ${C2.tasks.map(t => {
                        const rawId = t.id || '';
                        const shortTaskId = rawId.length > 14 ? escapeHtml(rawId.substring(0, 12)) + '\u2026' : escapeHtml(rawId);
                        const sid = t.sessionId ? escapeHtml(String(t.sessionId).substring(0, 8)) + '\u2026' : '-';
                        return `
                        <tr>
                            <td class="c2-task-table-col-check">
                                <label class="c2-task-check-label" onclick="event.stopPropagation();">
                                    <input type="checkbox" class="c2-task-row-check" data-id="${escapeHtml(rawId)}" onchange="C2.syncTasksToolbar()">
                                </label>
                            </td>
                            <td>${shortTaskId}</td>
                            <td>${sid}</td>
                            <td>${escapeHtml(t.taskType || '')}</td>
                            <td><span class="c2-status-badge ${escapeHtml(t.status || '')}">${escapeHtml(taskStatusLabel(t.status))}</span></td>
                            <td>${formatDuration(t.durationMs)}</td>
                            <td>${formatTime(t.createdAt)}</td>
                            <td class="c2-task-table-col-actions">
                                <div class="c2-task-table-actions">
                                <button type="button" class="btn-secondary btn-small" data-c2-task-action="view" data-task-id="${escapeHtml(rawId)}">${escapeHtml(c2t('c2.tasks.view'))}</button>
                                ${t.status === 'queued' || t.status === 'sent'
                                    ? `<button type="button" class="btn-danger btn-small" data-c2-task-action="cancel" data-task-id="${escapeHtml(rawId)}">${escapeHtml(c2t('c2.tasks.cancelBtn'))}</button>`
                                    : ''}
                                <button type="button" class="btn-danger btn-small" data-c2-task-action="delete" data-task-id="${escapeHtml(rawId)}" title="${delTitle}" aria-label="${delTitle}">${escapeHtml(c2t('c2.tasks.deleteBtn'))}</button>
                                </div>
                            </td>
                        </tr>
                    `;
                    }).join('')}
                </tbody>
            </table>
        `;
        C2.syncTasksToolbar();
        if (typeof applyTranslations === 'function') applyTranslations(container);
    };

    C2.viewTask = function(id) {
        const modal = document.getElementById('c2-modal');
        const content = document.getElementById('c2-modal-content');
        if (!content) return;

        const renderTaskModal = function(t) {
            if (!t || !modal) return;
            content.innerHTML = `
            <div class="c2-modal-header">
                <h3>${escapeHtml(c2t('c2.tasks.modalTitle'))}</h3>
                <button class="c2-modal-close" onclick="C2.closeModal()">&times;</button>
            </div>
            <div class="c2-modal-body">
                <div class="c2-task-detail">
                    <div><strong>${escapeHtml(c2t('c2.tasks.labelId'))}:</strong> ${escapeHtml(t.id || '')}</div>
                    <div><strong>${escapeHtml(c2t('c2.tasks.labelSession'))}:</strong> ${escapeHtml(t.sessionId || '')}</div>
                    <div><strong>${escapeHtml(c2t('c2.tasks.labelType'))}:</strong> ${escapeHtml(t.taskType || '')}</div>
                    <div><strong>${escapeHtml(c2t('c2.tasks.labelStatus'))}:</strong> <span class="c2-status-badge ${escapeHtml(t.status || '')}">${escapeHtml(taskStatusLabel(t.status))}</span></div>
                    <div><strong>${escapeHtml(c2t('c2.tasks.labelCreated'))}:</strong> ${formatTime(t.createdAt)}</div>
                    <div><strong>${escapeHtml(c2t('c2.tasks.labelSent'))}:</strong> ${formatTime(t.sentAt)}</div>
                    <div><strong>${escapeHtml(c2t('c2.tasks.labelCompleted'))}:</strong> ${formatTime(t.completedAt)}</div>
                    <div><strong>${escapeHtml(c2t('c2.tasks.labelDuration'))}:</strong> ${formatDuration(t.durationMs)}</div>
                    ${t.error ? `<div class="c2-task-error"><strong>${escapeHtml(c2t('c2.tasks.labelError'))}:</strong> ${escapeHtml(t.error)}</div>` : ''}
                    ${t.resultText ? `
                        <div class="c2-task-result">
                            <strong>${escapeHtml(c2t('c2.tasks.labelResult'))}:</strong>
                            <pre>${escapeHtml(t.resultText)}</pre>
                        </div>
                    ` : ''}
                </div>
            </div>
            <div class="c2-modal-footer">
                <button class="btn-secondary" onclick="C2.closeModal()">${escapeHtml(c2t('common.close'))}</button>
            </div>
        `;
            modal.style.display = 'flex';
        };

        const local = C2.tasks.find(x => x.id === id);
        if (local) {
            renderTaskModal(local);
            return;
        }
        apiRequest('GET', `${API_BASE}/tasks/${encodeURIComponent(id)}`).then(data => {
            if (data.error) {
                showToast(String(data.error), 'error');
                return;
            }
            if (data.task) renderTaskModal(data.task);
            else showToast(c2t('c2.tasks.emptyAll'), 'warn');
        }).catch(err => showToast(err.message || String(err), 'error'));
    };

    C2.cancelTask = function(id) {
        apiRequest('POST', `${API_BASE}/tasks/${encodeURIComponent(id)}/cancel`, {}).then(data => {
            if (data.error) showToast(String(data.error), 'error');
            else {
                showToast(c2t('c2.tasks.toastCancelled'), 'success');
                C2.loadTasks(C2.tasksPage || 1);
            }
        }).catch(err => showToast(err.message || String(err), 'error'));
    };

    // ============================================================================
    // Payload 生成
    // ============================================================================

    C2.loadListenersForPayload = function() {
        apiRequest('GET', `${API_BASE}/listeners`).then(data => {
            if (data.error) {
                showToast(data.error, 'error');
                return;
            }
            C2.listeners = data.listeners || [];
            C2.renderPayloadPage();
        }).catch(err => {
            showToast(c2t('c2.payloads.toastLoadListenersFail', { msg: err.message || '' }), 'error');
        });
    };

    var onelinerKindsByListenerType = {
        'tcp_reverse': [
            { value: 'bash',       label: 'Bash (/dev/tcp)' },
            { value: 'nc',         label: 'Netcat (-e)' },
            { value: 'nc_mkfifo',  label: 'Netcat (mkfifo)' },
            { value: 'python',     label: 'Python' },
            { value: 'perl',       label: 'Perl' },
            { value: 'powershell', label: 'PowerShell' }
        ],
        'http_beacon': [
            { value: 'curl_beacon', label: 'Curl Beacon (HTTP)' }
        ],
        'https_beacon': [
            { value: 'curl_beacon', label: 'Curl Beacon (HTTP)' }
        ],
        'websocket': [
            { value: 'curl_beacon', label: 'Curl Beacon (HTTP)' }
        ]
    };

    C2.updateOnelinerKinds = function() {
        var listenerSelect = document.getElementById('c2-payload-listener');
        var kindSelect = document.getElementById('c2-payload-kind');
        if (!listenerSelect || !kindSelect) return;

        var listenerId = listenerSelect.value;
        var listener = (C2.listeners || []).find(function(l) { return l.id === listenerId; });
        var ltype = listener ? listener.type : '';
        var kinds = onelinerKindsByListenerType[ltype] || [];

        if (kinds.length === 0) {
            kindSelect.innerHTML = '<option value="">' + escapeHtml(c2t('c2.payloads.noKindOption')) + '</option>';
        } else {
            kindSelect.innerHTML = kinds.map(function(k) {
                return '<option value="' + k.value + '">' + k.label + '</option>';
            }).join('');
        }
    };

    C2.updateLoopbackBuildHint = function() {
        const sel = document.getElementById('c2-build-listener');
        const hint = document.getElementById('c2-build-loopback-hint');
        if (!hint) return;
        const override = document.getElementById('c2-build-host') && String(document.getElementById('c2-build-host').value || '').trim();
        if (override) {
            hint.style.display = 'none';
            return;
        }
        const id = sel && sel.value;
        if (!id) {
            hint.style.display = 'none';
            return;
        }
        const l = (C2.listeners || []).find(function(x) { return x.id === id; });
        const h = (l && l.bindHost ? String(l.bindHost) : '').toLowerCase().trim();
        if (h === '127.0.0.1' || h === 'localhost' || h === '::1') {
            hint.textContent = c2t('c2.payloads.loopbackBeaconWarning');
            hint.style.display = 'block';
        } else {
            hint.style.display = 'none';
        }
    };

    C2.renderPayloadPage = function() {
        const optionsHtml = C2.listeners.length > 0
            ? C2.listeners.map(l =>
                `<option value="${l.id}">${escapeHtml(l.name)} (${l.type} ${l.bindHost}:${l.bindPort})</option>`
              ).join('')
            : '<option value="">' + escapeHtml(c2t('c2.payloads.noListenersOption')) + '</option>';

        const listenerSelect = document.getElementById('c2-payload-listener');
        if (listenerSelect) {
            listenerSelect.innerHTML = optionsHtml;
            listenerSelect.removeEventListener('change', C2.updateOnelinerKinds);
            listenerSelect.addEventListener('change', C2.updateOnelinerKinds);
        }

        const buildSelect = document.getElementById('c2-build-listener');
        if (buildSelect) {
            const listeners = C2.listeners || [];
            let buildOptionsHtml;
            if (listeners.length > 0) {
                buildOptionsHtml = listeners.map(l =>
                    `<option value="${l.id}">${escapeHtml(l.name)} (${l.type} ${l.bindHost}:${l.bindPort})</option>`
                ).join('');
            } else {
                buildOptionsHtml = '<option value="">' + escapeHtml(c2t('c2.payloads.noListenersOption')) + '</option>';
            }
            buildSelect.innerHTML = buildOptionsHtml;
            buildSelect.removeEventListener('change', C2.updateLoopbackBuildHint);
            buildSelect.addEventListener('change', C2.updateLoopbackBuildHint);
            C2.updateLoopbackBuildHint();
        }

        const buildHostInput = document.getElementById('c2-build-host');
        if (buildHostInput) {
            buildHostInput.removeEventListener('input', C2.updateLoopbackBuildHint);
            buildHostInput.addEventListener('input', C2.updateLoopbackBuildHint);
        }

        C2.updateOnelinerKinds();
        const buildBtn = document.getElementById('c2-build-btn');
        if (buildBtn && !buildBtn.disabled) buildBtn.textContent = c2t('c2.payloads.buildBeaconBtn');
        const genBtn = document.getElementById('c2-generate-oneliner-btn');
        if (genBtn) genBtn.textContent = c2t('c2.payloads.generateOnelinerBtn');
    };

    C2.generateOneliner = function() {
        const listenerId = document.getElementById('c2-payload-listener')?.value;
        const kind = document.getElementById('c2-payload-kind')?.value || 'bash';
        const host = document.getElementById('c2-payload-host')?.value;

        if (!listenerId) {
            showToast(c2t('c2.payloads.toastPickListener'), 'error');
            return;
        }

        apiRequest('POST', `${API_BASE}/payloads/oneliner`, {
            listener_id: listenerId,
            kind: kind,
            host: host
        }).then(data => {
            if (data.error) {
                showToast(data.error, 'error');
            } else {
                const output = document.getElementById('c2-oneliner-output');
                if (output) {
                    output.textContent = data.oneliner;
                    output.style.display = 'block';
                }
            }
        }).catch(err => {
            showToast(c2t('c2.payloads.toastOnelinerFail', { msg: err.message || '' }), 'error');
        });
    };

    C2.copyOneliner = function() {
        const el = document.getElementById('c2-oneliner-output');
        if (el && el.textContent) copyToClipboard(el.textContent);
    };

    C2.buildBeacon = function() {
        const listenerId = document.getElementById('c2-build-listener')?.value;
        const os = document.getElementById('c2-build-os')?.value || 'linux';
        const arch = document.getElementById('c2-build-arch')?.value || 'amd64';
        const host = document.getElementById('c2-build-host')?.value;

        if (!listenerId) {
            showToast(c2t('c2.payloads.toastPickListener'), 'error');
            return;
        }

        const btn = document.getElementById('c2-build-btn');
        if (btn) {
            btn.disabled = true;
            btn.textContent = c2t('c2.payloads.building');
        }

        apiRequest('POST', `${API_BASE}/payloads/build`, {
            listener_id: listenerId,
            os: os,
            arch: arch,
            host: host
        }).then(data => {
            if (btn) {
                btn.disabled = false;
                btn.textContent = c2t('c2.payloads.buildBeaconBtn');
            }
            if (data.error) {
                showToast(data.error, 'error');
            } else {
                showToast(c2t('c2.payloads.toastBuildSuccess', { bytes: data.payload?.size_bytes }), 'success');
                const result = document.getElementById('c2-build-result');
                if (result) {
                    result.innerHTML = `
                        <div class="c2-build-success">
                            <div>✓ ${escapeHtml(c2t('c2.payloads.buildSuccessTitle'))}</div>
                            <div>${escapeHtml(c2t('c2.payloads.buildMetaOsArch', { os: data.payload?.os, arch: data.payload?.arch }))}</div>
                            <div>${escapeHtml(c2t('c2.payloads.buildSize', { bytes: data.payload?.size_bytes }))}</div>
                            <button onclick="window.__c2DownloadPayload('${data.payload?.download_path?.split('/').pop()}')"
                               class="btn-primary" style="margin-top:8px;display:inline-block;cursor:pointer;">${escapeHtml(c2t('c2.payloads.download'))}</button>
                        </div>
                    `;
                }
            }
        }).catch(err => {
            if (btn) {
                btn.disabled = false;
                btn.textContent = c2t('c2.payloads.buildBeaconBtn');
            }
            showToast(c2t('c2.payloads.toastBuildFail', { msg: err.message || '' }), 'error');
        });
    };

    // ============================================================================
    // 事件审计
    // ============================================================================

    C2.loadEvents = function(page) {
        const p = page != null ? page : (C2.eventsPage || 1);
        C2.eventsPage = p;
        const ps = C2.eventsPageSize || 10;
        apiRequest('GET', `${API_BASE}/events?page=${encodeURIComponent(String(p))}&page_size=${encodeURIComponent(String(ps))}`).then(data => {
            if (data.error) {
                showToast(String(data.error), 'error');
                return;
            }
            C2.events = data.events || [];
            C2.eventsTotal = typeof data.total === 'number' ? data.total : (C2.events.length || 0);
            const maxPage = Math.max(1, Math.ceil(C2.eventsTotal / ps));
            if (p > maxPage) {
                C2.loadEvents(maxPage);
                return;
            }
            C2.renderEvents();
            C2.renderEventsPagination();
            C2.syncEventsToolbar();
        }).catch(err => {
            showToast(err.message || String(err), 'error');
        });
    };

    C2.goEventsPage = function(targetPage) {
        const totalPages = Math.max(1, Math.ceil((C2.eventsTotal || 0) / (C2.eventsPageSize || 10)));
        if (targetPage < 1 || targetPage > totalPages) return;
        C2.loadEvents(targetPage);
        const list = document.getElementById('c2-event-list');
        if (list) list.scrollIntoView({ behavior: 'smooth', block: 'start' });
    };

    C2.changeEventsPageSize = function() {
        const sel = document.getElementById('c2-events-page-size-pagination');
        if (!sel) return;
        const n = parseInt(sel.value, 10);
        if (n > 0) {
            C2.eventsPageSize = n;
            C2.loadEvents(1);
        }
    };

    C2.renderEventsPagination = function() {
        const paginationContainer = document.getElementById('c2-events-pagination');
        if (!paginationContainer) return;

        const total = C2.eventsTotal || 0;
        const currentPage = C2.eventsPage || 1;
        const pageSize = C2.eventsPageSize || 10;
        const totalPages = Math.max(1, Math.ceil(total / pageSize));

        if (total === 0) {
            paginationContainer.innerHTML = '';
            return;
        }

        const start = total === 0 ? 0 : (currentPage - 1) * pageSize + 1;
        const end = Math.min(currentPage * pageSize, total);

        let html = '<div class="monitor-pagination">';
        html += `
            <div class="pagination-info">
                <span>${escapeHtml(c2t('c2.events.paginationShow', { start, end, total }))}</span>
                <label class="pagination-page-size">
                    ${escapeHtml(c2t('c2.events.paginationPerPage'))}
                    <select id="c2-events-page-size-pagination" onchange="C2.changeEventsPageSize()">
                        <option value="10" ${pageSize === 10 ? 'selected' : ''}>10</option>
                        <option value="20" ${pageSize === 20 ? 'selected' : ''}>20</option>
                        <option value="50" ${pageSize === 50 ? 'selected' : ''}>50</option>
                        <option value="100" ${pageSize === 100 ? 'selected' : ''}>100</option>
                    </select>
                </label>
            </div>
            <div class="pagination-controls">
                <button type="button" class="btn-secondary" onclick="C2.goEventsPage(1)" ${currentPage === 1 ? 'disabled' : ''}>${escapeHtml(c2t('c2.events.paginationFirst'))}</button>
                <button type="button" class="btn-secondary" onclick="C2.goEventsPage(${currentPage - 1})" ${currentPage === 1 ? 'disabled' : ''}>${escapeHtml(c2t('c2.events.paginationPrev'))}</button>
                <span class="pagination-page">${escapeHtml(c2t('c2.events.paginationPage', { current: currentPage, total: totalPages }))}</span>
                <button type="button" class="btn-secondary" onclick="C2.goEventsPage(${currentPage + 1})" ${currentPage >= totalPages ? 'disabled' : ''}>${escapeHtml(c2t('c2.events.paginationNext'))}</button>
                <button type="button" class="btn-secondary" onclick="C2.goEventsPage(${totalPages})" ${currentPage >= totalPages ? 'disabled' : ''}>${escapeHtml(c2t('c2.events.paginationLast'))}</button>
            </div>
        `;
        html += '</div>';
        paginationContainer.innerHTML = html;
        if (typeof applyTranslations === 'function') applyTranslations(paginationContainer);
    };

    C2.collectCheckedEventIds = function() {
        return Array.from(document.querySelectorAll('.c2-event-check:checked')).map(cb => cb.getAttribute('data-id')).filter(Boolean);
    };

    C2.syncEventsToolbar = function() {
        const batchBtn = document.getElementById('c2-events-batch-delete');
        const ids = C2.collectCheckedEventIds();
        if (batchBtn) batchBtn.disabled = ids.length === 0;

        const all = document.querySelectorAll('.c2-event-check');
        const selAll = document.getElementById('c2-events-select-all');
        if (selAll && all.length) {
            const nChecked = document.querySelectorAll('.c2-event-check:checked').length;
            selAll.checked = nChecked === all.length;
            selAll.indeterminate = nChecked > 0 && nChecked < all.length;
        } else if (selAll) {
            selAll.checked = false;
            selAll.indeterminate = false;
        }
    };

    C2.onEventsSelectAll = function(checked) {
        document.querySelectorAll('.c2-event-check').forEach(cb => { cb.checked = checked; });
        C2.syncEventsToolbar();
    };

    C2.deleteEventById = function(id) {
        if (!id) return;
        if (!confirm(c2t('c2.events.confirmDeleteOne'))) return;
        apiRequest('DELETE', `${API_BASE}/events`, { ids: [id] }).then(data => {
            if (data.error) {
                showToast(String(data.error), 'error');
                return;
            }
            showToast(c2t('c2.events.toastDeleted', { n: data.deleted != null ? data.deleted : 1 }), 'success');
            C2.loadEvents(C2.eventsPage || 1);
        }).catch(err => showToast(err.message || String(err), 'error'));
    };

    C2.deleteSelectedEvents = function() {
        const ids = C2.collectCheckedEventIds();
        if (!ids.length) {
            showToast(c2t('c2.events.toastSelectFirst'), 'warn');
            return;
        }
        if (!confirm(c2t('c2.events.confirmBatchDelete', { n: ids.length }))) return;
        apiRequest('DELETE', `${API_BASE}/events`, { ids }).then(data => {
            if (data.error) {
                showToast(String(data.error), 'error');
                return;
            }
            const deleted = data.deleted != null ? data.deleted : ids.length;
            showToast(c2t('c2.events.toastDeleted', { n: deleted }), 'success');
            C2.loadEvents(C2.eventsPage || 1);
        }).catch(err => showToast(err.message || String(err), 'error'));
    };

    C2.renderEvents = function() {
        const container = document.getElementById('c2-event-list');
        if (!container) return;

        const selAll = document.getElementById('c2-events-select-all');
        if (selAll) {
            selAll.checked = false;
            selAll.indeterminate = false;
        }

        if (C2.events.length === 0) {
            container.innerHTML = '<div class="c2-empty">' + escapeHtml(c2t('c2.events.empty')) + '</div>';
            if (selAll) selAll.disabled = true;
            C2.syncEventsToolbar();
            return;
        }
        if (selAll) selAll.disabled = false;

        const delTitle = escapeHtml(c2t('c2.events.deleteOne'));
        container.innerHTML = C2.events.map(e => {
            const eid = escapeHtml(e.id || '');
            return `
            <div class="c2-event-item">
                <label class="c2-event-check-label" onclick="event.stopPropagation();">
                    <input type="checkbox" class="c2-event-check" data-id="${eid}" onchange="C2.syncEventsToolbar()">
                </label>
                <div class="c2-event-level ${escapeHtml(e.level || '')}"></div>
                <div class="c2-event-content">
                    <div class="c2-event-message">${escapeHtml(e.message)}</div>
                    <div class="c2-event-meta">
                        ${formatTime(e.createdAt)} · ${escapeHtml(e.category || '')}${e.sessionId ? ' · ' + escapeHtml(String(e.sessionId).substring(0, 8)) : ''}
                    </div>
                </div>
                <button type="button" class="btn-secondary c2-event-row-delete" onclick="event.stopPropagation();C2.deleteEventById('${eid}')" title="${delTitle}" aria-label="${delTitle}">🗑</button>
            </div>
        `;
        }).join('');

        C2.syncEventsToolbar();
        if (typeof applyTranslations === 'function') applyTranslations(container);
    };

    C2.connectEventStream = function() {
        if (C2.eventSource) C2.eventSource.close();

        let streamUrl = `${API_BASE}/events/stream`;
        if (typeof authToken !== 'undefined' && authToken) {
            streamUrl += `?token=${encodeURIComponent(authToken)}`;
        }
        C2.eventSource = new EventSource(streamUrl);
        C2.eventSource.onmessage = (e) => {
            try {
                const event = JSON.parse(e.data);
                C2.onEvent(event);
            } catch (err) {}
        };
        C2.eventSource.onerror = () => {
            setTimeout(() => C2.connectEventStream(), 5000);
        };
    };

    C2.onEvent = function(event) {
        if (window.currentPageId === 'c2-events' && (C2.eventsPage || 1) === 1) {
            C2.loadEvents(1);
        }

        const msg = event.message || '';
        const sessionOnline = event.category === 'session' && (
            msg.includes('上线') || msg.includes('新会话') || /new session/i.test(msg)
        );
        if (event.level === 'critical' || sessionOnline) {
            showToast(`[${event.category}] ${event.message}`, event.level === 'critical' ? 'error' : 'info');
        }

        C2.updateDashboardStats();
    };

    // ============================================================================
    // Profile 管理
    // ============================================================================

    C2.loadProfiles = function() {
        apiRequest('GET', `${API_BASE}/profiles`).then(data => {
            C2.profiles = data.profiles || [];
            C2.renderProfiles();
        });
    };

    C2.renderProfiles = function() {
        const container = document.getElementById('c2-profile-list');
        if (!container) return;

        if (C2.profiles.length === 0) {
            container.innerHTML = '<div class="c2-empty">' + escapeHtml(c2t('c2.profiles.empty')) + '</div>';
            return;
        }

        const defVal = c2t('c2.profiles.defaultValue');
        container.innerHTML = C2.profiles.map(p => `
            <div class="c2-profile-card">
                <div class="c2-profile-header">
                    <h4>${escapeHtml(p.name)}</h4>
                    <button class="btn-danger btn-sm" onclick="C2.deleteProfile('${p.id}')">${escapeHtml(c2t('common.delete'))}</button>
                </div>
                <div class="c2-profile-info">
                    <div><strong>UA:</strong> ${escapeHtml(p.userAgent || defVal)}</div>
                    <div><strong>URIs:</strong> ${escapeHtml((p.uris || []).join(', ') || defVal)}</div>
                    <div><strong>Jitter:</strong> ${p.jitterMinMs || 0}ms – ${p.jitterMaxMs || 0}ms</div>
                </div>
            </div>
        `).join('');
    };

    C2.showCreateProfileModal = function() {
        const modal = document.getElementById('c2-modal');
        const content = document.getElementById('c2-modal-content');
        if (!content) return;

        content.innerHTML = `
            <div class="c2-modal-header">
                <h3>${escapeHtml(c2t('c2.profiles.modalCreateTitle'))}</h3>
                <button class="c2-modal-close" onclick="C2.closeModal()">&times;</button>
            </div>
            <div class="c2-modal-body">
                <div class="c2-form-group">
                    <label>${escapeHtml(c2t('c2.profiles.profileNameLabel'))}</label>
                    <input type="text" id="c2-profile-name" class="form-control" placeholder="${escapeHtml(c2t('c2.profiles.placeholderProfileName'))}">
                </div>
                <div class="c2-form-group">
                    <label>${escapeHtml(c2t('c2.profiles.userAgent'))}</label>
                    <input type="text" id="c2-profile-ua" class="form-control" placeholder="Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...">
                    <div class="form-hint">${escapeHtml(c2t('c2.profiles.hintUa'))}</div>
                </div>
                <div class="c2-form-group">
                    <label>${escapeHtml(c2t('c2.profiles.labelBeaconUris'))}</label>
                    <textarea id="c2-profile-uris" class="form-control" rows="3" placeholder="/api/v1/status&#10;/cdn/health&#10;/assets/check">/api/v1/status</textarea>
                    <div class="form-hint">${escapeHtml(c2t('c2.profiles.hintUris'))}</div>
                </div>
                <div class="c2-form-row">
                    <div class="c2-form-group">
                        <label>${escapeHtml(c2t('c2.profiles.labelJitterMin'))}</label>
                        <input type="number" id="c2-profile-jmin" class="form-control" value="100" min="0">
                    </div>
                    <div class="c2-form-group">
                        <label>${escapeHtml(c2t('c2.profiles.labelJitterMax'))}</label>
                        <input type="number" id="c2-profile-jmax" class="form-control" value="500" min="0">
                    </div>
                </div>
                <div class="c2-form-group">
                    <label>${escapeHtml(c2t('c2.profiles.labelRespHeaders'))}</label>
                    <textarea id="c2-profile-headers" class="form-control" rows="3" placeholder='{"Server":"nginx","X-Powered-By":"ASP.NET"}'>{"Server":"nginx"}</textarea>
                    <div class="form-hint">${escapeHtml(c2t('c2.profiles.hintHeaders'))}</div>
                </div>
            </div>
            <div class="c2-modal-footer">
                <button class="btn-secondary" onclick="C2.closeModal()">${escapeHtml(c2t('common.cancel'))}</button>
                <button class="btn-primary" onclick="C2.createProfile()">${escapeHtml(c2t('c2.profiles.submitCreate'))}</button>
            </div>
        `;
        modal.style.display = 'flex';
    };

    C2.createProfile = function() {
        const name = document.getElementById('c2-profile-name')?.value.trim();
        if (!name) {
            showToast(c2t('c2.profiles.toastNameRequired'), 'error');
            return;
        }

        const userAgent = document.getElementById('c2-profile-ua')?.value.trim() || '';
        const urisRaw = document.getElementById('c2-profile-uris')?.value.trim() || '';
        const uris = urisRaw.split('\n').map(u => u.trim()).filter(u => u);
        const jitterMinMs = parseInt(document.getElementById('c2-profile-jmin')?.value) || 100;
        const jitterMaxMs = parseInt(document.getElementById('c2-profile-jmax')?.value) || 500;

        let responseHeaders = {};
        const headersRaw = document.getElementById('c2-profile-headers')?.value.trim();
        if (headersRaw) {
            try { responseHeaders = JSON.parse(headersRaw); }
            catch (e) { showToast(c2t('c2.profiles.toastInvalidHeadersJson'), 'error'); return; }
        }

        apiRequest('POST', `${API_BASE}/profiles`, {
            name,
            user_agent: userAgent,
            uris,
            jitter_min_ms: jitterMinMs,
            jitter_max_ms: jitterMaxMs,
            response_headers: responseHeaders
        }).then(data => {
            if (data.error) {
                showToast(data.error, 'error');
            } else {
                showToast(c2t('c2.profiles.toastCreated'), 'success');
                C2.closeModal();
                C2.loadProfiles();
            }
        });
    };

    C2.deleteProfile = function(id) {
        if (!confirm(c2t('c2.profiles.confirmDelete'))) return;
        apiRequest('DELETE', `${API_BASE}/profiles/${id}`, {}).then(data => {
            showToast(c2t('c2.profiles.toastDeleted'), 'success');
            C2.loadProfiles();
        });
    };

    // ============================================================================
    // 仪表盘
    // ============================================================================

    C2.updateDashboardStats = function() {
        const runningListeners = C2.listeners.filter(l => l.status === 'running').length;
        const activeSessions = C2.sessions.filter(s => s.status === 'active').length;
        const pendingTasks = typeof C2.tasksPendingQueuedCount === 'number'
            ? C2.tasksPendingQueuedCount
            : C2.tasks.filter(t => t.status === 'queued' || t.status === 'pending').length;

        const elListeners = document.getElementById('c2-stat-listeners');
        const elSessions = document.getElementById('c2-stat-sessions');
        const elPending = document.getElementById('c2-stat-pending');

        if (elListeners) elListeners.textContent = runningListeners;
        if (elSessions) elSessions.textContent = activeSessions;
        if (elPending) elPending.textContent = pendingTasks;
    };

    // ============================================================================
    // 模态框
    // ============================================================================

    C2.closeModal = function() {
        const modal = document.getElementById('c2-modal');
        if (modal) modal.style.display = 'none';
    };

    // ============================================================================
    // 暴露到全局
    // ============================================================================

    window.C2 = C2;

    // 页面切换监听
    window.addEventListener('pageChanged', function(e) {
        if (e.detail?.pageId?.startsWith('c2')) {
            C2.init();
        }
    });

    // DOM 加载完成后初始化
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => {
            if (window.currentPageId?.startsWith('c2')) C2.init();
        });
    } else {
        if (window.currentPageId?.startsWith('c2')) C2.init();
    }

    document.addEventListener('languagechange', function () {
        try {
            if (!window.currentPageId || !String(window.currentPageId).startsWith('c2')) return;
            if (typeof applyTranslations === 'function') applyTranslations(document);
            C2.init();
            if (C2.selectedSessionId && (window.currentPageId === 'c2-sessions')) {
                C2.renderSessions();
                C2.renderSessionDetail(C2.selectedSessionId);
            }
        } catch (e) {
            console.warn('languagechange C2 refresh failed', e);
        }
    });

})();
