// Topology Graph Component with D3.js force-directed visualization
class TopologyGraphComponent extends Component {
    constructor(container, viewModel, eventBus) {
        super(container, viewModel, eventBus);
        logger.debug('TopologyGraphComponent: Constructor called');
        this.svg = null;
        this.simulation = null;
        this.zoom = null;
        this.width = 0;  // Will be set dynamically based on container size
        this.height = 0;  // Will be set dynamically based on container size
        this.isInitialized = false;

        // Drawer state for desktop reuse (shared singleton)
        this.drawer = new DrawerComponent();

        // Tooltip for labels on hover
        this.tooltipEl = null;
    }

    // Determine desktop threshold
    isDesktop() {
        return this.drawer.isDesktop();
    }


    openDrawerForNode(nodeData) {
        // Get display name for drawer title
        let displayName = 'Node Details';
        try {
            const hostname = nodeData.hostname || '';
            const ip = nodeData.ip || '';
            
            if (hostname && ip) {
                displayName = `${hostname} - ${ip}`;
            } else if (hostname) {
                displayName = hostname;
            } else if (ip) {
                displayName = ip;
            }
        } catch (_) {}

        // Open drawer with content callback
        this.drawer.openDrawer(displayName, (contentContainer, setActiveComponent) => {
            // Mount NodeDetailsComponent
            const nodeDetailsVM = new NodeDetailsViewModel();
            const nodeDetailsComponent = new NodeDetailsComponent(contentContainer, nodeDetailsVM, this.eventBus);
            setActiveComponent(nodeDetailsComponent);

            const ip = nodeData.ip || nodeData.id;
            nodeDetailsVM.loadNodeDetails(ip).then(() => {
                nodeDetailsComponent.mount();
            }).catch((error) => {
                logger.error('Failed to load node details (topology drawer):', error);
                contentContainer.innerHTML = `
                    <div class="error">
                        <strong>Error loading node details:</strong><br>
                        ${this.escapeHtml(error.message)}
                    </div>
                `;
            });
        });

        try {
            if (window.TerminalPanel && typeof nodeData.ip === 'string') {
                window.TerminalPanel.lastNodeIp = nodeData.ip;
                if (window.TerminalPanel._updateTitle) {
                    window.TerminalPanel._updateTitle(nodeData.ip);
                }
            }
        } catch (_) {}
    }

    closeDrawer() {
        this.drawer.closeDrawer();
    }

    // Tooltip helpers
    ensureTooltip() {
        if (this.tooltipEl) return;
        const el = document.createElement('div');
        el.className = 'topology-tooltip';
        document.body.appendChild(el);
        this.tooltipEl = el;
    }

    showTooltip(nodeData, pageX, pageY) {
        this.ensureTooltip();
        const labels = (nodeData && nodeData.labels) ? nodeData.labels : ((nodeData && nodeData.resources) ? nodeData.resources : null);
        if (!labels || Object.keys(labels).length === 0) {
            this.hideTooltip();
            return;
        }
        const chips = Object.entries(labels)
            .map(([k, v]) => `<span class=\"label-chip\">${this.escapeHtml(String(k))}: ${this.escapeHtml(String(v))}</span>`)
            .join('');
        this.tooltipEl.innerHTML = `<div class=\"member-labels\">${chips}</div>`;
        this.positionTooltip(pageX, pageY);
        this.tooltipEl.classList.add('visible');
    }

    positionTooltip(pageX, pageY) {
        if (!this.tooltipEl) return;
        const offset = 12;
        let left = pageX + offset;
        let top = pageY + offset;
        const { innerWidth, innerHeight } = window;
        const rect = this.tooltipEl.getBoundingClientRect();
        if (left + rect.width > innerWidth - 8) left = pageX - rect.width - offset;
        if (top + rect.height > innerHeight - 8) top = pageY - rect.height - offset;
        this.tooltipEl.style.left = `${Math.max(8, left)}px`;
        this.tooltipEl.style.top = `${Math.max(8, top)}px`;
    }

    moveTooltip(pageX, pageY) {
        if (!this.tooltipEl || !this.tooltipEl.classList.contains('visible')) return;
        this.positionTooltip(pageX, pageY);
    }

    hideTooltip() {
        if (this.tooltipEl) this.tooltipEl.classList.remove('visible');
    }

