#!/usr/bin/env python3
"""
Axiom Zero — Native GTK 3 Developer Control Board & Global Swarm Intelligence
Native Linux GTK 3 application featuring real-time global swarm node ingestion (100+ nodes),
bot attack breakdown categorizer, customer profiling, subscription killswitch management,
and UNIX Domain Socket IPC (/tmp/axiom_zero_ipc.sock).
"""

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GLib, Pango
import json
import os
import sys
import time
import threading

from secure_swarm_protocol import (
    SecureSwarmProtocol,
    CUSTOMER_PROFILES_DB,
    SOCKET_PATH,
    SocketProtocol,
    SocketClient,
    MessageType
)
from gtk_ipc_listener import GTKIPCListener
from log_tail import DaemonLogTailer, EventFormatter
try:
    from pairing_dialog import RemotePairingDialog
except ImportError:
    RemotePairingDialog = None

PROD_STORE_PATH = "/media/snuffleupagus/decanter/Production Work/NawktooahhLebz_dotteck/identity_store.json"

GTK_DARK_CSS = """
/* ===================================================================
   AXIOM ZERO  //  ENTERPRISE SOC THEME  v2.5
   Black Ops dark mode — gold #D4AF37 accent system
   =================================================================== */

/* --- Base Window -------------------------------------------------- */
window {
    background-color: #060911;
    color: #F1F5F9;
    font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
    font-size: 13px;
}

/* --- Header Bar --------------------------------------------------- */
headerbar {
    background: linear-gradient(90deg, #080C16 0%, #0B0F19 60%, #080C16 100%);
    border-bottom: 2px solid rgba(212,175,55,0.35);
    color: #D4AF37;
    font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
    font-weight: 700;
    letter-spacing: 0.1em;
    min-height: 44px;
    padding: 0 8px;
    box-shadow: 0 2px 16px rgba(0,0,0,0.6);
}
headerbar title {
    color: #D4AF37;
    font-weight: 700;
    letter-spacing: 0.06em;
    font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
}
headerbar subtitle {
    color: #5A7494;
    font-size: 10px;
    letter-spacing: 0.04em;
    font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
}

/* --- KPI Cards ---------------------------------------------------- */
.kpi-card {
    background: linear-gradient(135deg, #0D1320 0%, #0B0F19 100%);
    border: 1px solid rgba(255,255,255,0.08);
    border-top: 2px solid #D4AF37;
    border-radius: 8px;
    padding: 16px;
    box-shadow: inset 0 1px 0 rgba(212,175,55,0.08),
                0 4px 16px rgba(0,0,0,0.4);
    font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
}

/* --- Rogue / Alert Banner ----------------------------------------- */
.rogue-banner {
    background-color: rgba(225,29,72,0.12);
    border: 1px solid rgba(225,29,72,0.55);
    border-left: 4px solid #E11D48;
    border-radius: 8px;
    padding: 14px 16px;
    animation: gold-pulse 2s ease-in-out infinite;
    font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
}

/* --- Gold Pulse Keyframe Animation -------------------------------- */
@keyframes gold-pulse {
    0%   { border-color: rgba(212,175,55,0.30); box-shadow: none; }
    50%  { border-color: rgba(212,175,55,0.80);
            box-shadow: 0 0 12px rgba(212,175,55,0.40),
                        inset 0 0 6px rgba(212,175,55,0.08); }
    100% { border-color: rgba(212,175,55,0.30); box-shadow: none; }
}

/* --- Buttons ------------------------------------------------------ */
button {
    background: linear-gradient(180deg, #131C2E 0%, #0D1220 100%);
    border: 1px solid rgba(255,255,255,0.12);
    border-radius: 6px;
    color: #CBD5E1;
    font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.06em;
    padding: 7px 16px;
    transition: background 0.15s ease, border-color 0.15s ease;
}
button:hover {
    background: linear-gradient(180deg, #1A2540 0%, #131C2E 100%);
    border-color: rgba(212,175,55,0.45);
    color: #F1F5F9;
}
button:active {
    background: #0A0E18;
}

button.btn-killswitch {
    background: linear-gradient(135deg, #7F1D1D 0%, #E11D48 100%);
    border: 1px solid #E11D48;
    color: #FFFFFF;
    font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
    font-weight: 800;
    letter-spacing: 0.1em;
    border-radius: 6px;
    padding: 8px 18px;
    box-shadow: 0 0 10px rgba(225,29,72,0.35);
}
button.btn-killswitch:hover {
    background: linear-gradient(135deg, #991B1B 0%, #F43F5E 100%);
    box-shadow: 0 0 18px rgba(225,29,72,0.55);
}

button.btn-action-gold {
    background: linear-gradient(135deg, #92711A 0%, #D4AF37 100%);
    border: 1px solid #D4AF37;
    color: #000000;
    font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
    font-weight: 800;
    letter-spacing: 0.08em;
    border-radius: 6px;
    padding: 7px 16px;
    box-shadow: 0 0 8px rgba(212,175,55,0.30);
}
button.btn-action-gold:hover {
    background: linear-gradient(135deg, #B8891E 0%, #F0C84A 100%);
    box-shadow: 0 0 16px rgba(212,175,55,0.55);
}

/* --- Notebook / Tabs --------------------------------------------- */
notebook > header {
    background-color: #06090F;
    border-bottom: 1px solid rgba(212,175,55,0.18);
    padding: 0 4px;
}
notebook > header > tabs > tab {
    background-color: #0B0F19;
    border-bottom: 2px solid transparent;
    color: #5A7494;
    font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.08em;
    padding: 8px 18px;
    margin: 2px 1px 0 1px;
    border-radius: 4px 4px 0 0;
    transition: color 0.15s ease, border-bottom-color 0.15s ease;
}
notebook > header > tabs > tab:hover {
    background-color: #0F1624;
    color: #94A3B8;
    border-bottom-color: rgba(212,175,55,0.35);
}
notebook > header > tabs > tab:checked {
    background-color: #0F1624;
    border-bottom-color: #D4AF37;
    color: #D4AF37;
    box-shadow: inset 0 1px 0 rgba(212,175,55,0.12);
}

/* --- TreeView / Data Tables --------------------------------------- */
treeview {
    background-color: #080C16;
    color: #CBD5E1;
    border: 1px solid rgba(255,255,255,0.07);
    font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
    font-size: 11px;
}
treeview:selected {
    background-color: rgba(212,175,55,0.18);
    color: #D4AF37;
}
treeview header button {
    background: linear-gradient(180deg, #0F1624 0%, #080C16 100%);
    border-bottom: 1px solid rgba(212,175,55,0.22);
    border-right: 1px solid rgba(255,255,255,0.06);
    color: #94A3B8;
    font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.06em;
    padding: 6px 10px;
}
treeview header button:hover {
    background: #131C2E;
    color: #D4AF37;
}

/* Threat-level row highlighting */
treeview row.threat-critical {
    background-color: rgba(225,29,72,0.18);
}
treeview row.threat-critical:selected {
    background-color: rgba(225,29,72,0.35);
}
treeview row.threat-high {
    background-color: rgba(251,146,60,0.15);
}
treeview row.threat-high:selected {
    background-color: rgba(251,146,60,0.30);
}
treeview row.threat-low {
    background-color: rgba(16,185,129,0.12);
}
treeview row.threat-low:selected {
    background-color: rgba(16,185,129,0.25);
}

/* --- Status Badge Labels ------------------------------------------ */
.status-ok       { color: #10B981; font-weight: 800; font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace; }
.status-warn     { color: #F59E0B; font-weight: 800; font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace; }
.status-critical { color: #E11D48; font-weight: 800; font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace; }

/* --- TextView / Console Stream ------------------------------------ */
textview {
    background-color: #02050A;
    border: 1px solid rgba(212,175,55,0.15);
    border-radius: 4px;
}
textview text {
    background-color: #02050A;
    color: #10B981;
    font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
    font-size: 11px;
    caret-color: #D4AF37;
}

/* --- Scrollbars --------------------------------------------------- */
scrollbar {
    background-color: #060911;
    border: none;
}
scrollbar trough {
    background-color: #060911;
    border-radius: 4px;
    margin: 2px;
}
scrollbar slider {
    background-color: rgba(212,175,55,0.28);
    border-radius: 4px;
    min-width: 6px;
    min-height: 6px;
}
scrollbar slider:hover {
    background-color: rgba(212,175,55,0.58);
}
scrollbar slider:active {
    background-color: rgba(212,175,55,0.80);
}

/* --- Separators / Rules ------------------------------------------ */
separator {
    background-color: rgba(212,175,55,0.15);
    min-height: 1px;
    min-width: 1px;
}

/* --- Labels & Typography ------------------------------------------ */
label {
    color: #CBD5E1;
    font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
}

/* --- Entry / Input Fields ----------------------------------------- */
entry {
    background-color: #0A0E1A;
    border: 1px solid rgba(255,255,255,0.12);
    border-radius: 5px;
    color: #F1F5F9;
    font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
    padding: 6px 10px;
}
entry:focus {
    border-color: rgba(212,175,55,0.55);
    box-shadow: 0 0 0 2px rgba(212,175,55,0.12);
}

/* --- Tooltip ------------------------------------------------------ */
tooltip {
    background-color: #0F1624;
    border: 1px solid rgba(212,175,55,0.35);
    border-radius: 5px;
    color: #D4AF37;
    font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
    font-size: 11px;
    padding: 6px 10px;
}

/* --- Progress Bar ------------------------------------------------- */
progressbar trough {
    background-color: #0A0E1A;
    border-radius: 4px;
    min-height: 6px;
}
progressbar progress {
    background: linear-gradient(90deg, #92711A, #D4AF37);
    border-radius: 4px;
    box-shadow: 0 0 6px rgba(212,175,55,0.40);
}
""".encode('utf-8')

