<style>
    .clicd-fw-panel {
        font-size: 14px;
        color: #1f2937
    }

    .clicd-fw-section {
        border: 1px solid #e5e7eb;
        border-radius: 6px;
        background: #fff;
        padding: 12px;
        margin-top: 8px
    }

    .clicd-fw-title {
        font-weight: 600;
        margin: 18px 0 8px
    }

    .clicd-fw-title-row {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 12px;
        margin: 18px 0 8px
    }

    .clicd-fw-title-row .clicd-fw-title {
        margin: 0
    }

    .clicd-fw-muted {
        color: #6b7280
    }

    .clicd-fw-settings {
        margin-bottom: 22px
    }

    .clicd-fw-settings-head {
        display: flex;
        align-items: flex-start;
        justify-content: space-between;
        gap: 16px;
        margin-bottom: 26px
    }

    .clicd-fw-settings-title {
        font-size: 16px;
        font-weight: 700;
        color: #111827;
        line-height: 1.35
    }

    .clicd-fw-settings-actions {
        display: flex;
        justify-content: flex-end;
        align-items: center;
        gap: 10px;
        margin-top: 12px
    }

    .clicd-fw-save-hint {
        color: #6b7280;
        font-size: 13px
    }

    .clicd-fw-toggle {
        position: relative;
        display: inline-flex;
        width: 56px;
        height: 30px;
        flex: 0 0 auto;
        cursor: pointer
    }

    .clicd-fw-toggle input {
        opacity: 0;
        width: 0;
        height: 0
    }

    .clicd-fw-toggle-slider {
        position: absolute;
        inset: 0;
        background: #cfd5df;
        border-radius: 999px;
        transition: .25s
    }

    .clicd-fw-toggle-slider:before {
        content: "";
        position: absolute;
        width: 22px;
        height: 22px;
        border-radius: 50%;
        background: #fff;
        top: 4px;
        left: 4px;
        transition: .25s;
        box-shadow: 0 1px 3px rgba(15, 23, 42, .18)
    }

    .clicd-fw-toggle input:checked+.clicd-fw-toggle-slider {
        background: #10b981
    }

    .clicd-fw-toggle input:checked+.clicd-fw-toggle-slider:before {
        transform: translateX(26px)
    }

    .clicd-fw-toggle-sm {
        width: 36px;
        height: 20px
    }

    .clicd-fw-toggle-sm .clicd-fw-toggle-slider:before {
        width: 16px;
        height: 16px;
        top: 2px;
        left: 2px
    }

    .clicd-fw-toggle-sm input:checked+.clicd-fw-toggle-slider:before {
        transform: translateX(16px)
    }

    .clicd-fw-status {
        font-size: 13px;
        color: #6b7280;
        line-height: 1.5
    }

    .clicd-fw-field label {
        display: block;
        color: #6b7280;
        font-size: 12px;
        margin-bottom: 4px
    }

    .clicd-fw-input,
    .clicd-fw-select {
        width: 100%;
        height: 34px;
        border: 1px solid #d1d5db;
        border-radius: 4px;
        padding: 6px 8px;
        box-sizing: border-box
    }

    .clicd-fw-input:focus,
    .clicd-fw-select:focus {
        border-color: #2563eb;
        outline: none
    }

    .clicd-fw-field-help {
        color: #9ca3af;
        font-size: 12px;
        line-height: 1.5;
        margin-top: 6px
    }

    .clicd-fw-actions {
        display: flex;
        gap: 8px;
        flex-wrap: wrap;
        align-items: end
    }

    .clicd-fw-btn {
        height: 34px;
        border: 1px solid #2563eb;
        background: #2563eb;
        color: #fff;
        border-radius: 4px;
        padding: 0 12px;
        cursor: pointer;
        font-size: 13px
    }

    .clicd-fw-btn[disabled] {
        opacity: .6;
        cursor: not-allowed
    }

    .clicd-fw-btn-secondary {
        border-color: #d1d5db;
        background: #fff;
        color: #374151
    }

    .clicd-fw-btn-danger {
        border-color: #dc2626;
        background: #dc2626;
        color: #fff
    }

    .clicd-fw-btn-dark {
        border-color: #000;
        background: #000;
        color: #fff
    }

    .clicd-fw-btn-sm {
        height: 30px;
        padding: 0 10px;
        font-size: 12px
    }

    .clicd-fw-rules {
        margin-top: 8px;
        overflow-x: auto;
        background: #fff
    }

    .clicd-fw-table {
        width: 100%;
        min-width: 920px;
        border-collapse: collapse;
        table-layout: fixed
    }

    .clicd-fw-table th,
    .clicd-fw-table td {
        padding: 13px 14px;
        border-bottom: 1px solid #e5e7eb;
        text-align: left;
        vertical-align: middle;
        color: #111827;
        font-size: 14px;
        white-space: nowrap
    }

    .clicd-fw-table th {
        background: #f8fafc;
        color: #6b7280;
        font-weight: 600
    }

    .clicd-fw-table tbody tr:hover {
        background: #f9fafb
    }

    .clicd-fw-table td:nth-child(8) {
        white-space: normal;
        word-break: break-word
    }

    .clicd-fw-table th:last-child,
    .clicd-fw-table td:last-child {
        text-align: right
    }

    .clicd-fw-rule-direction {
        display: inline-flex;
        align-items: center;
        gap: 4px;
        padding: 2px 8px;
        border-radius: 4px;
        font-size: 12px;
        font-weight: 600
    }

    .clicd-fw-direction-in {
        background: #dbeafe;
        color: #1d4ed8
    }

    .clicd-fw-direction-out {
        background: #fef3c7;
        color: #92400e
    }

    .clicd-fw-rule-action {
        display: inline-flex;
        align-items: center;
        gap: 4px;
        padding: 2px 8px;
        border-radius: 4px;
        font-size: 12px;
        font-weight: 600
    }

    .clicd-fw-action-ACCEPT {
        background: #d1fae5;
        color: #065f46
    }

    .clicd-fw-action-DROP {
        background: #fee2e2;
        color: #991b1b
    }

    .clicd-fw-rule-network {
        display: inline-flex;
        align-items: center;
        padding: 3px 8px;
        border-radius: 4px;
        background: #f3f4f6;
        color: #374151;
        font-size: 12px;
        font-weight: 600
    }

    .clicd-fw-rule-ops {
        display: flex;
        align-items: center;
        gap: 8px;
        justify-content: flex-end
    }

    .clicd-fw-icon-btn {
        width: 30px;
        height: 30px;
        border: 0;
        border-radius: 4px;
        background: transparent;
        color: #9ca3af;
        cursor: pointer;
        font-size: 17px;
        line-height: 30px;
        padding: 0;
        text-align: center
    }

    .clicd-fw-icon-btn:hover {
        background: #f3f4f6;
        color: #374151
    }

    .clicd-fw-message {
        border: 1px solid #bfdbfe;
        background: #eff6ff;
        color: #1d4ed8;
        border-radius: 6px;
        padding: 10px 12px;
        margin-bottom: 12px;
        display: none
    }

    .clicd-fw-message.error {
        border-color: #fecaca;
        background: #fef2f2;
        color: #b91c1c
    }

    .clicd-fw-debug {
        margin-top: 12px;
        border: 1px dashed #d1d5db;
        border-radius: 6px;
        background: #f9fafb;
        padding: 10px;
        color: #374151;
        white-space: pre-wrap;
        font-size: 12px;
        display: none
    }

    .clicd-fw-modal-mask {
        position: fixed;
        inset: 0;
        background: rgba(15, 23, 42, .42);
        display: none;
        align-items: center;
        justify-content: center;
        z-index: 9999;
        padding: 16px
    }

    .clicd-fw-modal {
        width: min(420px, 100%);
        background: #fff;
        border-radius: 6px;
        border: 1px solid #e5e7eb;
        box-shadow: 0 18px 48px rgba(15, 23, 42, .22);
        padding: 16px
    }

    .clicd-fw-rule-modal {
        width: min(560px, 100%);
        max-height: calc(100vh - 48px);
        padding: 0;
        display: flex;
        flex-direction: column;
        overflow: hidden
    }

    .clicd-fw-modal-header {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 12px;
        padding: 20px 26px;
        border-bottom: 1px solid #e5e7eb
    }

    .clicd-fw-modal-close {
        width: 28px;
        height: 28px;
        border: 0;
        background: transparent;
        color: #6b7280;
        cursor: pointer;
        font-size: 28px;
        line-height: 24px;
        padding: 0
    }

    .clicd-fw-rule-modal-body {
        padding: 24px 26px 8px;
        overflow-y: auto
    }

    .clicd-fw-rule-modal-body .clicd-fw-field {
        margin-bottom: 16px
    }

    .clicd-fw-rule-modal-body .clicd-fw-input,
    .clicd-fw-rule-modal-body .clicd-fw-select {
        height: 48px;
        border-radius: 6px;
        padding: 0 14px;
        font-size: 15px
    }

    .clicd-fw-rule-modal-body .clicd-fw-field label {
        color: #4b5563;
        font-size: 14px;
        font-weight: 600;
        margin-bottom: 8px
    }

    .clicd-fw-modal-title {
        font-size: 16px;
        font-weight: 700;
        color: #111827;
        margin-bottom: 8px
    }

    .clicd-fw-modal-header .clicd-fw-modal-title {
        margin-bottom: 0
    }

    .clicd-fw-modal-body {
        font-size: 14px;
        color: #4b5563;
        line-height: 1.6;
        margin-bottom: 14px
    }

    .clicd-fw-modal-actions {
        display: flex;
        justify-content: flex-end;
        gap: 12px
    }

    .clicd-fw-rule-modal .clicd-fw-modal-actions {
        padding: 6px 26px 24px
    }

    .clicd-fw-rule-modal .clicd-fw-modal-actions .clicd-fw-btn {
        height: 44px;
        min-width: 76px;
        font-size: 14px
    }

    .clicd-fw-default-action-row {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 12px;
        border: 1px solid #e5e7eb;
        border-radius: 6px;
        background: #fff;
        padding: 12px 16px
    }

    .clicd-fw-default-title {
        font-size: 15px;
        font-weight: 700;
        color: #111827;
        line-height: 1.35
    }

    .clicd-fw-default-desc {
        color: #6b7280;
        font-size: 13px;
        line-height: 1.5
    }

    .clicd-fw-default-action-row select {
        width: auto;
        min-width: 126px;
        height: 38px;
        border: 1px solid #d1d5db;
        border-radius: 4px;
        padding: 0 12px;
        font-size: 14px;
        color: #111827;
        background: #fff
    }

    .clicd-fw-network-note {
        margin-top: 14px;
        border: 1px solid #bfdbfe;
        border-radius: 6px;
        background: #eff6ff;
        color: #1d4ed8;
        padding: 12px 16px;
        line-height: 1.6
    }

    .clicd-fw-network-note-title {
        font-weight: 700;
        margin-bottom: 2px
    }

    @media (max-width: 640px) {
        .clicd-fw-default-action-row,
        .clicd-fw-settings-actions {
            align-items: stretch;
            flex-direction: column
        }

        .clicd-fw-default-action-row select,
        .clicd-fw-settings-actions .clicd-fw-btn {
            width: 100%
        }

        .clicd-fw-title-row {
            align-items: stretch;
            flex-direction: column
        }

        .clicd-fw-title-row .clicd-fw-btn {
            width: 100%
        }

        .clicd-fw-modal-header,
        .clicd-fw-rule-modal-body,
        .clicd-fw-rule-modal .clicd-fw-modal-actions {
            padding-left: 18px;
            padding-right: 18px
        }
    }