    updateDimensions(container) {
        // Get the container's actual dimensions
        const rect = container.getBoundingClientRect();
        this.width = rect.width || 1400;  // Fallback to 1400 if width is 0
        this.height = rect.height || 1000; // Fallback to 1000 if height is 0
        
        // Ensure minimum dimensions
        this.width = Math.max(this.width, 800);
        this.height = Math.max(this.height, 600);
        
        logger.debug('TopologyGraphComponent: Updated dimensions to', this.width, 'x', this.height);
    }

    handleResize() {
        // Debounce resize events to avoid excessive updates
        if (this.resizeTimeout) {
            clearTimeout(this.resizeTimeout);
        }
        
        this.resizeTimeout = setTimeout(() => {
            const container = this.findElement('#topology-graph-container');
            if (container && this.svg) {
                this.updateDimensions(container);
                // Update SVG viewBox and force center
                this.svg.attr('viewBox', `0 0 ${this.width} ${this.height}`);
                if (this.simulation) {
                    this.simulation.force('center', d3.forceCenter(this.width / 2, this.height / 2));
                    this.simulation.alpha(0.3).restart();
                }
            }
        }, 250); // 250ms debounce
    }

    // Override mount to ensure proper initialization
    mount() {
        if (this.isMounted) return;
        
        logger.debug('TopologyGraphComponent: Starting mount...');
        logger.debug('TopologyGraphComponent: isInitialized =', this.isInitialized);
        
        // Call initialize if not already done
        if (!this.isInitialized) {
            logger.debug('TopologyGraphComponent: Initializing during mount...');
            this.initialize().then(() => {
                logger.debug('TopologyGraphComponent: Initialization completed, calling completeMount...');
                // Complete mount after initialization
                this.completeMount();
            }).catch(error => {
                logger.error('TopologyGraphComponent: Initialization failed during mount:', error);
                // Still complete mount to prevent blocking
                this.completeMount();
            });
        } else {
            logger.debug('TopologyGraphComponent: Already initialized, calling completeMount directly...');
            this.completeMount();
        }
    }

    completeMount() {
        logger.debug('TopologyGraphComponent: completeMount called');
        this.isMounted = true;
        logger.debug('TopologyGraphComponent: Setting up event listeners...');
        this.setupEventListeners();
        logger.debug('TopologyGraphComponent: Setting up view model listeners...');
        this.setupViewModelListeners();
        logger.debug('TopologyGraphComponent: Calling render...');
        this.render();
        
        logger.debug('TopologyGraphComponent: Mounted successfully');
    }

    setupEventListeners() {
        logger.debug('TopologyGraphComponent: setupEventListeners called');
        logger.debug('TopologyGraphComponent: Container:', this.container);
        logger.debug('TopologyGraphComponent: Container ID:', this.container?.id);
        
        // Add resize listener to update dimensions when window is resized
        this.resizeHandler = this.handleResize.bind(this);
        window.addEventListener('resize', this.resizeHandler);
        
        // Refresh button removed from HTML, so no need to set up event listeners
        logger.debug('TopologyGraphComponent: No event listeners needed (refresh button removed)');
    }

    setupViewModelListeners() {
        logger.debug('TopologyGraphComponent: setupViewModelListeners called');
        logger.debug('TopologyGraphComponent: isInitialized =', this.isInitialized);
        
        if (this.isInitialized) {
            // Component is already initialized, set up subscriptions immediately
            logger.debug('TopologyGraphComponent: Setting up property subscriptions immediately');
            this.subscribeToProperty('nodes', this.renderGraph.bind(this));
            this.subscribeToProperty('links', this.renderGraph.bind(this));
            this.subscribeToProperty('isLoading', this.handleLoadingState.bind(this));
            this.subscribeToProperty('error', this.handleError.bind(this));
            this.subscribeToProperty('selectedNode', this.updateSelection.bind(this));
        } else {
            // Component not yet initialized, store for later
            logger.debug('TopologyGraphComponent: Component not initialized, storing pending subscriptions');
            this._pendingSubscriptions = [
                ['nodes', this.renderGraph.bind(this)],
                ['links', this.renderGraph.bind(this)],
                ['isLoading', this.handleLoadingState.bind(this)],
                ['error', this.handleError.bind(this)],
                ['selectedNode', this.updateSelection.bind(this)]
            ];
        }
    }