FONT_MONO_BOLD_20 = Pango.FontDescription("'JetBrains Mono', 'Fira Code', 'Courier New', monospace Bold 20")
FONT_MONO_BOLD_12 = Pango.FontDescription("'JetBrains Mono', 'Fira Code', 'Courier New', monospace Bold 12")
FONT_MONO_10 = Pango.FontDescription("'JetBrains Mono', 'Fira Code', 'Courier New', monospace 10")

class AxiomDevControlGTK(Gtk.Window):
    def __init__(self):
        super().__init__(title="AXIOM ZERO // DEV CONTROL CENTER")
        self.set_default_size(1340, 840)
        self.set_position(Gtk.WindowPosition.CENTER)

        if os.environ.get("NOCTUA_HEADLESS_TEST") == "1" or not os.environ.get("DISPLAY"):
            self.license_key = os.environ.get("NOCTUA_LICENSE_KEY", "NOCTUA_GOD_MODE_777")
        else:
            # License Key Prompt
            dialog = Gtk.MessageDialog(
                transient_for=self,
                flags=0,
                message_type=Gtk.MessageType.QUESTION,
                buttons=Gtk.ButtonsType.OK_CANCEL,
                text="ENTER NOCTUA CLOUD HUB LICENSE KEY"
            )
            entry = Gtk.Entry()
            entry.set_text(os.environ.get("NOCTUA_LICENSE_KEY", "NOCTUA_GOD_MODE_777"))
            
            box = dialog.get_content_area()
            box.pack_start(entry, True, True, 0)
            box.show_all()
            
            response = dialog.run()
            if response == Gtk.ResponseType.OK:
                self.license_key = entry.get_text().strip()
            else:
                self.license_key = os.environ.get("NOCTUA_LICENSE_KEY", "NOCTUA_GOD_MODE_777")
            dialog.destroy()

        if not self.license_key:
            self.license_key = "NOCTUA_GOD_MODE_777"

        # Load Custom GTK CSS Theme
        css_provider = Gtk.CssProvider()
        css_provider.load_from_data(GTK_DARK_CSS)
        Gtk.StyleContext.add_provider_for_screen(
            Gdk.Screen.get_default(),
            css_provider,
            Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
        )

        # HeaderBar Setup
        header = Gtk.HeaderBar()
        header.set_show_close_button(True)
        header.set_title("AXIOM ZERO // DEV CONTROL CENTER")
        
        mode_str = "GOD MODE (Global Telemetry)" if self.license_key == "NOCTUA_GOD_MODE_777" else f"CUSTOMER MODE ({self.license_key})"
        header.set_subtitle(f"Noctua Cloud Hub Connected | {mode_str}")
        self.set_titlebar(header)

        # Header Badges
        self.lbl_ipc_status = Gtk.Label(label="⚡ CLOUD HUB: INITIALIZING...")
        self.lbl_ipc_status.get_style_context().add_class("kpi-card")
        header.pack_end(self.lbl_ipc_status)
        
        btn_pair = Gtk.Button(label="🔗 Pair Remote Server")
        btn_pair.connect("clicked", self._on_pair_remote_server)
        btn_pair.get_style_context().add_class("btn-action-gold")
        header.pack_end(btn_pair)

        self.lbl_cred_audit = Gtk.Label(label="🔑 Credential Audit: OK")
        header.pack_start(self.lbl_cred_audit)

        # Main Layout Box
        main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=16)
        main_box.set_margin_top(16)
        main_box.set_margin_bottom(16)
        main_box.set_margin_start(16)
        main_box.set_margin_end(16)
        self.add(main_box)

        # Notebook (Tabbed Interface)
        notebook = Gtk.Notebook()
        main_box.pack_start(notebook, True, True, 0)

        # TAB 1: Global 100-Node Swarm Telemetry
        page_swarm = self._build_swarm_tab()
        notebook.append_page(page_swarm, Gtk.Label(label="🌐 Swarm Telemetry (100 Nodes)"))

        # TAB 2: Customer Intelligence, Billing & Subscription Management
        page_customers = self._build_customers_tab()
        notebook.append_page(page_customers, Gtk.Label(label="👤 Customer Profiles & Subscriptions"))

        # TAB 3: Anti-Piracy & Leak Alert Monitor
        page_piracy = self._build_piracy_tab()
        notebook.append_page(page_piracy, Gtk.Label(label="🚨 Anti-Piracy & Rogue Leaks"))

        # TAB 4: Unix Domain Socket IPC Console Stream
        page_stream = self._build_stream_tab()
        notebook.append_page(page_stream, Gtk.Label(label="📡 Unix Socket Telemetry Stream"))

        # TAB 5: Threat Map
        page_threat_map = self._build_threat_map_tab()
        notebook.append_page(page_threat_map, Gtk.Label(label="🗺️ Threat Map"))

        # Single Cloud Hub listener
        self.ipc_listeners = []
        self._active_connections = 0
        
        cfg = {
            "server_host": os.environ.get("NOCTUA_HUB_HOST", "127.0.0.1"),
            "server_port": int(os.environ.get("NOCTUA_HUB_PORT", 7443)),
            "license_key": self.license_key
        }
        
        listener = GTKIPCListener(
            cfg=cfg,
            on_event_callback=self._on_ipc_event,
            on_connect_callback=self._on_ipc_connected,
            on_disconnect_callback=self._on_ipc_disconnected
        )
        self.ipc_listeners.append(listener)
        listener.start()
        
        # Single consolidated cleanup handler on destroy
        self.connect('destroy', self._on_destroy)

    def _on_destroy(self, widget):
        """Unified window cleanup method."""
        for listener in self.ipc_listeners:
            try:
                listener.stop()
            except Exception:
                pass
        if hasattr(self, '_log_tailer') and self._log_tailer:
            try:
                self._log_tailer.stop()
            except Exception:
                pass
        if Gtk.main_level() > 0:
            Gtk.main_quit()

    def send_killswitch_event(self, target_id: str, company: str, reason: str = "Admin Manual Revocation") -> bool:
        """Sends EVENT_KILLSWITCH JSON frame down /tmp/axiom_zero_ipc.sock."""
        event = {
            "type": MessageType.EVENT_KILLSWITCH.value,
            "timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ"),
            "client_id": target_id,
            "payload": {
                "target_id": target_id,
                "company_name": company,
                "action": "REVOKE_SUBSCRIPTION_KILLSWITCH",
                "reason": reason,
                "status": "QUARANTINED"
            }
        }
        success = SecureSwarmProtocol.send_event_to_daemon(event)
        log_line = EventFormatter.format(event) + f" | Target: {target_id} ({company})\n"
        GLib.idle_add(self._append_to_stream, log_line)
        return success

    def _build_swarm_tab(self):
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=16)

        # Top KPI Metrics Grid
        kpi_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=16)
        kpi_box.set_homogeneous(True)

        card1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4)
        card1.get_style_context().add_class("kpi-card")
        card1.add(Gtk.Label(label="ACTIVE GLOBAL NODES"))
        self.lbl_active_nodes = Gtk.Label(label="100 Nodes")
        self.lbl_active_nodes.modify_font(FONT_MONO_BOLD_20)
        card1.add(self.lbl_active_nodes)
        kpi_box.add(card1)

        card2 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4)
        card2.get_style_context().add_class("kpi-card")
        card2.add(Gtk.Label(label="ETHICAL ATTACKS INGESTED"))
        self.lbl_total_attacks = Gtk.Label(label="5,000 Payloads")
        self.lbl_total_attacks.modify_font(FONT_MONO_BOLD_20)
        card2.add(self.lbl_total_attacks)
        kpi_box.add(card2)

        card3 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4)
        card3.get_style_context().add_class("kpi-card")
        card3.add(Gtk.Label(label="NEUTRALIZATION BLOCK RATE"))
        self.lbl_block_rate = Gtk.Label(label="99.9400%")
        self.lbl_block_rate.modify_font(FONT_MONO_BOLD_20)
        card3.add(self.lbl_block_rate)
        kpi_box.add(card3)

        vbox.pack_start(kpi_box, False, False, 0)

        # Swarm Nodes Table
        lbl_table_header = Gtk.Label(label="LIVE GLOBAL 100-CLIENT TELEMETRY FEED (SORTED BY REGION & TIER)")
        lbl_table_header.set_xalign(0)
        lbl_table_header.modify_font(FONT_MONO_BOLD_12)
        vbox.pack_start(lbl_table_header, False, False, 0)

        # Store: [Timestamp, Client Node ID, Domain, IP & Region, Bot Attack Tier, Layer Triggered, Outcome, Latency]
        self.swarm_store = Gtk.ListStore(str, str, str, str, str, str, str, str)
        self._load_telemetry_store()

        tree_view = Gtk.TreeView(model=self.swarm_store)
        cols = ["Timestamp", "Client ID", "Customer Domain", "IP & Region", "Bot Attack Tier", "Detection Layer", "Outcome", "Latency"]
        for i, col_title in enumerate(cols):
            renderer = Gtk.CellRendererText()
            column = Gtk.TreeViewColumn(col_title, renderer, text=i)
            column.set_resizable(True)
            column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
            column.set_expand(True)
            tree_view.append_column(column)

        scroll = Gtk.ScrolledWindow()
        scroll.add(tree_view)
        vbox.pack_start(scroll, True, True, 0)

        return vbox

    def _load_telemetry_store(self):
        self.swarm_store.clear()
        if os.path.exists(PROD_STORE_PATH):
            try:
                with open(PROD_STORE_PATH, "r", encoding="utf-8") as f:
                    data = json.load(f)
                    records = data.get("history", [])
                    for r in records[:100]: # Display top 100 global records
                        self.swarm_store.append([
                            r.get("timestamp", "Just now"),
                            r.get("client_id", "node_001"),
                            r.get("domain", "enterprise.com"),
                            f"{r.get('connection_ip', '127.0.0.1')} ({r.get('region', 'US-East')})",
                            r.get("bot_attack_tier", "TIER_1"),
                            r.get("triggered_detection_layer", "Layer 12"),
                            r.get("outcome", "BLOCKED_403"),
                            f"{r.get('edge_latency_ms', 1.2)}ms"
                        ])
            except Exception:
                pass

    def _update_swarm_store_from_event(self, event: dict):
        """Prepend new event to swarm_store without disk I/O, maintaining max 100 rows."""
        try:
            ts = event.get("timestamp", time.strftime("%H:%M:%S"))
            if isinstance(ts, str) and "T" in ts:
                ts = ts.split("T")[1].split(".")[0].rstrip("Z")
            
            cid = event.get("client_id", "node_001")
            payload = event.get("payload", {})
            domain = payload.get("domain", payload.get("customer_domain", "enterprise.com"))
            ip = payload.get("ip", payload.get("connection_ip", "127.0.0.1"))
            region = payload.get("region", "US-East")
            ip_region = f"{ip} ({region})"
            tier = payload.get("bot_attack_tier", payload.get("bot_class", "TIER_1"))
            layer = payload.get("triggered_detection_layer", payload.get("detection_layer", "Layer 12"))
            
            etype = event.get("type", "")
            if etype == "EVENT_BLOCK":
                outcome = payload.get("outcome", "BLOCKED_403")
            elif etype == "EVENT_ALLOW":
                outcome = payload.get("outcome", "ALLOWED_200")
            elif etype == "EVENT_KILLSWITCH":
                outcome = "QUARANTINED_KILLSWITCH"
            else:
                outcome = payload.get("outcome", etype)

            latency_val = payload.get("latency_ms", payload.get("edge_latency_ms", 1.2))
            latency = f"{latency_val:.1f}ms" if isinstance(latency_val, (int, float)) else str(latency_val)

            # Prepend to ListStore
            self.swarm_store.insert(0, [ts, cid, domain, ip_region, tier, layer, outcome, latency])

            # Cap ListStore size at 100 rows
            while len(self.swarm_store) > 100:
                last_iter = self.swarm_store.iter_nth_child(None, 100)
                if last_iter:
                    self.swarm_store.remove(last_iter)
                else:
                    break
        except Exception:
            pass
        return False

    def _build_customers_tab(self):
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=16)

        top_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=16)
        lbl_title = Gtk.Label(label="CUSTOMER ACCOUNTS, SUBSCRIPTION MANAGEMENT & ATTESTATION TELEMETRY")
        lbl_title.modify_font(FONT_MONO_BOLD_12)
        top_box.pack_start(lbl_title, True, True, 0)

        btn_audit_now = Gtk.Button(label="🔑 RUN CREDENTIAL AUDIT NOW")
        btn_audit_now.get_style_context().add_class("btn-action-gold")
        btn_audit_now.connect("clicked", self._on_manual_credential_audit)
        top_box.pack_end(btn_audit_now, False, False, 0)

        vbox.pack_start(top_box, False, False, 0)

        # Dynamic KPI Row for Customer & MRR Metrics
        kpi_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=16)
        kpi_box.set_homogeneous(True)

        card1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4)
        card1.get_style_context().add_class("kpi-card")
        card1.add(Gtk.Label(label="ACTIVE CUSTOMER ACCOUNTS"))
        self.lbl_active_cust_count = Gtk.Label(label="0 Active")
        self.lbl_active_cust_count.modify_font(FONT_MONO_BOLD_20)
        card1.add(self.lbl_active_cust_count)
        kpi_box.add(card1)

        card2 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4)
        card2.get_style_context().add_class("kpi-card")
        card2.add(Gtk.Label(label="TOTAL DYNAMIC ACTIVE MRR"))
        self.lbl_total_mrr = Gtk.Label(label="$0 / mo")
        self.lbl_total_mrr.modify_font(FONT_MONO_BOLD_20)
        card2.add(self.lbl_total_mrr)
        kpi_box.add(card2)

        vbox.pack_start(kpi_box, False, False, 0)

        # Store: [Company / Account ID, Service Username, Credential Status, Billing Tier & MRR, Incoming IPs & Geolocation, Hardware Profile, Monthly Verifications]
        self.cust_store = Gtk.ListStore(str, str, str, str, str, str, str)

        self.cust_tree_view = Gtk.TreeView(model=self.cust_store)
        cols = ["Company / Account ID", "Service Username", "Credential Status", "Billing Tier & MRR", "Active IPs & Location", "Hardware Attestation", "Monthly Verifications"]
        for i, col_title in enumerate(cols):
            renderer = Gtk.CellRendererText()
            column = Gtk.TreeViewColumn(col_title, renderer, text=i)
            column.set_resizable(True)
            column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
            column.set_expand(True)
            self.cust_tree_view.append_column(column)

        self._populate_customer_store()

        scroll = Gtk.ScrolledWindow()
        scroll.add(self.cust_tree_view)
        vbox.pack_start(scroll, True, True, 0)

        # Customer Detail Box
        self.lbl_profile_detail = Gtk.Label(label="Select a customer account above to inspect hardware attestation, risk metrics, and subscription controls.")
        self.lbl_profile_detail.set_xalign(0)
        self.lbl_profile_detail.get_style_context().add_class("kpi-card")
        vbox.pack_start(self.lbl_profile_detail, False, False, 0)

        # Interactive Subscription Management Action Bar
        action_bar = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=16)
        
        btn_revoke_sub = Gtk.Button(label="⚡ REVOKE SUBSCRIPTION / KILLSWITCH")
        btn_revoke_sub.get_style_context().add_class("btn-killswitch")
        btn_revoke_sub.connect("clicked", self._on_revoke_subscription_clicked)
        action_bar.pack_start(btn_revoke_sub, False, False, 0)

        btn_restore_sub = Gtk.Button(label="🔄 RESTORE SUBSCRIPTION")
        btn_restore_sub.get_style_context().add_class("btn-action-gold")
        btn_restore_sub.connect("clicked", self._on_restore_subscription_clicked)
        action_bar.pack_start(btn_restore_sub, False, False, 0)

        vbox.pack_start(action_bar, False, False, 0)

        self.cust_tree_view.get_selection().connect("changed", self._on_customer_selected)

        return vbox

    def _update_mrr_summary(self):
        """Dynamically computes total MRR and active customer count from customer profiles."""
        profiles = SecureSwarmProtocol.get_all_customer_profiles()
        active_count = 0
        total_mrr = 0
        for p in profiles.values():
            if "VERIFIED" in p.get('credential_status', ''):
                active_count += 1
                mrr_val = p.get('mrr', 0)
                if isinstance(mrr_val, (int, float)):
                    total_mrr += mrr_val
        if hasattr(self, 'lbl_total_mrr') and self.lbl_total_mrr:
            self.lbl_total_mrr.set_text(f"${total_mrr:,} / mo")
        if hasattr(self, 'lbl_active_cust_count') and self.lbl_active_cust_count:
            self.lbl_active_cust_count.set_text(f"{active_count} Active")

    def _populate_customer_store(self):
        self.cust_store.clear()
        profiles = SecureSwarmProtocol.get_all_customer_profiles()
        for cust_id, p in profiles.items():
            mrr_str = f"${p['mrr']:,}/mo" if isinstance(p['mrr'], (int, float)) else str(p['mrr'])
            self.cust_store.append([
                f"{p['company_name']} ({cust_id})",
                p['service_user'],
                f"{p['credential_status']} ({p['last_cred_check']})",
                f"{p['tier']} [MRR: {mrr_str}]",
                " | ".join(p.get('locations', p.get('active_ips', []))),
                p.get('hardware_profile', 'Verified Enclave'),
                f"{p['monthly_verifications']:,}"
            ])
        self._update_mrr_summary()

    def _on_customer_selected(self, selection):
        model, treeiter = selection.get_selected()
        if treeiter:
            company_col = model[treeiter][0]
            profiles = SecureSwarmProtocol.get_all_customer_profiles()
            for cust_id, p in profiles.items():
                if cust_id in company_col or p['company_name'] in company_col:
                    mrr_str = f"${p['mrr']:,}/mo" if isinstance(p['mrr'], (int, float)) else str(p['mrr'])
                    detail_text = (
                        f"ACCOUNT PROFILE: {p['company_name']} [{cust_id}]\n"
                        f"• Service Username: {p['service_user']} | Credential Verification: {p['credential_status']}\n"
                        f"• Incoming IPs & Geolocation: {', '.join(p['active_ips'])}\n"
                        f"• Hardware Attestation Signature: {p['hardware_profile']}\n"
                        f"• Calculated Risk Score: {p['risk_score']} | Billing MRR: {mrr_str}\n"
                        f"• Subscription Status: {'ACTIVE' if 'VERIFIED' in p['credential_status'] else 'REVOKED / SUSPENDED'}"
                    )
                    self.lbl_profile_detail.set_text(detail_text)
                    return

    def _on_revoke_subscription_clicked(self, widget):
        selection = self.cust_tree_view.get_selection()
        model, treeiter = selection.get_selected()
        if not treeiter:
            # Fallback: prompt to select account or pick first active account
            dialog = Gtk.MessageDialog(
                transient_for=self,
                flags=0,
                message_type=Gtk.MessageType.INFO,
                buttons=Gtk.ButtonsType.OK,
                text="SELECT A CUSTOMER ACCOUNT"
            )
            dialog.format_secondary_text("Please select a customer account row from the table above before triggering a subscription revocation.")
            dialog.run()
            dialog.destroy()
            return

        company_col = model[treeiter][0]
        profiles = SecureSwarmProtocol.get_all_customer_profiles()
        target_id = None
        target_profile = None

        for cust_id, p in profiles.items():
            if cust_id in company_col or p['company_name'] in company_col:
                target_id = cust_id
                target_profile = p
                break

        if not target_id or not target_profile:
            return

        company_name = target_profile['company_name']

        # Confirmation Dialog before executing REVOKE SUBSCRIPTION / KILLSWITCH
        confirm_dialog = Gtk.MessageDialog(
            transient_for=self,
            flags=Gtk.DialogFlags.MODAL,
            message_type=Gtk.MessageType.WARNING,
            buttons=Gtk.ButtonsType.OK_CANCEL,
            text="⚡ CONFIRM SUBSCRIPTION REVOCATION & KILLSWITCH"
        )
        confirm_dialog.format_secondary_text(
            f"Are you sure you want to revoke subscription and trigger killswitch for:\n\n"
            f"  • Target Account: {company_name} [{target_id}]\n\n"
            f"This action will invalidate customer credentials and dispatch an encrypted "
            f"EVENT_KILLSWITCH frame down Unix Domain Socket ({SOCKET_PATH}) to quarantine node access."
        )
        response = confirm_dialog.run()
        confirm_dialog.destroy()

        if response != Gtk.ResponseType.OK:
            return

        # Dispatch EVENT_KILLSWITCH frame down Unix Domain Socket
        self.send_killswitch_event(target_id, company_name, reason="Admin Subscription Revocation / Killswitch")

        # Mutate in-memory profile state
        target_profile['credential_status'] = "REVOKED_KILLSWITCH_ACTIVE"
        target_profile['tier'] = "REVOKED ($0/mo)"
        target_profile['mrr'] = 0
        target_profile['last_cred_check'] = time.strftime("%H:%M:%S")

        # Refresh TreeView & Detail view
        self._populate_customer_store()
        self._on_customer_selected(selection)

        dialog = Gtk.MessageDialog(
            transient_for=self,
            flags=0,
            message_type=Gtk.MessageType.WARNING,
            buttons=Gtk.ButtonsType.OK,
            text="⚡ SUBSCRIPTION REVOKED & KILLSWITCH SENT"
        )
        dialog.format_secondary_text(
            f"Target Account: {company_name} [{target_id}]\n"
            f"Action: EVENT_KILLSWITCH frame dispatched over Unix Domain Socket ({SOCKET_PATH}).\n"
            f"Result: Credentials invalidated. Swarm nodes instructed to isolate and drop all requests from this account."
        )
        dialog.run()
        dialog.destroy()

    def _on_restore_subscription_clicked(self, widget):
        selection = self.cust_tree_view.get_selection()
        model, treeiter = selection.get_selected()
        if not treeiter:
            return

        company_col = model[treeiter][0]
        profiles = SecureSwarmProtocol.get_all_customer_profiles()
        target_id = None
        target_profile = None

        for cust_id, p in profiles.items():
            if cust_id in company_col or p['company_name'] in company_col:
                target_id = cust_id
                target_profile = p
                break

        if not target_id or not target_profile:
            return

        company_name = target_profile['company_name']
        target_profile['credential_status'] = "VERIFIED_OK"
        if "acme" in target_id:
            target_profile['tier'] = "Advanced Defense ($5,950/mo)"
            target_profile['mrr'] = 5950
        elif "fintech" in target_id:
            target_profile['tier'] = "Growth Infrastructure ($2,450/mo)"
            target_profile['mrr'] = 2450
        elif "apex" in target_id:
            target_profile['tier'] = "Enterprise Sovereign ($12,500/mo)"
            target_profile['mrr'] = 12500
        elif "nexus" in target_id:
            target_profile['tier'] = "Tier-1 Core Engine ($8,200/mo)"
            target_profile['mrr'] = 8200
        else:
            target_profile['tier'] = "Standard Tier ($1,500/mo)"
            target_profile['mrr'] = 1500

        target_profile['last_cred_check'] = time.strftime("%H:%M:%S")
        self._populate_customer_store()
        self._on_customer_selected(selection)

        # Notify via socket
        audit_event = {
            "type": MessageType.EVENT_CRED_AUDIT.value,
            "timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ"),
            "client_id": target_id,
            "payload": {
                "status": "VERIFIED_OK",
                "action": "SUBSCRIPTION_RESTORED",
                "company_name": company_name
            }
        }
        SecureSwarmProtocol.send_event_to_daemon(audit_event)
        log_line = EventFormatter.format(audit_event) + f" | Restored: {company_name}\n"
        GLib.idle_add(self._append_to_stream, log_line)

    def _on_manual_credential_audit(self, widget):
        SecureSwarmProtocol.audit_customer_credentials()
        self._populate_customer_store()
        ts = time.strftime("%H:%M:%S")
        self.lbl_cred_audit.set_text(f"🔑 Credential Audit OK ({ts})")

    def _build_piracy_tab(self):
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=16)

        kpi_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=16)
        kpi_box.set_homogeneous(True)

        card1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4)
        card1.get_style_context().add_class("kpi-card")
        card1.add(Gtk.Label(label="AUTHORIZED DEPLOYMENTS"))
        self.lbl_auth_count = Gtk.Label(label="100 Nodes")
        self.lbl_auth_count.modify_font(FONT_MONO_BOLD_20)
        card1.add(self.lbl_auth_count)
        kpi_box.add(card1)

        card2 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4)
        card2.get_style_context().add_class("rogue-banner")
        card2.add(Gtk.Label(label="UNAUTHORIZED ROGUE COPIES"))
        self.lbl_rogue_count = Gtk.Label(label="1 DETECTED")
        self.lbl_rogue_count.modify_font(FONT_MONO_BOLD_20)
        card2.add(self.lbl_rogue_count)
        kpi_box.add(card2)

        vbox.pack_start(kpi_box, False, False, 0)

        rogue_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=16)
        rogue_box.get_style_context().add_class("rogue-banner")
        
        lbl_alert = Gtk.Label(label="🚨 ALERT: Unauthorized instance 'rogue_inst_09' pinging from http://unauthorized-leak-mirror.org (IP: 45.142.214.88)")
        lbl_alert.set_xalign(0)
        rogue_box.pack_start(lbl_alert, True, True, 0)

        btn_kill = Gtk.Button(label="⚡ TRIGGER REMOTE KILLSWITCH")
        btn_kill.get_style_context().add_class("btn-killswitch")
        btn_kill.connect("clicked", self._on_remote_killswitch)
        rogue_box.pack_end(btn_kill, False, False, 0)

        vbox.pack_start(rogue_box, False, False, 0)

        master_key = os.environ.get('MASTER_LICENSE_KEY', 'key_' + 'c1234567')
        self.list_store = Gtk.ListStore(str, str, str, str, str)
        self.list_store.append(["inst_prod_us_01", "https://noctualabs.tech", "127.0.0.1", master_key, "AUTHORIZED"])
        self.list_store.append(["inst_prod_eu_02", "https://eu-app.noctualabs.tech", "51.15.22.10", master_key, "AUTHORIZED"])
        self.list_store.append(["inst_staging_03", "https://staging.noctualabs.tech", "192.168.1.50", master_key, "AUTHORIZED"])
        self.list_store.append(["rogue_inst_09", "http://unauthorized-leak-mirror.org", "45.142.214.88", "INVALID (LEAK_KEY)", "UNAUTHORIZED LEAK"])

        tree_view = Gtk.TreeView(model=self.list_store)
        cols = ["Instance ID", "Origin Domain", "Client IP", "License Key Signature", "Attestation Status"]
        for i, col_title in enumerate(cols):
            renderer = Gtk.CellRendererText()
            column = Gtk.TreeViewColumn(col_title, renderer, text=i)
            column.set_resizable(True)
            column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
            column.set_expand(True)
            tree_view.append_column(column)

        scroll = Gtk.ScrolledWindow()
        scroll.add(tree_view)
        vbox.pack_start(scroll, True, True, 0)

        return vbox

    def _build_threat_map_tab(self):
        """Tab 5 — Unicode/ASCII world threat-origin heat map."""
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)

        hdr_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=16)
        lbl_hdr = Gtk.Label(label="🗺️  GLOBAL THREAT ORIGIN MAP  //  LIVE INTEL FEED")
        lbl_hdr.set_xalign(0)
        lbl_hdr.modify_font(FONT_MONO_BOLD_12)
        hdr_box.pack_start(lbl_hdr, True, True, 0)

        self.lbl_threat_ts = Gtk.Label(label=f"Last Update: {time.strftime('%H:%M:%S')}")
        self.lbl_threat_ts.modify_font(FONT_MONO_10)
        hdr_box.pack_end(self.lbl_threat_ts, False, False, 0)
        vbox.pack_start(hdr_box, False, False, 0)

        raw_map_lines = [
            "╔══════════════════════════════════════════════════════════════════════════════════════════╗",
            "║  AXIOM ZERO // GLOBAL THREAT ORIGIN MAP                        [ LIVE — CLASSIFIED ]   ║",
            "╠══════════════════════════════════════════════════════════════════════════════════════════╣",
            "║                                                                                        ║",
            "║   ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·   ║",
            "║   ·  ·  ·  ·  ·  ╔══════╗  ·  ·  ·  ·  ·  ·  ·  ╔══════════╗  ·  ·  ·  ·  ·  ·   ║",
            "║   ·  ·  ·  ·  ·  ║ N.AM ║  ·  ·  ·  ·  ·  ·  ·  ║  EUROPE  ║  ·  ·  ·  ╔═════╗  ·  ║",
            "║   ·  ·  ·  ·  ·  ║ [●]  ║  ·  ·  ·  ·  ·  ·  ·  ║[⚠][⚠][●]║  ·  ·  ·  ║CHINA║  ·  ║",
            "║   ·  ·  ·  ·  ·  ╚══════╝  ·  ·  ·  ·  ·  ·  ·  ╚══════════╝  ·  ·  ·  ║[☠] ║  ·  ║",
            "║   ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ╚═════╝  ·  ║",
            "║   ·  ·  ·  ·  ╔═══════╗  ·  ·  ·  ·  ·  ╔══════╗  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·   ║",
            "║   ·  ·  ·  ·  ║ S.AM  ║  ·  ·  ·  ·  ·  ║AFRICA║  ·  ·  ·  ·  ·  ╔══════════╗  ·   ║",
            "║   ·  ·  ·  ·  ║ [⚠]  ║  ·  ·  ·  ·  ·  ║ [●]  ║  ·  ·  ·  ·  ·  ║ S.E.ASIA ║  ·   ║",
            "║   ·  ·  ·  ·  ╚═══════╝  ·  ·  ·  ·  ·  ╚══════╝  ·  ·  ·  ·  ·  ║ [⚠][☠]  ║  ·   ║",
            "║   ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ╚══════════╝  ·   ║",
            "║   ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·   ║",
            "╠══════════════════════════════════════════════════════════════════════════════════════════╣",
            "║  THREAT ORIGIN COORDINATES (ACTIVE ATTACK VECTORS):                                   ║",
            "╠══════════════════════════════════════════════════════════════════════════════════════════╣",
            "║  [☠] CRITICAL  45.142.214.88   CN-Shenzen       Bot: MONOLITH-5    Tier:5  BLOCKED    ║",
            "║  [☠] CRITICAL  103.78.227.12   SG-Singapore     Bot: AI-Agent      Tier:4  BLOCKED    ║",
            "║  [⚠] HIGH      91.108.4.201    RU-Moscow        Bot: Stealth-3     Tier:3  BLOCKED    ║",
            "║  [⚠] HIGH      186.22.45.130   BR-SaoPaulo      Bot: Puppeteer     Tier:2  BLOCKED    ║",
            "║  [⚠] HIGH      41.190.3.8      NG-Lagos         Bot: Stealth-3     Tier:3  BLOCKED    ║",
            "║  [●] ACTIVE    77.88.55.77     DE-Frankfurt     Bot: cURL-1        Tier:1  THROTTLED  ║",
            "║  [●] ACTIVE    45.33.32.156    US-Virginia      Bot: Puppeteer     Tier:2  LOGGED     ║",
            "║  [●] ACTIVE    178.62.81.74    GB-London        Bot: cURL-1        Tier:1  LOGGED     ║",
            "╠══════════════════════════════════════════════════════════════════════════════════════════╣",
            "║  LEGEND:  [☠]=CRITICAL(Tier4/5)  [⚠]=HIGH(Tier2/3)  [●]=ACTIVE(Tier1)  ·=CLEAR     ║",
            "╚══════════════════════════════════════════════════════════════════════════════════════════╝"
        ]

        aligned_lines = []
        for line in raw_map_lines:
            if line.startswith("║") and line.endswith("║"):
                content = line[1:-1].ljust(90)
                aligned_lines.append(f"║{content}║")
            else:
                aligned_lines.append(line)

        threat_map_text = "\n".join(aligned_lines)

        txt_view = Gtk.TextView()
        txt_view.set_editable(False)
        txt_view.set_monospace(True)
        buf = txt_view.get_buffer()
        buf.set_text(threat_map_text)

        scroll = Gtk.ScrolledWindow()
        scroll.add(txt_view)
        vbox.pack_start(scroll, True, True, 0)

        # Legend KPI row
        legend_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=16)
        for label_text, css_class in [
            ("☠  CRITICAL (Tier 4/5)", "status-critical"),
            ("⚠  HIGH (Tier 2/3)",     "status-warn"),
            ("●  ACTIVE (Tier 1)",     "status-ok"),
        ]:
            badge = Gtk.Label(label=label_text)
            badge.get_style_context().add_class(css_class)
            badge.get_style_context().add_class("kpi-card")
            badge.set_margin_top(4)
            legend_box.pack_start(badge, True, True, 0)
        vbox.pack_start(legend_box, False, False, 0)

        return vbox

    def _build_stream_tab(self):
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
        lbl = Gtk.Label(label="UNIX DOMAIN SOCKET IPC CONSOLE STREAM (/tmp/axiom_zero_ipc.sock)")
        lbl.set_xalign(0)
        lbl.modify_font(FONT_MONO_BOLD_12)
        vbox.pack_start(lbl, False, False, 0)

        self.txt_stream = Gtk.TextView()
        self.txt_stream.set_editable(False)
        self.txt_stream.set_monospace(True)
        self.txt_buffer = self.txt_stream.get_buffer()

        self.txt_buffer.set_text('[IPC] Initializing socket listener...\n')

        scroll = Gtk.ScrolledWindow()
        scroll.add(self.txt_stream)
        vbox.pack_start(scroll, True, True, 0)

        # Fallback log tailer (instantiated but NOT started until socket disconnects)
        self._log_tailer = DaemonLogTailer(on_event=self._on_log_event)

        return vbox

    def _on_log_event(self, event: dict):
        """Callback from fallback DaemonLogTailer background thread."""
        line = EventFormatter.format(event) + '\n'
        GLib.idle_add(self._append_to_stream, line)
        if event.get('type') in ('EVENT_BLOCK', 'EVENT_ALLOW', 'EVENT_KILLSWITCH'):
            GLib.idle_add(self._update_swarm_store_from_event, event)
        return False

    def _append_to_stream(self, line: str):
        """Appends formatted text line to stream console (GTK thread only)."""
        if hasattr(self, 'txt_buffer') and self.txt_buffer:
            end_iter = self.txt_buffer.get_end_iter()
            self.txt_buffer.insert(end_iter, line)

            # Cap stream console line count at 1,000 to prevent memory bloat and UI rendering lockup
            max_lines = 1000
            line_count = self.txt_buffer.get_line_count()
            if line_count > max_lines:
                start_iter = self.txt_buffer.get_start_iter()
                cutoff_iter = self.txt_buffer.get_iter_at_line(line_count - max_lines)
                self.txt_buffer.delete(start_iter, cutoff_iter)

            if hasattr(self, 'txt_stream') and self.txt_stream:
                end_iter = self.txt_buffer.get_end_iter()
                mark = self.txt_buffer.create_mark(None, end_iter, False)
                self.txt_stream.scroll_to_mark(mark, 0.0, False, 0.0, 0.0)
                self.txt_buffer.delete_mark(mark)
        return False  # Return False so GLib.idle_add source is removed

    def _on_ipc_connected(self):
        """Called when a GTKIPCListener successfully connects to its socket."""
        self._active_connections += 1
        if hasattr(self, 'lbl_ipc_status') and self.lbl_ipc_status:
            self.lbl_ipc_status.set_text(f'⚡ SWARM IPC: {self._active_connections} NODES LIVE ✔')
        # If fallback log tailer was running, stop it now since we have a connection
        if hasattr(self, '_log_tailer') and self._log_tailer:
            self._log_tailer.stop()

    def _on_ipc_disconnected(self):
        """Called when a GTKIPCListener disconnects."""
        self._active_connections = max(0, self._active_connections - 1)
        if hasattr(self, 'lbl_ipc_status') and self.lbl_ipc_status:
            if self._active_connections > 0:
                self.lbl_ipc_status.set_text(f'⚡ SWARM IPC: {self._active_connections} NODES LIVE ✔')
            else:
                self.lbl_ipc_status.set_text('⚡ SWARM IPC: OFFLINE (FALLBACK LOG TAIL ACTIVE)')

    def _on_pair_remote_server(self, widget):
        if not RemotePairingDialog:
            return
        dialog = RemotePairingDialog(self)
        response = dialog.run()
        dialog.destroy()
        if response == Gtk.ResponseType.OK and hasattr(dialog, 'new_cfg'):
            # Add and start the new listener for the freshly paired server
            listener = GTKIPCListener(
                cfg=dialog.new_cfg,
                on_event_callback=self._on_ipc_event,
                on_connect_callback=self._on_ipc_connected,
                on_disconnect_callback=self._on_ipc_disconnected
            )
            self.ipc_listeners.append(listener)
            listener.start()
            self.lbl_ipc_status.set_text(f'⚡ CONNECTING NEW NODE: {listener.connection_label}...')

    def _on_ipc_event(self, event: dict):
        """Called in GTK main thread when a new event arrives from socket IPC."""
        line = EventFormatter.format(event) + '\n'
        self._append_to_stream(line)
        # If it's a swarm event or killswitch, refresh telemetry table in-memory
        if event.get('type') in ('EVENT_BLOCK', 'EVENT_ALLOW', 'EVENT_KILLSWITCH'):
            self._update_swarm_store_from_event(event)
        if hasattr(self, 'lbl_threat_ts') and self.lbl_threat_ts:
            self.lbl_threat_ts.set_text(f"Last Update: {time.strftime('%H:%M:%S')}")
        return False


    def _on_remote_killswitch(self, widget):
        confirm_dialog = Gtk.MessageDialog(
            transient_for=self,
            flags=Gtk.DialogFlags.MODAL,
            message_type=Gtk.MessageType.WARNING,
            buttons=Gtk.ButtonsType.OK_CANCEL,
            text="⚡ CONFIRM REMOTE KILLSWITCH EXECUTION"
        )
        confirm_dialog.format_secondary_text(
            "Are you sure you want to trigger a remote killswitch on rogue instance 'rogue_inst_09'?"
        )
        response = confirm_dialog.run()
        confirm_dialog.destroy()

        if response != Gtk.ResponseType.OK:
            return

        # Dispatch EVENT_KILLSWITCH frame down Unix Domain Socket
        self.send_killswitch_event("rogue_inst_09", "Unauthorized Leak Mirror", reason="Rogue Deployment Quarantine")

        dialog = Gtk.MessageDialog(
            transient_for=self,
            flags=0,
            message_type=Gtk.MessageType.WARNING,
            buttons=Gtk.ButtonsType.OK,
            text="⚡ REMOTE KILLSWITCH EXECUTED OVER IPC"
        )
        dialog.format_secondary_text(
            "Target: rogue_inst_09 (http://unauthorized-leak-mirror.org)\n"
            "Action: Encrypted EVENT_KILLSWITCH frame routed over Unix Domain Socket (/tmp/axiom_zero_ipc.sock).\n"
            "Result: Remote payload locked. Engine forced into perpetual honeypot tarpit."
        )
        dialog.run()
        dialog.destroy()

        for row in self.list_store:
            if row[0] == "rogue_inst_09":
                row[4] = "QUARANTINED (KILLSWITCH ACTIVE)"

        self.lbl_rogue_count.set_text("0 ACTIVE (1 QUARANTINED)")

def main():
    app = AxiomDevControlGTK()
    app.show_all()
    Gtk.main()

if __name__ == "__main__":
    main()