</style>

<div class="clicd-fw-panel" id="clicd-fw-panel" data-service-id="{$service_id}" data-area-key="{$area_key}">
    <div class="clicd-fw-message" id="clicd-fw-message"></div>

    <div class="clicd-fw-settings">
        <div class="clicd-fw-settings-head">
            <div>
                <div class="clicd-fw-settings-title">防火墙</div>
                <div class="clicd-fw-status" id="clicd-fw-status-text">加载中...</div>
            </div>
            <label class="clicd-fw-toggle">
                <input type="checkbox" id="clicd-fw-enabled">
                <span class="clicd-fw-toggle-slider"></span>
            </label>
        </div>
        <div class="clicd-fw-default-action-row">
            <div>
                <div class="clicd-fw-default-title">默认动作</div>
                <div class="clicd-fw-default-desc">没有命中下方规则时如何处理</div>
            </div>
            <select class="clicd-fw-select" id="clicd-fw-default-action">
                <option value="DROP">未匹配拒绝</option>
                <option value="ACCEPT">未匹配放行</option>
            </select>
        </div>
        <div class="clicd-fw-network-note">
            <div class="clicd-fw-network-note-title">网络范围</div>
            <div>可配置：IPv4（NAT）。 IPv4 规则覆盖 IPv4 NAT 端口映射。 NAT 入站端口按容器内部端口匹配，不是宿主机公网端口。</div>
        </div>
        <div class="clicd-fw-settings-actions">
            <span class="clicd-fw-save-hint" id="clicd-fw-save-hint">当前设置已保存</span>
            <button class="clicd-fw-btn" type="button" id="clicd-fw-save-settings" disabled>保存设置</button>
        </div>
    </div>

    <div class="clicd-fw-title-row">
        <div class="clicd-fw-title">防火墙规则</div>
        <button class="clicd-fw-btn clicd-fw-btn-dark" type="button" data-clicd-fw-action="open-add-rule">+ 添加规则</button>
    </div>
    <div id="clicd-fw-rules" class="clicd-fw-rules">
        <div class="clicd-fw-section clicd-fw-muted">加载中...</div>
    </div>

    <pre class="clicd-fw-debug" id="clicd-fw-debug"></pre>

    <div class="clicd-fw-modal-mask" id="clicd-fw-add-modal">
        <div class="clicd-fw-modal clicd-fw-rule-modal">
            <div class="clicd-fw-modal-header">
                <div class="clicd-fw-modal-title" id="clicd-fw-rule-modal-title">添加规则</div>
                <button class="clicd-fw-modal-close" type="button" id="clicd-fw-add-close" aria-label="关闭">×</button>
            </div>
            <div class="clicd-fw-rule-modal-body">
                <div class="clicd-fw-field">
                    <label>网络</label>
                    <select class="clicd-fw-select" id="clicd-fw-add-network">
                        <option value="ipv4">IPv4（NAT）</option>
                        <option value="ipv6">IPv6</option>
                        <option value="all">全部</option>
                    </select>
                </div>
                <div class="clicd-fw-field">
                    <label>方向</label>
                    <select class="clicd-fw-select" id="clicd-fw-add-direction">
                        <option value="in">入站 (Inbound)</option>
                        <option value="out">出站 (Outbound)</option>
                    </select>
                </div>
                <div class="clicd-fw-field">
                    <label>协议</label>
                    <select class="clicd-fw-select" id="clicd-fw-add-protocol">
                        <option value="tcp">TCP</option>
                        <option value="udp">UDP</option>
                    </select>
                </div>
                <div class="clicd-fw-field">
                    <label>端口</label>
                    <input class="clicd-fw-input" id="clicd-fw-add-port" type="text" placeholder="如: 22 或 80,443 或 8000-9000">
                    <div class="clicd-fw-field-help">NAT 入站填容器内部端口，例如公网 22023 -> 容器 22，这里填 22</div>
                </div>
                <div class="clicd-fw-field">
                    <label>来源 IP</label>
                    <input class="clicd-fw-input" id="clicd-fw-add-source-ip" type="text" placeholder="如: 192.168.1.0/24">
                    <div class="clicd-fw-field-help">留空为任意 IPv4，支持 CIDR: 192.168.1.0/24</div>
                </div>
                <div class="clicd-fw-field">
                    <label>动作</label>
                    <select class="clicd-fw-select" id="clicd-fw-add-action">
                        <option value="DROP">拒绝 (DROP)</option>
                        <option value="ACCEPT">放行 (ACCEPT)</option>
                    </select>
                </div>
                <div class="clicd-fw-field">
                    <label>描述</label>
                    <input class="clicd-fw-input" id="clicd-fw-add-desc" type="text" placeholder="规则描述">
                </div>
            </div>
            <div class="clicd-fw-modal-actions">
                <button class="clicd-fw-btn clicd-fw-btn-secondary" type="button" id="clicd-fw-add-cancel">取消</button>
                <button class="clicd-fw-btn clicd-fw-btn-dark" type="button" id="clicd-fw-add-confirm">确定</button>
            </div>
        </div>
    </div>

    <div class="clicd-fw-modal-mask" id="clicd-fw-delete-modal">
        <div class="clicd-fw-modal">
            <div class="clicd-fw-modal-title">确认删除</div>
            <div class="clicd-fw-modal-body" id="clicd-fw-delete-text">确认删除该规则？</div>
            <div class="clicd-fw-modal-actions">
                <button class="clicd-fw-btn clicd-fw-btn-secondary" type="button"
                    id="clicd-fw-delete-cancel">取消</button>
                <button class="clicd-fw-btn clicd-fw-btn-danger" type="button" id="clicd-fw-delete-confirm">删除</button>
            </div>
        </div>
    </div>