    async initialize() {
        logger.debug('TopologyGraphComponent: Initializing...');
        
        // Wait for DOM to be ready
        if (document.readyState === 'loading') {
            await new Promise(resolve => {
                document.addEventListener('DOMContentLoaded', resolve);
            });
        }
        
        // Set up the SVG container
        this.setupSVG();
        
        // Mark as initialized
        this.isInitialized = true;
        
        // Now set up the actual property listeners after initialization
        if (this._pendingSubscriptions) {
            this._pendingSubscriptions.forEach(([property, callback]) => {
                this.subscribeToProperty(property, callback);
            });
            this._pendingSubscriptions = null;
        }
        
        // Initial data load
        await this.viewModel.updateNetworkTopology();
    }

    setupSVG() {
        const container = this.findElement('#topology-graph-container');
        if (!container) {
            logger.error('TopologyGraphComponent: Graph container not found');
            return;
        }

        // Calculate dynamic dimensions based on container size
        this.updateDimensions(container);

        // Clear existing content
        container.innerHTML = '';

        // Create SVG element
        this.svg = d3.select(container)
            .append('svg')
            .attr('width', '100%')
            .attr('height', '100%')
            .attr('viewBox', `0 0 ${this.width} ${this.height}`)
            .style('border', '1px solid rgba(255, 255, 255, 0.1)')
            //.style('background', 'rgba(0, 0, 0, 0.2)')
            .style('border-radius', '12px');

        // Add zoom behavior
        this.zoom = d3.zoom()
            .scaleExtent([0.5, 5])
            .on('zoom', (event) => {
                this.svg.select('g').attr('transform', event.transform);
            });

        this.svg.call(this.zoom);

        // Create main group for zoom and apply initial zoom
        const mainGroup = this.svg.append('g');
        
        // Apply initial zoom to show the graph more zoomed in
        mainGroup.attr('transform', 'scale(1.4) translate(-200, -150)');

        logger.debug('TopologyGraphComponent: SVG setup completed');
    }

    // Ensure component is initialized
    async ensureInitialized() {
        if (!this.isInitialized) {
            logger.debug('TopologyGraphComponent: Ensuring initialization...');
            await this.initialize();
        }
        return this.isInitialized;
    }

