var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
    function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
    var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
    var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
    var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
    var _, done = false;
    for (var i = decorators.length - 1; i >= 0; i--) {
        var context = {};
        for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
        for (var p in contextIn.access) context.access[p] = contextIn.access[p];
        context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
        var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
        if (kind === "accessor") {
            if (result === void 0) continue;
            if (result === null || typeof result !== "object") throw new TypeError("Object expected");
            if (_ = accept(result.get)) descriptor.get = _;
            if (_ = accept(result.set)) descriptor.set = _;
            if (_ = accept(result.init)) initializers.unshift(_);
        }
        else if (_ = accept(result)) {
            if (kind === "field") initializers.unshift(_);
            else descriptor[key] = _;
        }
    }
    if (target) Object.defineProperty(target, contextIn.name, descriptor);
    done = true;
};
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
    var useValue = arguments.length > 2;
    for (var i = 0; i < initializers.length; i++) {
        value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
    }
    return useValue ? value : void 0;
};
import { DeesElement, property, html, customElement, css, state, cssManager } from '@design.estate/dees-element';
import * as appstate from '../../appstate.js';
import * as interfaces from '../../../dist_ts_interfaces/index.js';
import { viewHostCss } from '../shared/css.js';
import {} from '@design.estate/dees-catalog';
let OpsViewNetworkActivity = (() => {
    let _classDecorators = [customElement('ops-view-network-activity')];
    let _classDescriptor;
    let _classExtraInitializers = [];
    let _classThis;
    let _classSuper = DeesElement;
    let _statsState_decorators;
    let _statsState_initializers = [];
    let _statsState_extraInitializers = [];
    let _networkState_decorators;
    let _networkState_initializers = [];
    let _networkState_extraInitializers = [];
    let _trafficDataIn_decorators;
    let _trafficDataIn_initializers = [];
    let _trafficDataIn_extraInitializers = [];
    let _trafficDataOut_decorators;
    let _trafficDataOut_initializers = [];
    let _trafficDataOut_extraInitializers = [];
    var OpsViewNetworkActivity = class extends _classSuper {
        static { _classThis = this; }
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            _statsState_decorators = [state()];
            _networkState_decorators = [state()];
            _trafficDataIn_decorators = [state()];
            _trafficDataOut_decorators = [state()];
            __esDecorate(this, null, _statsState_decorators, { kind: "accessor", name: "statsState", static: false, private: false, access: { has: obj => "statsState" in obj, get: obj => obj.statsState, set: (obj, value) => { obj.statsState = value; } }, metadata: _metadata }, _statsState_initializers, _statsState_extraInitializers);
            __esDecorate(this, null, _networkState_decorators, { kind: "accessor", name: "networkState", static: false, private: false, access: { has: obj => "networkState" in obj, get: obj => obj.networkState, set: (obj, value) => { obj.networkState = value; } }, metadata: _metadata }, _networkState_initializers, _networkState_extraInitializers);
            __esDecorate(this, null, _trafficDataIn_decorators, { kind: "accessor", name: "trafficDataIn", static: false, private: false, access: { has: obj => "trafficDataIn" in obj, get: obj => obj.trafficDataIn, set: (obj, value) => { obj.trafficDataIn = value; } }, metadata: _metadata }, _trafficDataIn_initializers, _trafficDataIn_extraInitializers);
            __esDecorate(this, null, _trafficDataOut_decorators, { kind: "accessor", name: "trafficDataOut", static: false, private: false, access: { has: obj => "trafficDataOut" in obj, get: obj => obj.trafficDataOut, set: (obj, value) => { obj.trafficDataOut = value; } }, metadata: _metadata }, _trafficDataOut_initializers, _trafficDataOut_extraInitializers);
            __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
            OpsViewNetworkActivity = _classThis = _classDescriptor.value;
            if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        }
        /** How far back the traffic chart shows */
        static CHART_WINDOW_MS = 5 * 60 * 1000; // 5 minutes
        /** How often a new data point is added */
        static UPDATE_INTERVAL_MS = 1000; // 1 second
        /** Derived: max data points the buffer holds */
        static MAX_DATA_POINTS = OpsViewNetworkActivity.CHART_WINDOW_MS / OpsViewNetworkActivity.UPDATE_INTERVAL_MS;
        #statsState_accessor_storage = __runInitializers(this, _statsState_initializers, appstate.statsStatePart.getState());
        get statsState() { return this.#statsState_accessor_storage; }
        set statsState(value) { this.#statsState_accessor_storage = value; }
        #networkState_accessor_storage = (__runInitializers(this, _statsState_extraInitializers), __runInitializers(this, _networkState_initializers, appstate.networkStatePart.getState()));
        get networkState() { return this.#networkState_accessor_storage; }
        set networkState(value) { this.#networkState_accessor_storage = value; }
        #trafficDataIn_accessor_storage = (__runInitializers(this, _networkState_extraInitializers), __runInitializers(this, _trafficDataIn_initializers, []));
        get trafficDataIn() { return this.#trafficDataIn_accessor_storage; }
        set trafficDataIn(value) { this.#trafficDataIn_accessor_storage = value; }
        #trafficDataOut_accessor_storage = (__runInitializers(this, _trafficDataIn_extraInitializers), __runInitializers(this, _trafficDataOut_initializers, []));
        get trafficDataOut() { return this.#trafficDataOut_accessor_storage; }
        set trafficDataOut(value) { this.#trafficDataOut_accessor_storage = value; }
        // Track if we need to update the chart to avoid unnecessary re-renders
        lastChartUpdate = (__runInitializers(this, _trafficDataOut_extraInitializers), 0);
        chartUpdateThreshold = OpsViewNetworkActivity.UPDATE_INTERVAL_MS; // Minimum ms between chart updates
        trafficUpdateTimer = null;
        requestsPerSecHistory = []; // Track requests/sec over time for trend
        historyLoaded = false; // Whether server-side throughput history has been loaded
        visibilityHandler = null;
        constructor() {
            super();
            this.subscribeToStateParts();
            this.initializeTrafficData();
            this.updateNetworkData();
            this.startTrafficUpdateTimer();
        }
        async connectedCallback() {
            await super.connectedCallback();
            // Pause/resume traffic timer when tab visibility changes
            this.visibilityHandler = () => {
                if (document.hidden) {
                    this.stopTrafficUpdateTimer();
                }
                else {
                    this.startTrafficUpdateTimer();
                }
            };
            document.addEventListener('visibilitychange', this.visibilityHandler);
            // When network view becomes visible, ensure we fetch network data
            await appstate.networkStatePart.dispatchAction(appstate.fetchNetworkStatsAction, null);
        }
        async disconnectedCallback() {
            await super.disconnectedCallback();
            this.stopTrafficUpdateTimer();
            if (this.visibilityHandler) {
                document.removeEventListener('visibilitychange', this.visibilityHandler);
                this.visibilityHandler = null;
            }
        }
        subscribeToStateParts() {
            // Subscribe and track unsubscribe functions
            const statsUnsubscribe = appstate.statsStatePart.select().subscribe((state) => {
                this.statsState = state;
            });
            this.rxSubscriptions.push(statsUnsubscribe);
            const networkUnsubscribe = appstate.networkStatePart.select().subscribe((state) => {
                this.networkState = state;
                this.updateNetworkData();
            });
            this.rxSubscriptions.push(networkUnsubscribe);
        }
        initializeTrafficData() {
            const now = Date.now();
            const { MAX_DATA_POINTS, UPDATE_INTERVAL_MS } = OpsViewNetworkActivity;
            // Initialize with empty data points for both in and out
            const emptyData = Array.from({ length: MAX_DATA_POINTS }, (_, i) => {
                const time = now - ((MAX_DATA_POINTS - 1 - i) * UPDATE_INTERVAL_MS);
                return {
                    x: new Date(time).toISOString(),
                    y: 0,
                };
            });
            this.trafficDataIn = [...emptyData];
            this.trafficDataOut = emptyData.map(point => ({ ...point }));
        }
        /**
         * Load server-side throughput history into the chart.
         * Called once when history data first arrives from the Rust engine.
         * This pre-populates the chart so users see historical data immediately
         * instead of starting from all zeros.
         */
        loadThroughputHistory() {
            const history = this.networkState.throughputHistory;
            if (!history || history.length === 0)
                return;
            this.historyLoaded = true;
            // Convert history points to chart data format (bytes/sec → Mbit/s)
            const historyIn = history.map(p => ({
                x: new Date(p.timestamp).toISOString(),
                y: Math.round((p.in * 8) / 1000000 * 10) / 10,
            }));
            const historyOut = history.map(p => ({
                x: new Date(p.timestamp).toISOString(),
                y: Math.round((p.out * 8) / 1000000 * 10) / 10,
            }));
            const { MAX_DATA_POINTS, UPDATE_INTERVAL_MS } = OpsViewNetworkActivity;
            // Use history as the chart data, keeping the most recent points within the window
            const sliceStart = Math.max(0, historyIn.length - MAX_DATA_POINTS);
            this.trafficDataIn = historyIn.slice(sliceStart);
            this.trafficDataOut = historyOut.slice(sliceStart);
            // If fewer than MAX_DATA_POINTS, pad the front with zeros
            if (this.trafficDataIn.length < MAX_DATA_POINTS) {
                const now = Date.now();
                const padCount = MAX_DATA_POINTS - this.trafficDataIn.length;
                const firstTimestamp = this.trafficDataIn.length > 0
                    ? new Date(this.trafficDataIn[0].x).getTime()
                    : now;
                const padIn = Array.from({ length: padCount }, (_, i) => ({
                    x: new Date(firstTimestamp - ((padCount - i) * UPDATE_INTERVAL_MS)).toISOString(),
                    y: 0,
                }));
                const padOut = padIn.map(p => ({ ...p }));
                this.trafficDataIn = [...padIn, ...this.trafficDataIn];
                this.trafficDataOut = [...padOut, ...this.trafficDataOut];
            }
        }
        static styles = [
            cssManager.defaultStyles,
            viewHostCss,
            css `
      .networkContainer {
        display: flex;
        flex-direction: column;
        gap: 24px;
      }


      .protocolBadge {
        display: inline-flex;
        align-items: center;
        padding: 4px 8px;
        border-radius: 4px;
        font-size: 12px;
        font-weight: 500;
      }

      .protocolBadge.http {
        background: ${cssManager.bdTheme('#e3f2fd', '#1a2c3a')};
        color: ${cssManager.bdTheme('#1976d2', '#5a9fd4')};
      }

      .protocolBadge.https {
        background: ${cssManager.bdTheme('#e8f5e9', '#1a3a1a')};
        color: ${cssManager.bdTheme('#388e3c', '#66bb6a')};
      }

      .protocolBadge.tcp {
        background: ${cssManager.bdTheme('#fff3e0', '#3a2a1a')};
        color: ${cssManager.bdTheme('#f57c00', '#ff9933')};
      }

      .protocolBadge.smtp {
        background: ${cssManager.bdTheme('#f3e5f5', '#2a1a3a')};
        color: ${cssManager.bdTheme('#7b1fa2', '#ba68c8')};
      }

      .protocolBadge.dns {
        background: ${cssManager.bdTheme('#e0f2f1', '#1a3a3a')};
        color: ${cssManager.bdTheme('#00796b', '#4db6ac')};
      }

      .protocolBadge.h1 {
        background: ${cssManager.bdTheme('#e3f2fd', '#1a2c3a')};
        color: ${cssManager.bdTheme('#1976d2', '#5a9fd4')};
      }

      .protocolBadge.h2 {
        background: ${cssManager.bdTheme('#e8f5e9', '#1a3a1a')};
        color: ${cssManager.bdTheme('#388e3c', '#66bb6a')};
      }

      .protocolBadge.h3 {
        background: ${cssManager.bdTheme('#f3e5f5', '#2a1a3a')};
        color: ${cssManager.bdTheme('#7b1fa2', '#ba68c8')};
      }

      .protocolBadge.unknown {
        background: ${cssManager.bdTheme('#f5f5f5', '#2a2a2a')};
        color: ${cssManager.bdTheme('#757575', '#999999')};
      }

      .suppressionBadge {
        display: inline-flex;
        align-items: center;
        padding: 2px 6px;
        border-radius: 3px;
        font-size: 11px;
        font-weight: 500;
        background: ${cssManager.bdTheme('#fff3e0', '#3a2a1a')};
        color: ${cssManager.bdTheme('#f57c00', '#ff9933')};
        margin-left: 4px;
      }

      .statusBadge {
        display: inline-flex;
        align-items: center;
        padding: 4px 8px;
        border-radius: 4px;
        font-size: 12px;
        font-weight: 500;
      }

      .statusBadge.success {
        background: ${cssManager.bdTheme('#e8f5e9', '#1a3a1a')};
        color: ${cssManager.bdTheme('#388e3c', '#66bb6a')};
      }

      .statusBadge.error {
        background: ${cssManager.bdTheme('#ffebee', '#3a1a1a')};
        color: ${cssManager.bdTheme('#d32f2f', '#ff6666')};
      }

      .statusBadge.warning {
        background: ${cssManager.bdTheme('#fff3e0', '#3a2a1a')};
        color: ${cssManager.bdTheme('#f57c00', '#ff9933')};
      }

      .intelligenceBadge {
        display: inline-flex;
        align-items: center;
        padding: 4px 8px;
        border-radius: 999px;
        font-size: 12px;
        font-weight: 500;
        background: ${cssManager.bdTheme('#eef2ff', '#1e1b4b')};
        color: ${cssManager.bdTheme('#4338ca', '#a5b4fc')};
      }

      .protocolChartGrid {
        display: grid;
        grid-template-columns: repeat(2, 1fr);
        gap: 16px;
      }
    `,
        ];
        render() {
            return html `
      <dees-heading level="3">Network Activity</dees-heading>

      <div class="networkContainer">
        <!-- Stats Grid -->
        ${this.renderNetworkStats()}

        <!-- Traffic Chart -->
        <dees-chart-area
          .label=${'Network Traffic'}
          .series=${[
                {
                    name: 'Inbound',
                    data: this.trafficDataIn,
                    color: '#22c55e',
                },
                {
                    name: 'Outbound',
                    data: this.trafficDataOut,
                    color: '#8b5cf6',
                }
            ]}
          .realtimeMode=${true}
          .rollingWindow=${OpsViewNetworkActivity.CHART_WINDOW_MS}
          .yAxisFormatter=${(val) => `${val} Mbit/s`}
        ></dees-chart-area>

        <!-- Protocol Distribution Charts -->
        ${this.renderProtocolCharts()}

        <!-- Top IPs by Connection Count -->
        ${this.renderTopIPs()}

        <!-- Top ASNs by Connection Count -->
        ${this.renderTopASNs()}

        <!-- Top IPs by Bandwidth -->
        ${this.renderTopIPsByBandwidth()}

        <!-- Domain Activity -->
        ${this.renderDomainActivity()}

        <!-- Backend Protocols Section -->
        ${this.renderBackendProtocols()}
      </div>
    `;
        }
        formatNumber(num) {
            if (num >= 1000000) {
                return (num / 1000000).toFixed(1) + 'M';
            }
            else if (num >= 1000) {
                return (num / 1000).toFixed(1) + 'K';
            }
            return num.toFixed(0);
        }
        formatBytes(bytes) {
            const units = ['B', 'KB', 'MB', 'GB'];
            let size = bytes;
            let unitIndex = 0;
            while (size >= 1024 && unitIndex < units.length - 1) {
                size /= 1024;
                unitIndex++;
            }
            return `${size.toFixed(1)} ${units[unitIndex]}`;
        }
        formatBitsPerSecond(bytesPerSecond) {
            const bitsPerSecond = bytesPerSecond * 8; // Convert bytes to bits
            const units = ['bit/s', 'kbit/s', 'Mbit/s', 'Gbit/s'];
            let size = bitsPerSecond;
            let unitIndex = 0;
            while (size >= 1000 && unitIndex < units.length - 1) {
                size /= 1000; // Use 1000 for bits (not 1024)
                unitIndex++;
            }
            return `${size.toFixed(1)} ${units[unitIndex]}`;
        }
        formatOptional(value) {
            if (value === null || value === undefined || value === '')
                return '-';
            return String(value);
        }
        formatDateTime(timestamp) {
            return timestamp ? new Date(timestamp).toLocaleString() : '-';
        }
        getIpIntelligence(ip) {
            return this.networkState.ipIntelligence?.find((record) => record.ipAddress === ip);
        }
        getIpOrganization(record) {
            return record?.asnOrg || record?.registrantOrg || '';
        }
        getIpIntelligenceColumns(ip) {
            const record = this.getIpIntelligence(ip);
            const organization = this.getIpOrganization(record);
            return {
                'Intelligence': record
                    ? html `<span class="intelligenceBadge">${this.formatOptional(organization || record.countryCode || 'Known')}</span>`
                    : html `<span class="statusBadge warning">Enriching...</span>`,
                'ASN': record?.asn ? `AS${record.asn}` : '-',
                'Organization': this.formatOptional(organization),
                'Country': this.formatOptional(record?.countryCode || record?.country),
                'Network Range': this.formatOptional(record?.networkRange),
                'Last Seen': this.formatDateTime(record?.lastSeenAt),
            };
        }
        getIpDataActions() {
            return [
                {
                    name: 'Refresh Intelligence',
                    iconName: 'lucide:refresh-cw',
                    type: ['inRow', 'contextmenu'],
                    actionFunc: async (actionData) => {
                        const ip = actionData.item.ip;
                        await appstate.securityPolicyStatePart.dispatchAction(appstate.refreshIpIntelligenceAction, ip);
                        await appstate.networkStatePart.dispatchAction(appstate.fetchNetworkStatsAction, null);
                    },
                },
                {
                    name: 'Block IP',
                    iconName: 'lucide:shield-ban',
                    type: ['inRow', 'contextmenu'],
                    actionFunc: async (actionData) => {
                        await this.createBlockRuleDialog('ip', actionData.item.ip, 'Blocked from Network Activity');
                    },
                },
                {
                    name: 'Block Network Range',
                    iconName: 'lucide:network',
                    type: ['contextmenu'],
                    actionRelevancyCheckFunc: (actionData) => Boolean(this.getIpIntelligence(actionData.item.ip)?.networkRange),
                    actionFunc: async (actionData) => {
                        const record = this.getIpIntelligence(actionData.item.ip);
                        await this.createBlockRuleDialog('cidr', record.networkRange, 'Blocked network range from Network Activity');
                    },
                },
                {
                    name: 'Block ASN',
                    iconName: 'lucide:radio-tower',
                    type: ['contextmenu'],
                    actionRelevancyCheckFunc: (actionData) => Boolean(this.getIpIntelligence(actionData.item.ip)?.asn),
                    actionFunc: async (actionData) => {
                        const record = this.getIpIntelligence(actionData.item.ip);
                        await this.createBlockRuleDialog('asn', String(record.asn), 'Blocked ASN from Network Activity');
                    },
                },
                {
                    name: 'Block Organization',
                    iconName: 'lucide:building-2',
                    type: ['contextmenu'],
                    actionRelevancyCheckFunc: (actionData) => Boolean(this.getIpOrganization(this.getIpIntelligence(actionData.item.ip))),
                    actionFunc: async (actionData) => {
                        const record = this.getIpIntelligence(actionData.item.ip);
                        await this.createBlockRuleDialog('organization', this.getIpOrganization(record), 'Blocked organization from Network Activity');
                    },
                },
                {
                    name: 'View Intelligence',
                    iconName: 'lucide:info',
                    type: ['doubleClick', 'contextmenu'],
                    actionRelevancyCheckFunc: (actionData) => Boolean(this.getIpIntelligence(actionData.item.ip)),
                    actionFunc: async (actionData) => {
                        await this.showIpIntelligenceDetails(actionData.item.ip);
                    },
                },
            ];
        }
        getAsnDataActions() {
            return [
                {
                    name: 'Block ASN',
                    iconName: 'lucide:radio-tower',
                    type: ['inRow', 'contextmenu'],
                    actionFunc: async (actionData) => {
                        await this.createBlockRuleDialog('asn', String(actionData.item.asn), 'Blocked ASN from Network Activity');
                    },
                },
                {
                    name: 'Block Organization',
                    iconName: 'lucide:building-2',
                    type: ['contextmenu'],
                    actionRelevancyCheckFunc: (actionData) => Boolean(actionData.item.organization),
                    actionFunc: async (actionData) => {
                        await this.createBlockRuleDialog('organization', actionData.item.organization, 'Blocked organization from Network Activity');
                    },
                },
            ];
        }
        calculateThroughput() {
            // Use real throughput data from network state
            return {
                in: this.networkState.throughputRate.bytesInPerSecond,
                out: this.networkState.throughputRate.bytesOutPerSecond,
            };
        }
        renderNetworkStats() {
            // Use server-side requests/sec from SmartProxy's Rust engine
            const reqPerSec = this.networkState.requestsPerSecond || 0;
            const throughput = this.calculateThroughput();
            const activeConnections = this.statsState.serverStats?.activeConnections || 0;
            // Build trend data from pre-computed history (mutated in updateNetworkData, not here)
            const trendData = [...this.requestsPerSecHistory];
            while (trendData.length < 20) {
                trendData.unshift(0);
            }
            const tiles = [
                {
                    id: 'connections',
                    title: 'Active Connections',
                    value: activeConnections,
                    type: 'number',
                    icon: 'lucide:Plug',
                    color: activeConnections > 100 ? '#f59e0b' : '#22c55e',
                    description: `Total: ${this.formatNumber(this.statsState.serverStats?.totalConnections || 0)} connections`,
                    actions: [
                        {
                            name: 'View Details',
                            iconName: 'fa:magnifyingGlass',
                            action: async () => {
                            },
                        },
                    ],
                },
                {
                    id: 'requests',
                    title: 'Requests/sec',
                    value: reqPerSec,
                    type: 'trend',
                    icon: 'lucide:ChartLine',
                    color: '#3b82f6',
                    trendData: trendData,
                    description: `Total: ${this.formatNumber(this.networkState.requestsTotal || 0)} requests`,
                },
                {
                    id: 'throughputIn',
                    title: 'Throughput In',
                    value: this.formatBitsPerSecond(throughput.in),
                    unit: '',
                    type: 'number',
                    icon: 'lucide:Download',
                    color: '#22c55e',
                    description: `Total: ${this.formatBytes(this.networkState.totalBytes?.in || 0)}`,
                },
                {
                    id: 'throughputOut',
                    title: 'Throughput Out',
                    value: this.formatBitsPerSecond(throughput.out),
                    unit: '',
                    type: 'number',
                    icon: 'lucide:Upload',
                    color: '#8b5cf6',
                    description: `Total: ${this.formatBytes(this.networkState.totalBytes?.out || 0)}`,
                },
            ];
            return html `
      <dees-statsgrid
        .tiles=${tiles}
        .minTileWidth=${200}
      ></dees-statsgrid>
    `;
        }
        renderProtocolCharts() {
            const fp = this.networkState.frontendProtocols;
            const bp = this.networkState.backendProtocols;
            const protoColors = {
                'HTTP/1.1': '#1976d2',
                'HTTP/2': '#388e3c',
                'HTTP/3': '#7b1fa2',
                'WebSocket': '#f57c00',
                'Other': '#757575',
            };
            const buildDonutData = (dist) => {
                if (!dist)
                    return [];
                const items = [];
                if (dist.h1Active > 0)
                    items.push({ name: 'HTTP/1.1', value: dist.h1Active, color: protoColors['HTTP/1.1'] });
                if (dist.h2Active > 0)
                    items.push({ name: 'HTTP/2', value: dist.h2Active, color: protoColors['HTTP/2'] });
                if (dist.h3Active > 0)
                    items.push({ name: 'HTTP/3', value: dist.h3Active, color: protoColors['HTTP/3'] });
                if (dist.wsActive > 0)
                    items.push({ name: 'WebSocket', value: dist.wsActive, color: protoColors['WebSocket'] });
                if (dist.otherActive > 0)
                    items.push({ name: 'Other', value: dist.otherActive, color: protoColors['Other'] });
                return items;
            };
            const frontendData = buildDonutData(fp);
            const backendData = buildDonutData(bp);
            return html `
      <div class="protocolChartGrid">
        <dees-chart-donut
          .label=${'Frontend Protocols'}
          .data=${frontendData.length > 0 ? frontendData : [{ name: 'No Traffic', value: 1, color: '#757575' }]}
          .showLegend=${true}
          .showLabels=${true}
          .innerRadiusPercent=${'55%'}
          .valueFormatter=${(val) => `${val} active`}
        ></dees-chart-donut>
        <dees-chart-donut
          .label=${'Backend Protocols'}
          .data=${backendData.length > 0 ? backendData : [{ name: 'No Traffic', value: 1, color: '#757575' }]}
          .showLegend=${true}
          .showLabels=${true}
          .innerRadiusPercent=${'55%'}
          .valueFormatter=${(val) => `${val} active`}
        ></dees-chart-donut>
      </div>
    `;
        }
        renderTopIPs() {
            if (this.networkState.topIPs.length === 0) {
                return html ``;
            }
            // Build per-IP bandwidth lookup
            const bandwidthByIP = new Map();
            if (this.networkState.throughputByIP) {
                for (const entry of this.networkState.throughputByIP) {
                    bandwidthByIP.set(entry.ip, { in: entry.in, out: entry.out });
                }
            }
            // Calculate total connections across all top IPs
            const totalConnections = this.networkState.topIPs.reduce((sum, ipData) => sum + ipData.count, 0);
            return html `
      <dees-table
        .data=${this.networkState.topIPs}
        .rowKey=${'ip'}
        .highlightUpdates=${'flash'}
        .displayFunction=${(ipData) => {
                const bw = bandwidthByIP.get(ipData.ip);
                return {
                    'IP Address': ipData.ip,
                    'Connections': ipData.count,
                    'Bandwidth In': bw ? this.formatBitsPerSecond(bw.in) : '0 bit/s',
                    'Bandwidth Out': bw ? this.formatBitsPerSecond(bw.out) : '0 bit/s',
                    'Share': totalConnections > 0 ? ((ipData.count / totalConnections) * 100).toFixed(1) + '%' : '0%',
                    ...this.getIpIntelligenceColumns(ipData.ip),
                };
            }}
        .dataActions=${this.getIpDataActions()}
        heading1="Top Connected IPs"
        heading2="IPs with most active connections, bandwidth, and intelligence"
        searchable
        .showColumnFilters=${true}
        .pagination=${false}
        dataName="ip"
      ></dees-table>
    `;
        }
        renderTopASNs() {
            if (!this.networkState.topASNs || this.networkState.topASNs.length === 0) {
                return html ``;
            }
            return html `
      <dees-table
        .data=${this.networkState.topASNs}
        .rowKey=${'asn'}
        .highlightUpdates=${'flash'}
        .displayFunction=${(asnData) => {
                return {
                    'ASN': `AS${asnData.asn}`,
                    'Organization': this.formatOptional(asnData.organization),
                    'Connections': asnData.activeConnections,
                    'IPs': asnData.ipCount,
                    'Bandwidth In': this.formatBitsPerSecond(asnData.bytesInPerSecond),
                    'Bandwidth Out': this.formatBitsPerSecond(asnData.bytesOutPerSecond),
                    'Total Bandwidth': this.formatBitsPerSecond(asnData.bytesInPerSecond + asnData.bytesOutPerSecond),
                    'Country': this.formatOptional(asnData.country),
                    'Sample IPs': asnData.sampleIps.join(', '),
                };
            }}
        .dataActions=${this.getAsnDataActions()}
        heading1="Top Connected ASNs"
        heading2="Organizations causing the most active connections across observed IPs"
        searchable
        .showColumnFilters=${true}
        .pagination=${false}
        dataName="ASN"
      ></dees-table>
    `;
        }
        renderTopIPsByBandwidth() {
            if (!this.networkState.topIPsByBandwidth || this.networkState.topIPsByBandwidth.length === 0) {
                return html ``;
            }
            return html `
      <dees-table
        .data=${this.networkState.topIPsByBandwidth}
        .rowKey=${'ip'}
        .highlightUpdates=${'flash'}
        .displayFunction=${(ipData) => {
                return {
                    'IP Address': ipData.ip,
                    'Bandwidth In': this.formatBitsPerSecond(ipData.bwIn),
                    'Bandwidth Out': this.formatBitsPerSecond(ipData.bwOut),
                    'Total Bandwidth': this.formatBitsPerSecond(ipData.bwIn + ipData.bwOut),
                    'Connections': ipData.count,
                    ...this.getIpIntelligenceColumns(ipData.ip),
                };
            }}
        .dataActions=${this.getIpDataActions()}
        heading1="Top IPs by Bandwidth"
        heading2="IPs with highest throughput and intelligence"
        searchable
        .showColumnFilters=${true}
        .pagination=${false}
        dataName="ip"
      ></dees-table>
    `;
        }
        renderDomainActivity() {
            if (!this.networkState.domainActivity || this.networkState.domainActivity.length === 0) {
                return html ``;
            }
            return html `
      <dees-table
        .data=${this.networkState.domainActivity}
        .rowKey=${'domain'}
        .highlightUpdates=${'flash'}
        .displayFunction=${(item) => {
                const totalBytesPerMin = (item.bytesInPerSecond + item.bytesOutPerSecond) * 60;
                return {
                    'Domain': item.domain,
                    'Throughput In': this.formatBitsPerSecond(item.bytesInPerSecond),
                    'Throughput Out': this.formatBitsPerSecond(item.bytesOutPerSecond),
                    'Transferred / min': this.formatBytes(totalBytesPerMin),
                    'Connections': item.activeConnections,
                    'Req/s': item.requestsPerSecond != null ? item.requestsPerSecond.toFixed(1) : '-',
                    'Req/min': item.requestsLastMinute != null ? item.requestsLastMinute.toFixed(0) : '-',
                    'Requests': item.requestCount?.toLocaleString() ?? '0',
                    'Routes': item.routeCount,
                };
            }}
        heading1="Domain Activity"
        heading2="Per-domain network activity from request-level metrics"
        searchable
        .showColumnFilters=${true}
        .pagination=${false}
        dataName="domain"
      ></dees-table>
    `;
        }
        renderBackendProtocols() {
            const backends = this.networkState.backends;
            if (!backends || backends.length === 0) {
                return html ``;
            }
            return html `
      <dees-table
        .data=${backends}
        .rowKey=${'id'}
        .highlightUpdates=${'flash'}
        .displayFunction=${(item) => {
                const totalErrors = item.connectErrors + item.handshakeErrors + item.requestErrors;
                const protocolClass = item.protocol.toLowerCase().replace(/[^a-z0-9]/g, '');
                return {
                    'Backend': item.backend,
                    'Domain': item.domain || '-',
                    'Protocol': html `
              <span class="protocolBadge ${protocolClass}">${item.protocol.toUpperCase()}</span>
              ${item.h2Suppressed ? html `<span class="suppressionBadge" title="H2 suppressed: ${item.h2ConsecutiveFailures ?? 0} failures, cooldown ${item.h2CooldownRemainingSecs ?? 0}s">H2 suppressed</span>` : ''}
              ${item.h3Suppressed ? html `<span class="suppressionBadge" title="H3 suppressed: ${item.h3ConsecutiveFailures ?? 0} failures, cooldown ${item.h3CooldownRemainingSecs ?? 0}s">H3 suppressed</span>` : ''}
            `,
                    'Active': item.activeConnections,
                    'Total': this.formatNumber(item.totalConnections),
                    'Avg Connect': item.avgConnectTimeMs > 0 ? `${item.avgConnectTimeMs.toFixed(1)}ms` : '-',
                    'Pool Hit Rate': item.poolHitRate > 0 ? `${(item.poolHitRate * 100).toFixed(1)}%` : '-',
                    'Errors': totalErrors > 0
                        ? html `<span class="statusBadge error">${totalErrors}</span>`
                        : html `<span class="statusBadge success">0</span>`,
                    'Cache Age': item.cacheAgeSecs != null ? `${Math.round(item.cacheAgeSecs)}s` : '-',
                };
            }}
        .dataActions=${[
                {
                    name: 'View Details',
                    iconName: 'lucide:info',
                    type: ['inRow', 'doubleClick', 'contextmenu'],
                    actionFunc: async (actionData) => {
                        await this.showBackendDetails(actionData.item);
                    }
                }
            ]}
        heading1="Backend Protocols"
        heading2="Auto-detected backend protocols and connection pool health"
        searchable
        .showColumnFilters=${true}
        .pagination=${false}
        dataName="backend"
      ></dees-table>
    `;
        }
        async showBackendDetails(backend) {
            const { DeesModal } = await import('@design.estate/dees-catalog');
            await DeesModal.createAndShow({
                heading: `Backend: ${backend.backend}`,
                content: html `
        <div style="padding: 20px;">
          <dees-dataview-codebox
            .heading=${'Backend Details'}
            progLang="json"
            .codeToDisplay=${JSON.stringify({
                    backend: backend.backend,
                    domain: backend.domain,
                    protocol: backend.protocol,
                    activeConnections: backend.activeConnections,
                    totalConnections: backend.totalConnections,
                    avgConnectTimeMs: backend.avgConnectTimeMs,
                    poolHitRate: backend.poolHitRate,
                    errors: {
                        connect: backend.connectErrors,
                        handshake: backend.handshakeErrors,
                        request: backend.requestErrors,
                        h2Failures: backend.h2Failures,
                    },
                    suppression: {
                        h2Suppressed: backend.h2Suppressed,
                        h3Suppressed: backend.h3Suppressed,
                        h2CooldownRemainingSecs: backend.h2CooldownRemainingSecs,
                        h3CooldownRemainingSecs: backend.h3CooldownRemainingSecs,
                        h2ConsecutiveFailures: backend.h2ConsecutiveFailures,
                        h3ConsecutiveFailures: backend.h3ConsecutiveFailures,
                    },
                    h3Port: backend.h3Port,
                    cacheAgeSecs: backend.cacheAgeSecs,
                }, null, 2)}
          ></dees-dataview-codebox>
        </div>
      `,
                menuOptions: [
                    {
                        name: 'Copy Backend Key',
                        iconName: 'lucide:Copy',
                        action: async () => {
                            await navigator.clipboard.writeText(backend.backend);
                        }
                    }
                ]
            });
        }
        getDropdownKey(value) {
            return typeof value === 'string' ? value : value?.key || '';
        }
        async createBlockRuleDialog(type, value, reason) {
            const { DeesModal } = await import('@design.estate/dees-catalog');
            const typeOptions = [
                { key: 'ip', option: 'IP address' },
                { key: 'cidr', option: 'CIDR / network range' },
                { key: 'asn', option: 'ASN' },
                { key: 'organization', option: 'Organization' },
            ];
            const matchModeOptions = [
                { key: 'contains', option: 'Organization contains value' },
                { key: 'exact', option: 'Organization exactly matches value' },
            ];
            await DeesModal.createAndShow({
                heading: 'Create Security Block Rule',
                content: html `
        <dees-form>
          <dees-input-dropdown
            .key=${'type'}
            .label=${'Rule Type'}
            .options=${typeOptions}
            .selectedOption=${typeOptions.find((option) => option.key === type)}
          ></dees-input-dropdown>
          <dees-input-text .key=${'value'} .label=${'Value'} .value=${value} .required=${true}></dees-input-text>
          <dees-input-dropdown
            .key=${'matchMode'}
            .label=${'Organization Match Mode'}
            .description=${'Only used for organization rules'}
            .options=${matchModeOptions}
            .selectedOption=${matchModeOptions[0]}
          ></dees-input-dropdown>
          <dees-input-text .key=${'reason'} .label=${'Reason'} .value=${reason}></dees-input-text>
          <dees-input-checkbox .key=${'enabled'} .label=${'Enable immediately'} .value=${true}></dees-input-checkbox>
        </dees-form>
      `,
                menuOptions: [
                    { name: 'Cancel', iconName: 'lucide:x', action: async (modalArg) => modalArg.destroy() },
                    {
                        name: 'Create',
                        iconName: 'lucide:shield-ban',
                        action: async (modalArg) => {
                            const form = modalArg.shadowRoot?.querySelector('.content')?.querySelector('dees-form');
                            if (!form)
                                return;
                            const data = await form.collectFormData();
                            const selectedType = this.getDropdownKey(data.type);
                            const selectedValue = String(data.value || '').trim();
                            if (!selectedType || !selectedValue)
                                return;
                            const matchMode = selectedType === 'organization'
                                ? this.getDropdownKey(data.matchMode)
                                : undefined;
                            await appstate.securityPolicyStatePart.dispatchAction(appstate.createSecurityBlockRuleAction, {
                                type: selectedType,
                                value: selectedValue,
                                matchMode,
                                reason: String(data.reason || '').trim() || undefined,
                                enabled: data.enabled !== false,
                            });
                            await appstate.networkStatePart.dispatchAction(appstate.fetchNetworkStatsAction, null);
                            await modalArg.destroy();
                        },
                    },
                ],
            });
        }
        async showIpIntelligenceDetails(ip) {
            const record = this.getIpIntelligence(ip);
            if (!record)
                return;
            const { DeesModal } = await import('@design.estate/dees-catalog');
            await DeesModal.createAndShow({
                heading: `IP Intelligence: ${ip}`,
                content: html `
        <div style="padding: 20px;">
          <dees-dataview-codebox
            .heading=${'Intelligence Record'}
            progLang="json"
            .codeToDisplay=${JSON.stringify(record, null, 2)}
          ></dees-dataview-codebox>
        </div>
      `,
                menuOptions: [
                    {
                        name: 'Copy Abuse Contact',
                        iconName: 'lucide:copy',
                        action: async () => {
                            if (record.abuseContact)
                                await navigator.clipboard.writeText(record.abuseContact);
                        },
                    },
                    {
                        name: 'Block IP',
                        iconName: 'lucide:shield-ban',
                        action: async () => {
                            await this.createBlockRuleDialog('ip', record.ipAddress, 'Blocked from IP intelligence details');
                        },
                    },
                ],
            });
        }
        async updateNetworkData() {
            // Track requests/sec history for the trend sparkline (moved out of render)
            const reqPerSec = this.networkState.requestsPerSecond || 0;
            this.requestsPerSecHistory.push(reqPerSec);
            if (this.requestsPerSecHistory.length > 20) {
                this.requestsPerSecHistory.shift();
            }
            // Load server-side throughput history into chart (once)
            if (!this.historyLoaded && this.networkState.throughputHistory && this.networkState.throughputHistory.length > 0) {
                this.loadThroughputHistory();
            }
        }
        startTrafficUpdateTimer() {
            this.stopTrafficUpdateTimer(); // Clear any existing timer
            this.trafficUpdateTimer = setInterval(() => {
                this.addTrafficDataPoint();
            }, OpsViewNetworkActivity.UPDATE_INTERVAL_MS);
        }
        addTrafficDataPoint() {
            const now = Date.now();
            // Throttle chart updates to avoid excessive re-renders
            if (now - this.lastChartUpdate < this.chartUpdateThreshold) {
                return;
            }
            const throughput = this.calculateThroughput();
            if (this.networkState.lastUpdated && now - this.networkState.lastUpdated > 3000) {
                return;
            }
            // Convert to Mbps (bytes * 8 / 1,000,000)
            const throughputInMbps = (throughput.in * 8) / 1000000;
            const throughputOutMbps = (throughput.out * 8) / 1000000;
            // Add new data points
            const timestamp = new Date(now).toISOString();
            const newDataPointIn = {
                x: timestamp,
                y: Math.round(throughputInMbps * 10) / 10
            };
            const newDataPointOut = {
                x: timestamp,
                y: Math.round(throughputOutMbps * 10) / 10
            };
            // In-place mutation then reassign for Lit reactivity (avoids 4 intermediate arrays)
            if (this.trafficDataIn.length >= OpsViewNetworkActivity.MAX_DATA_POINTS) {
                this.trafficDataIn.shift();
                this.trafficDataOut.shift();
            }
            this.trafficDataIn = [...this.trafficDataIn, newDataPointIn];
            this.trafficDataOut = [...this.trafficDataOut, newDataPointOut];
            this.lastChartUpdate = now;
        }
        stopTrafficUpdateTimer() {
            if (this.trafficUpdateTimer) {
                clearInterval(this.trafficUpdateTimer);
                this.trafficUpdateTimer = null;
            }
        }
        static {
            __runInitializers(_classThis, _classExtraInitializers);
        }
    };
    return OpsViewNetworkActivity = _classThis;
})();
export { OpsViewNetworkActivity };
//# sourceMappingURL=data:application/json;base64, 