</div>

<script>
    (function () {
        var panel = document.getElementById('clicd-fw-panel');
        if (!panel || panel.getAttribute('data-bound') === '1') return;
        panel.setAttribute('data-bound', '1');

        var message = document.getElementById('clicd-fw-message');
        var debugBox = document.getElementById('clicd-fw-debug');
        var rulesContainer = document.getElementById('clicd-fw-rules');
        var enabledCheckbox = document.getElementById('clicd-fw-enabled');
        var defaultActionSelect = document.getElementById('clicd-fw-default-action');
        var statusText = document.getElementById('clicd-fw-status-text');
        var saveSettingsButton = document.getElementById('clicd-fw-save-settings');
        var saveHint = document.getElementById('clicd-fw-save-hint');
        var addModal = document.getElementById('clicd-fw-add-modal');
        var addClose = document.getElementById('clicd-fw-add-close');
        var addCancel = document.getElementById('clicd-fw-add-cancel');
        var addConfirm = document.getElementById('clicd-fw-add-confirm');
        var ruleModalTitle = document.getElementById('clicd-fw-rule-modal-title');
        var deleteModal = document.getElementById('clicd-fw-delete-modal');
        var deleteText = document.getElementById('clicd-fw-delete-text');
        var deleteCancel = document.getElementById('clicd-fw-delete-cancel');
        var deleteConfirm = document.getElementById('clicd-fw-delete-confirm');
        var pendingDeleteRule = null;
        var currentRules = [];
        var ruleModalMode = 'add';
        var editingRuleIndex = null;
        var settingsDirty = false;
        var busy = false;

        function showMessage(type, text) {
            message.className = 'clicd-fw-message' + (type === 'error' ? ' error' : '');
            message.style.display = 'block';
            message.textContent = text || '';
        }

        function showDebug(data) {
            debugBox.style.display = 'block';
            debugBox.textContent = JSON.stringify(data || {}, null, 2);
        }

        function endpoint() {
            return "{$MODULE_CUSTOM_API}";
        }

        function serviceId() {
            return panel.getAttribute('data-service-id') || '';
        }

        function updateSaveState() {
            if (saveSettingsButton) {
                saveSettingsButton.disabled = busy || !settingsDirty;
            }
            if (saveHint) {
                saveHint.textContent = settingsDirty ? '有未保存更改，点击保存设置后生效' : '当前设置已保存';
            }
        }

        function setBusy(value) {
            busy = !!value;
            panel.querySelectorAll('button, input, select').forEach(function (el) { el.disabled = busy; });
            updateSaveState();
        }

        function markDirty(text) {
            settingsDirty = true;
            updateStatusText();
            updateSaveState();
            showMessage('success', text || '设置已修改，点击保存设置后生效');
        }

        function clearDirty() {
            settingsDirty = false;
            updateSaveState();
        }

        function escapeHtml(value) {
            return String(value)
                .replace(/&/g, '&amp;')
                .replace(/</g, '&lt;')
                .replace(/>/g, '&gt;')
                .replace(/"/g, '&quot;')
                .replace(/'/g, '&#039;');
        }

        function portDisplay(port) { return port || '所有'; }
        function sourceIpDisplay(ip) { return ip || '任意'; }
        function networkLabel(net) {
            return net === 'ipv6' ? 'IPv6' : net === 'all' ? 'ALL' : 'IPv4';
        }

        function renderRules(rules) {
            currentRules = Array.isArray(rules) ? rules : [];
            if (currentRules.length === 0) {
                rulesContainer.innerHTML = '<div class="clicd-fw-section clicd-fw-muted">暂无防火墙规则</div>';
                return;
            }
            var rows = currentRules.map(function (rule, idx) {
                var dir = (rule.direction || 'in').toLowerCase();
                var proto = (rule.protocol || 'tcp').toLowerCase();
                var rawAction = (rule.action || 'ACCEPT').toUpperCase();
                var action = rawAction === 'ACCEPT' ? 'ACCEPT' : 'DROP';
                var network = (rule.network || 'ipv4').toLowerCase();
                var port = escapeHtml(portDisplay(rule.port));
                var srcIp = escapeHtml(sourceIpDisplay(rule.source_ip));
                var desc = escapeHtml(rule.description || '');
                var enabled = rule.enabled !== false;
                var enabledChecked = enabled ? 'checked' : '';
                var dirClass = dir === 'in' ? 'clicd-fw-direction-in' : 'clicd-fw-direction-out';
                var actionClass = 'clicd-fw-action-' + action;
                return '<tr data-rule-index="' + idx + '">' +
                    '<td><label class="clicd-fw-toggle clicd-fw-toggle-sm"><input type="checkbox" class="clicd-fw-rule-enabled" ' + enabledChecked + '><span class="clicd-fw-toggle-slider"></span></label></td>' +
                    '<td><span class="clicd-fw-rule-network">' + networkLabel(network) + '</span></td>' +
                    '<td><span class="clicd-fw-rule-direction ' + dirClass + '">' + (dir === 'in' ? '入站' : '出站') + '</span></td>' +
                    '<td>' + escapeHtml(proto.toUpperCase()) + '</td>' +
                    '<td>' + port + '</td>' +
                    '<td>' + srcIp + '</td>' +
                    '<td><span class="clicd-fw-rule-action ' + actionClass + '">' + (action === 'ACCEPT' ? '放行' : '拒绝') + '</span></td>' +
                    '<td>' + (desc || '-') + '</td>' +
                    '<td><div class="clicd-fw-rule-ops">' +
                    '<button class="clicd-fw-icon-btn" type="button" data-clicd-fw-action="edit-rule" title="编辑" aria-label="编辑">&#9998;</button>' +
                    '<button class="clicd-fw-icon-btn" type="button" data-clicd-fw-action="delete-rule" title="删除" aria-label="删除">&#9003;</button>' +
                    '</div></td>' +
                    '</tr>';
            }).join('');

            rulesContainer.innerHTML = '<table class="clicd-fw-table">' +
                '<thead><tr>' +
                '<th style="width:88px">状态</th>' +
                '<th style="width:120px">网络</th>' +
                '<th style="width:120px">方向</th>' +
                '<th style="width:110px">协议</th>' +
                '<th style="width:110px">端口</th>' +
                '<th>来源/目标 IP</th>' +
                '<th style="width:120px">动作</th>' +
                '<th>描述</th>' +
                '<th style="width:110px;text-align:right">操作</th>' +
                '</tr></thead><tbody>' + rows + '</tbody></table>';

            rulesContainer.querySelectorAll('.clicd-fw-rule-enabled').forEach(function (toggle, idx) {
                toggle.addEventListener('change', function () {
                    var rule = currentRules[idx];
                    if (!rule) return;
                    rule.enabled = toggle.checked;
                    markDirty('规则开关已修改，点击保存设置后生效');
                });
            });
        }

        function setRuleField(id, value) {
            var field = document.getElementById(id);
            if (field) {
                field.value = value == null ? '' : value;
            }
        }

        function getRuleField(id, fallback) {
            var field = document.getElementById(id);
            if (!field) return fallback || '';
            return field.value;
        }

        function getRuleFormPayload(baseRule) {
            var base = baseRule || {};
            return {
                id: base.id || '',
                network: getRuleField('clicd-fw-add-network', 'ipv4'),
                direction: getRuleField('clicd-fw-add-direction', 'in'),
                protocol: getRuleField('clicd-fw-add-protocol', 'tcp'),
                port: getRuleField('clicd-fw-add-port', ''),
                source_ip: getRuleField('clicd-fw-add-source-ip', ''),
                action: getRuleField('clicd-fw-add-action', 'DROP'),
                description: getRuleField('clicd-fw-add-desc', ''),
                enabled: baseRule ? base.enabled !== false : true
            };
        }

        function clearAddForm() {
            setRuleField('clicd-fw-add-network', 'ipv4');
            setRuleField('clicd-fw-add-direction', 'in');
            setRuleField('clicd-fw-add-protocol', 'tcp');
            setRuleField('clicd-fw-add-port', '');
            setRuleField('clicd-fw-add-source-ip', '');
            setRuleField('clicd-fw-add-action', 'DROP');
            setRuleField('clicd-fw-add-desc', '');
        }

        function fillRuleForm(rule) {
            var item = rule || {};
            setRuleField('clicd-fw-add-network', item.network || 'ipv4');
            setRuleField('clicd-fw-add-direction', item.direction || 'in');
            setRuleField('clicd-fw-add-protocol', item.protocol || 'tcp');
            setRuleField('clicd-fw-add-port', item.port || '');
            setRuleField('clicd-fw-add-source-ip', item.source_ip || '');
            setRuleField('clicd-fw-add-action', item.action || 'DROP');
            setRuleField('clicd-fw-add-desc', item.description || '');
        }

        function saveFirewall() {
            var enabled = enabledCheckbox.checked;
            var defaultAction = defaultActionSelect.value;
            var rules = currentRules.map(function (r) {
                return {
                    id: r.id || '',
                    network: r.network || 'ipv4',
                    direction: r.direction || 'in',
                    protocol: r.protocol || 'tcp',
                    port: r.port || '',
                    source_ip: r.source_ip || '',
                    action: r.action || 'ACCEPT',
                    description: r.description || '',
                    enabled: r.enabled !== false
                };
            });

            var body = new URLSearchParams();
            body.set('id', serviceId());
            body.set('func', 'firewallUpdate');
            body.set('enabled', enabled ? 'true' : 'false');
            body.set('default_action', defaultAction);
            body.set('rules', JSON.stringify(rules));

            setBusy(true);
            fetch(endpoint(), {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                    'Authorization': 'JWT {$Think.get.jwt}'
                },
                credentials: 'same-origin',
                body: body.toString()
            })
                .then(function (res) { return res.text(); })
                .then(function (text) {
                    var data;
                    try { data = JSON.parse(text); } catch (e) { data = { status: 'error', msg: '非 JSON 响应: ' + text }; }
                    showDebug((data.data && data.data.debug) || data.debug || data);
                    if (data.status === 200 || data.status === 'success') {
                        showMessage('success', data.msg || '防火墙设置已更新');
                        if (data.data && Object.prototype.hasOwnProperty.call(data.data, 'enabled')) {
                            enabledCheckbox.checked = data.data.enabled === true || data.data.enabled === 'true' || data.data.enabled === 1;
                        }
                        if (data.data && data.data.default_action && defaultActionSelect) {
                            defaultActionSelect.value = data.data.default_action;
                        }
                        if (data.data && Array.isArray(data.data.rules)) {
                            currentRules = data.data.rules;
                            renderRules(currentRules);
                        }
                        clearDirty();
                        updateStatusText();
                    } else {
                        showMessage('error', data.msg || '更新失败');
                    }
                })
                .catch(function (e) {
                    showMessage('error', e.message || '请求失败');
                    showDebug({ error: String(e) });
                })
                .finally(function () {
                    setBusy(false);
                });
        }

        function loadFirewall() {
            var body = new URLSearchParams();
            body.set('id', serviceId());
            body.set('func', 'firewallList');

            setBusy(true);
            fetch(endpoint(), {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                    'Authorization': 'JWT {$Think.get.jwt}'
                },
                credentials: 'same-origin',
                body: body.toString()
            })
                .then(function (res) { return res.text(); })
                .then(function (text) {
                    var data;
                    try { data = JSON.parse(text); } catch (e) { data = { status: 'error', msg: '非 JSON 响应: ' + text }; }
                    showDebug((data.data && data.data.debug) || data.debug || data);
                    if (data.status === 200 || data.status === 'success') {
                        if (data.data) {
                            enabledCheckbox.checked = data.data.enabled === true || data.data.enabled === 'true' || data.data.enabled === 1;
                            if (data.data.default_action && defaultActionSelect) {
                                defaultActionSelect.value = data.data.default_action;
                            }
                            currentRules = Array.isArray(data.data.rules) ? data.data.rules : [];
                            renderRules(currentRules);
                            clearDirty();
                            updateStatusText();
                        }
                    } else {
                        showMessage('error', data.msg || '获取防火墙设置失败');
                        rulesContainer.innerHTML = '<div class="clicd-fw-section clicd-fw-muted">加载失败</div>';
                    }
                })
                .catch(function (e) {
                    showMessage('error', e.message || '请求失败');
                    showDebug({ error: String(e) });
                    rulesContainer.innerHTML = '<div class="clicd-fw-section clicd-fw-muted">加载失败</div>';
                })
                .finally(function () {
                    setBusy(false);
                });
        }

        function updateStatusText() {
            if (enabledCheckbox.checked) {
                statusText.textContent = '已启用，未匹配规则的流量将被' + (defaultActionSelect.value === 'DROP' ? '拒绝' : '放行');
            } else {
                statusText.textContent = '未启用时不接管该容器流量';
            }
        }

        function openDeleteModal(idx, desc) {
            pendingDeleteRule = idx;
            if (deleteText) {
                deleteText.textContent = '确认删除规则: ' + (desc || '未命名规则') + ' ?';
            }
            if (deleteModal) {
                deleteModal.style.display = 'flex';
            }
        }

        function closeDeleteModal() {
            pendingDeleteRule = null;
            if (deleteModal) {
                deleteModal.style.display = 'none';
            }
        }

        function openRuleModal(mode, idx) {
            ruleModalMode = mode === 'edit' ? 'edit' : 'add';
            editingRuleIndex = ruleModalMode === 'edit' ? idx : null;
            if (ruleModalTitle) {
                ruleModalTitle.textContent = ruleModalMode === 'edit' ? '编辑规则' : '添加规则';
            }
            if (ruleModalMode === 'edit' && currentRules[idx]) {
                fillRuleForm(currentRules[idx]);
            } else {
                clearAddForm();
            }
            if (addModal) {
                addModal.style.display = 'flex';
            }
            var portInput = document.getElementById('clicd-fw-add-port');
            if (portInput) {
                setTimeout(function () { portInput.focus(); }, 0);
            }
        }

        function closeAddModal() {
            ruleModalMode = 'add';
            editingRuleIndex = null;
            if (addModal) {
                addModal.style.display = 'none';
            }
        }

        panel.addEventListener('click', function (event) {
            var button = event.target.closest('[data-clicd-fw-action]');
            if (!button) return;
            var action = button.getAttribute('data-clicd-fw-action');

            if (action === 'open-add-rule') {
                openRuleModal('add');
                return;
            }

            var item = button.closest('[data-rule-index]');
            if (!item) return;
            var idx = parseInt(item.getAttribute('data-rule-index'), 10);
            if (isNaN(idx) || !currentRules[idx]) return;

            if (action === 'edit-rule') {
                openRuleModal('edit', idx);
                return;
            }

            if (action === 'delete-rule') {
                var desc = currentRules[idx].description || (currentRules[idx].protocol + '/' + (currentRules[idx].port || 'all'));
                openDeleteModal(idx, desc);
                return;
            }
        });

        enabledCheckbox.addEventListener('change', function () {
            updateStatusText();
            markDirty('防火墙开关已修改，点击保存设置后生效');
        });

        defaultActionSelect.addEventListener('change', function () {
            updateStatusText();
            markDirty('默认动作已修改，点击保存设置后生效');
        });

        if (saveSettingsButton) {
            saveSettingsButton.addEventListener('click', saveFirewall);
        }

        if (addClose) {
            addClose.addEventListener('click', closeAddModal);
        }
        if (addCancel) {
            addCancel.addEventListener('click', closeAddModal);
        }
        if (addModal) {
            addModal.addEventListener('click', function (event) {
                if (event.target === addModal) closeAddModal();
            });
        }
        if (addConfirm) {
            addConfirm.addEventListener('click', function () {
                if (ruleModalMode === 'edit' && editingRuleIndex !== null && currentRules[editingRuleIndex]) {
                    currentRules[editingRuleIndex] = getRuleFormPayload(currentRules[editingRuleIndex]);
                    renderRules(currentRules);
                    closeAddModal();
                    markDirty('规则已更新，点击保存设置后生效');
                    return;
                }
                currentRules.push(getRuleFormPayload(null));
                renderRules(currentRules);
                closeAddModal();
                clearAddForm();
                markDirty('规则已添加，点击保存设置后生效');
            });
        }

        if (deleteCancel) {
            deleteCancel.addEventListener('click', closeDeleteModal);
        }
        if (deleteModal) {
            deleteModal.addEventListener('click', function (event) {
                if (event.target === deleteModal) closeDeleteModal();
            });
        }
        if (deleteConfirm) {
            deleteConfirm.addEventListener('click', function () {
                if (pendingDeleteRule === null) return;
                var idx = pendingDeleteRule;
                closeDeleteModal();
                if (idx >= 0 && idx < currentRules.length) {
                    currentRules.splice(idx, 1);
                    renderRules(currentRules);
                    markDirty('规则已删除，点击保存设置后生效');
                }
            });
        }

        updateSaveState();
        loadFirewall();
    })();
</script>