    renderGraph() {
        try {
            // Check if component is initialized
            if (!this.isInitialized) {
                logger.debug('TopologyGraphComponent: Component not yet initialized, ensuring initialization...');
                this.ensureInitialized().then(() => {
                    // Re-render after initialization
                    this.renderGraph();
                }).catch(error => {
                    logger.error('TopologyGraphComponent: Failed to initialize:', error);
                });
                return;
            }

            const nodes = this.viewModel.get('nodes');
            const links = this.viewModel.get('links');

            // Check if SVG is initialized
            if (!this.svg) {
                logger.debug('TopologyGraphComponent: SVG not initialized yet, setting up SVG first');
                this.setupSVG();
            }

            if (!nodes || nodes.length === 0) {
                this.showNoData();
                return;
            }

            logger.debug('TopologyGraphComponent: Rendering graph with', nodes.length, 'nodes and', links.length, 'links');

            // Get the main SVG group (the one created in setupSVG)
            let svgGroup = this.svg.select('g');
            if (!svgGroup || svgGroup.empty()) {
                logger.debug('TopologyGraphComponent: Creating new SVG group');
                svgGroup = this.svg.append('g');
                // Apply initial zoom to show the graph more zoomed in
                svgGroup.attr('transform', 'scale(1.4) translate(-200, -150)');
            }

            // Clear existing graph elements but preserve the main group and its transform
            svgGroup.selectAll('.graph-element').remove();

            // Create links
            const link = svgGroup.append('g')
                .attr('class', 'graph-element')
                .selectAll('line')
                .data(links)
                .enter().append('line')
                .attr('stroke', d => this.getLinkColor(d.latency))
                .attr('stroke-opacity', 0.7)
                .attr('stroke-width', d => Math.max(1, Math.min(3, d.latency / 8)))
                .attr('marker-end', null);

            // Create nodes
            const node = svgGroup.append('g')
                .attr('class', 'graph-element')
                .selectAll('g')
                .data(nodes)
                .enter().append('g')
                .attr('class', 'node')
                .call(this.drag(this.simulation));

            // Add circles to nodes
            node.append('circle')
                .attr('r', d => this.getNodeRadius(d.status))
                .attr('fill', d => this.getNodeColor(d.status))
                .attr('stroke', '#fff')
                .attr('stroke-width', 2);

            // Status indicator
            node.append('circle')
                .attr('r', 3)
                .attr('fill', d => this.getStatusIndicatorColor(d.status))
                .attr('cx', -8)
                .attr('cy', -8);

            // Hostname
            node.append('text')
                .text(d => d.hostname.length > 15 ? d.hostname.substring(0, 15) + '...' : d.hostname)
                .attr('x', 15)
                .attr('y', 4)
                .attr('font-size', '13px')
                .attr('fill', 'var(--text-primary)')
                .attr('font-weight', '500');

            // IP
            node.append('text')
                .text(d => d.ip)
                .attr('x', 15)
                .attr('y', 22)
                .attr('font-size', '11px')
                .attr('fill', 'var(--text-secondary)');

            // App label (between IP and Status)
            node.append('text')
                .text(d => (d.labels && d.labels.app) ? String(d.labels.app) : '')
                .attr('x', 15)
                .attr('y', 38)
                .attr('font-size', '11px')
                .attr('fill', 'var(--text-secondary)')
                .attr('font-weight', '500')
                .attr('display', d => (d.labels && d.labels.app) ? null : 'none');

            // Status text
            node.append('text')
                .text(d => d.status)
                .attr('x', 15)
                .attr('y', 56)
                .attr('font-size', '11px')
                .attr('fill', d => this.getNodeColor(d.status))
                .attr('font-weight', '600');

            // Latency labels on links
            const linkLabels = svgGroup.append('g')
                .attr('class', 'graph-element')
                .selectAll('text')
                .data(links)
                .enter().append('text')
                .attr('font-size', '12px')
                .attr('fill', 'var(--text-primary)')
                .attr('font-weight', '600')
                .attr('text-anchor', 'middle')
                .style('text-shadow', '0 1px 2px rgba(0, 0, 0, 0.8)')
                .text(d => `${d.latency}ms`);

            // Simulation
            if (!this.simulation) {
                this.simulation = d3.forceSimulation(nodes)
                    .force('link', d3.forceLink(links).id(d => d.id).distance(300))
                    .force('charge', d3.forceManyBody().strength(-800))
                    .force('center', d3.forceCenter(this.width / 2, this.height / 2))
                    .force('collision', d3.forceCollide().radius(80));

                this.simulation.on('tick', () => {
                    link
                        .attr('x1', d => d.source.x)
                        .attr('y1', d => d.source.y)
                        .attr('x2', d => d.target.x)
                        .attr('y2', d => d.target.y);

                    linkLabels
                        .attr('x', d => (d.source.x + d.target.x) / 2)
                        .attr('y', d => (d.source.y + d.target.y) / 2 - 5);

                    node
                        .attr('transform', d => `translate(${d.x},${d.y})`);
                });
            } else {
                this.simulation.nodes(nodes);
                this.simulation.force('link').links(links);
                this.simulation.alpha(0.3).restart();
            }

            // Node interactions
            node.on('click', (event, d) => {
                this.viewModel.selectNode(d.id);
                this.updateSelection(d.id);
                if (this.isDesktop()) {
                    // Desktop: open slide-in drawer, reuse NodeDetailsComponent
                    this.openDrawerForNode(d);
                } else {
                    // Mobile/low-res: keep existing overlay
                    this.showMemberCardOverlay(d);
                }
            });

            node.on('mouseover', (event, d) => {
                d3.select(event.currentTarget).select('circle')
                    .attr('r', d => this.getNodeRadius(d.status) + 4)
                    .attr('stroke-width', 3);
                this.showTooltip(d, event.pageX, event.pageY);
            });

            node.on('mouseout', (event, d) => {
                d3.select(event.currentTarget).select('circle')
                    .attr('r', d => this.getNodeRadius(d.status))
                    .attr('stroke-width', 2);
                this.hideTooltip();
            });

            node.on('mousemove', (event, d) => {
                this.moveTooltip(event.pageX, event.pageY);
            });

            link.on('mouseover', (event, d) => {
                d3.select(event.currentTarget)
                    .attr('stroke-width', d => Math.max(2, Math.min(4, d.latency / 6)))
                    .attr('stroke-opacity', 0.9);
            });

            link.on('mouseout', (event, d) => {
                d3.select(event.currentTarget)
                    .attr('stroke-width', d => Math.max(1, Math.min(3, d.latency / 8)))
                    .attr('stroke-opacity', 0.7);
            });

            this.addLegend(svgGroup);
        } catch (error) {
            logger.error('Failed to render graph:', error);
        }
    }

    addLegend(svgGroup) {
        const legend = svgGroup.append('g')
            .attr('class', 'graph-element')
            .attr('transform', `translate(120, 120)`)  // Hidden by CSS opacity
            .style('opacity', '0');

        legend.append('rect')
            .attr('width', 320)
            .attr('height', 120)
            .attr('fill', 'rgba(0, 0, 0, 0.7)')
            .attr('rx', 8)
            .attr('stroke', 'rgba(255, 255, 255, 0.2)')
            .attr('stroke-width', 1);

        const nodeLegend = legend.append('g')
            .attr('transform', 'translate(20, 20)');
        
        nodeLegend.append('text')
            .text('Node Status:')
            .attr('x', 0)
            .attr('y', 0)
            .attr('font-size', '14px')
            .attr('font-weight', '600')
            .attr('fill', '#ecf0f1');

        const statuses = [
            { status: 'ACTIVE', color: '#10b981', y: 20 },
            { status: 'INACTIVE', color: '#f59e0b', y: 40 },
            { status: 'DEAD', color: '#ef4444', y: 60 }
        ];

        statuses.forEach(item => {
            nodeLegend.append('circle')
                .attr('r', 6)
                .attr('cx', 0)
                .attr('cy', item.y)
                .attr('fill', item.color);

            nodeLegend.append('text')
                .text(item.status)
                .attr('x', 15)
                .attr('y', item.y + 4)
                .attr('font-size', '12px')
                .attr('fill', '#ecf0f1');
        });

        const linkLegend = legend.append('g')
            .attr('transform', 'translate(150, 20)');

        linkLegend.append('text')
            .text('Link Latency:')
            .attr('x', 0)
            .attr('y', 0)
            .attr('font-size', '14px')
            .attr('font-weight', '600')
            .attr('fill', '#ecf0f1');

        const latencies = [
            { range: '≤30ms', color: '#10b981', y: 20 },
            { range: '31-50ms', color: '#f59e0b', y: 40 },
            { range: '>50ms', color: '#ef4444', y: 60 }
        ];

        latencies.forEach(item => {
            linkLegend.append('line')
                .attr('x1', 0)
                .attr('y1', item.y)
                .attr('x2', 20)
                .attr('y2', item.y)
                .attr('stroke', item.color)
                .attr('stroke-width', 2);

            linkLegend.append('text')
                .text(item.range)
                .attr('x', 25)
                .attr('y', item.y + 4)
                .attr('font-size', '12px')
                .attr('fill', '#ecf0f1');
        });
    }

    getNodeRadius(status) {
        switch (status?.toUpperCase()) {
            case 'ACTIVE':
                return 10;
            case 'INACTIVE':
                return 8;
            case 'DEAD':
                return 6;
            default:
                return 8;
        }
    }

    getStatusIndicatorColor(status) {
        switch (status?.toUpperCase()) {
            case 'ACTIVE':
                return '#10b981';
            case 'INACTIVE':
                return '#f59e0b';
            case 'DEAD':
                return '#ef4444';
            default:
                return '#6b7280';
        }
    }

    getLinkColor(latency) {
        if (latency <= 30) return '#10b981';
        if (latency <= 50) return '#f59e0b';
        return '#ef4444';
    }

    getNodeColor(status) {
        switch (status?.toUpperCase()) {
            case 'ACTIVE':
                return '#10b981';
            case 'INACTIVE':
                return '#f59e0b';
            case 'DEAD':
                return '#ef4444';
            default:
                return '#6b7280';
        }
    }

    drag(simulation) {
        return d3.drag()
            .on('start', function(event, d) {
                if (!event.active && simulation && simulation.alphaTarget) {
                    simulation.alphaTarget(0.3).restart();
                }
                d.fx = d.x;
                d.fy = d.y;
            })
            .on('drag', function(event, d) {
                d.fx = event.x;
                d.fy = event.y;
            })
            .on('end', function(event, d) {
                if (!event.active && simulation && simulation.alphaTarget) {
                    simulation.alphaTarget(0);
                }
                d.fx = null;
                d.fy = null;
            });
    }

    updateSelection(selectedNodeId) {
        // Update visual selection
        if (!this.svg || !this.isInitialized) {
            return;
        }
        
        this.svg.selectAll('.node').select('circle')
            .attr('stroke-width', d => d.id === selectedNodeId ? 4 : 2)
            .attr('stroke', d => d.id === selectedNodeId ? '#2196F3' : '#fff');
    }

    handleRefresh() {
        logger.debug('TopologyGraphComponent: handleRefresh called');
        
        if (!this.isInitialized) {
            logger.debug('TopologyGraphComponent: Component not yet initialized, ensuring initialization...');
            this.ensureInitialized().then(() => {
                // Refresh after initialization
                this.viewModel.updateNetworkTopology();
            }).catch(error => {
                logger.error('TopologyGraphComponent: Failed to initialize for refresh:', error);
            });
            return;
        }
        
        logger.debug('TopologyGraphComponent: Calling updateNetworkTopology...');
        this.viewModel.updateNetworkTopology();
    }

    handleLoadingState(isLoading) {
        logger.debug('TopologyGraphComponent: handleLoadingState called with:', isLoading);
        const container = this.findElement('#topology-graph-container');
        
        if (isLoading) {
            container.innerHTML = '<div class="loading"><div>Loading network topology...</div></div>';
        }
    }

    handleError() {
        const error = this.viewModel.get('error');
        if (error) {
            const container = this.findElement('#topology-graph-container');
            container.innerHTML = `<div class="error"><div>Error: ${error}</div></div>`;
        }
    }

    showNoData() {
        const container = this.findElement('#topology-graph-container');
        container.innerHTML = '<div class="no-data"><div>No cluster members found</div></div>';
    }

    showMemberCardOverlay(nodeData) {
        // Create overlay container if it doesn't exist
        let overlayContainer = document.getElementById('member-card-overlay');
        if (!overlayContainer) {
            overlayContainer = document.createElement('div');
            overlayContainer.id = 'member-card-overlay';
            overlayContainer.className = 'member-card-overlay';
            document.body.appendChild(overlayContainer);
        }

        // Create and show the overlay component
        if (!this.memberOverlayComponent) {
            const overlayVM = new ViewModel();
            this.memberOverlayComponent = new MemberCardOverlayComponent(overlayContainer, overlayVM, this.eventBus);
            this.memberOverlayComponent.mount();
        }

        // Convert node data to member data format
        const memberData = {
            ip: nodeData.ip,
            hostname: nodeData.hostname,
            status: this.normalizeStatus(nodeData.status),
            latency: nodeData.latency,
            labels: (nodeData.labels && typeof nodeData.labels === 'object') ? nodeData.labels : (nodeData.resources || {})
        };

        this.memberOverlayComponent.show(memberData);
    }

    // Normalize status from topology format to member card format
    normalizeStatus(status) {
        if (!status) return 'unknown';
        
        const normalized = status.toLowerCase();
        switch (normalized) {
            case 'active':
                return 'active';
            case 'inactive':
                return 'inactive';
            case 'dead':
                return 'offline';
            default:
                return 'unknown';
        }
    }

    // Override render method to display the graph
    render() {
        logger.debug('TopologyGraphComponent: render called');
        if (!this.isInitialized) {
            logger.debug('TopologyGraphComponent: Not initialized yet, skipping render');
            return;
        }
        const nodes = this.viewModel.get('nodes');
        const links = this.viewModel.get('links');
        if (nodes && nodes.length > 0) {
            logger.debug('TopologyGraphComponent: Rendering graph with data');
            this.renderGraph();
        } else {
            logger.debug('TopologyGraphComponent: No data available, showing loading state');
            this.handleLoadingState(true);
        }
    }

    unmount() {
        // Clean up resize listener
        if (this.resizeHandler) {
            window.removeEventListener('resize', this.resizeHandler);
            this.resizeHandler = null;
        }
        
        // Clear resize timeout
        if (this.resizeTimeout) {
            clearTimeout(this.resizeTimeout);
            this.resizeTimeout = null;
        }
        
        // Call parent unmount
        super.unmount();
    }
}

// Minimal Member Card Overlay Component (kept in same file to avoid circular loads)
class MemberCardOverlayComponent extends Component {
    constructor(container, viewModel, eventBus) {
        super(container, viewModel, eventBus);
        this.isVisible = false;
        this.currentMember = null;
    }

    mount() {
        super.mount();
        this.setupEventListeners();
    }

    setupEventListeners() {
        // Close overlay when clicking outside or pressing escape
        this.addEventListener(this.container, 'click', (e) => {
            if (!this.isVisible) return;
            if (e.target === this.container) {
                this.hide();
            }
        });

        this.addEventListener(document, 'keydown', (e) => {
            if (e.key === 'Escape' && this.isVisible) {
                this.hide();
            }
        });
    }

    show(memberData) {
        this.currentMember = memberData;
        this.isVisible = true;
        
        const memberCardHTML = this.renderMemberCard(memberData);
        this.setHTML('', memberCardHTML);
        
        setTimeout(() => {
            this.container.classList.add('visible');
        }, 10);
        
        this.setupMemberCardInteractions();
    }

    hide() {
        this.isVisible = false;
        this.container.classList.remove('visible');
        this.currentMember = null;
    }

    renderMemberCard(member) {
        const statusClass = (member.status && member.status.toUpperCase() === 'ACTIVE') ? 'status-online' : 
                          (member.status && member.status.toUpperCase() === 'INACTIVE') ? 'status-inactive' : 'status-offline';
        const statusIcon = (member.status && member.status.toUpperCase() === 'ACTIVE') ? '🟢' : 
                          (member.status && member.status.toUpperCase() === 'INACTIVE') ? '🟠' : '🔴';
        
        return `
            <div class="member-overlay-content">
                <div class="member-overlay-header">
                    <div class="member-info">
                        <div class="member-row-1">
                            <div class="status-hostname-group">
                                <div class="member-status ${statusClass}">
                                    ${statusIcon}
                                </div>
                                <div class="member-hostname">${member.hostname || 'Unknown Device'}</div>
                            </div>
                            <div class="member-ip">${member.ip || 'No IP'}</div>
                            <div class="member-latency">
                                <span class="latency-label">Latency:</span>
                                <span class="latency-value">${member.latency ? member.latency + 'ms' : 'N/A'}</span>
                            </div>
                        </div>
                        <div class="member-labels" style="display: none;"></div>
                    </div>
                    <button class="member-overlay-close" aria-label="Close">
                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                            <path d="M18 6L6 18M6 6l12 12"/>
                        </svg>
                    </button>
                </div>
                
                <div class="member-overlay-body">
                    <div class="member-card expanded" data-member-ip="${member.ip}">
                        <div class="member-details">
                            <div class="loading-details">Loading detailed information...</div>
                        </div>
                    </div>
                </div>
            </div>
        `;
    }

    setupMemberCardInteractions() {
        const closeBtn = this.findElement('.member-overlay-close');
        if (closeBtn) {
            this.addEventListener(closeBtn, 'click', () => {
                this.hide();
            });
        }

        setTimeout(async () => {
            const memberCard = this.findElement('.member-card');
            if (memberCard) {
                const memberDetails = memberCard.querySelector('.member-details');
                const memberIp = memberCard.dataset.memberIp;
                await this.expandCard(memberCard, memberIp, memberDetails);
            }
        }, 100);
    }

    async expandCard(card, memberIp, memberDetails) {
        try {
            const nodeDetailsVM = new NodeDetailsViewModel();
            const nodeDetailsComponent = new NodeDetailsComponent(memberDetails, nodeDetailsVM, this.eventBus);
            await nodeDetailsVM.loadNodeDetails(memberIp);

            const nodeStatus = nodeDetailsVM.get('nodeStatus');
            if (nodeStatus && nodeStatus.labels) {
                const labelsContainer = document.querySelector('.member-overlay-header .member-labels');
                if (labelsContainer) {
                    labelsContainer.innerHTML = Object.entries(nodeStatus.labels)
                        .map(([key, value]) => `<span class="label-chip">${key}: ${value}</span>`)
                        .join('');
                    labelsContainer.style.display = 'block';
                }
            }
            nodeDetailsComponent.mount();
            card.classList.add('expanded');
        } catch (error) {
            logger.error('Failed to expand member card:', error);
            card.classList.add('expanded');
            const details = card.querySelector('.member-details');
            if (details) {
                details.innerHTML = '<div class="error">Failed to load node details</div>';
            }
        }
    }
}

window.TopologyGraphComponent = TopologyGraphComponent;
window.MemberCardOverlayComponent = MemberCardOverlayComponent; 