"""
Filtered Flow View Widget - Grid with frame type filter buttons
"""

from textual.widgets import Button, DataTable, Static
from textual.containers import Vertical, Horizontal
from textual.reactive import reactive
from textual.message import Message
from textual.binding import Binding
from typing import TYPE_CHECKING, Optional, List, Dict
from rich.text import Text

if TYPE_CHECKING:
    from ....analysis.core import EthernetAnalyzer
    from ....models import FlowStats


class FrameTypeButton(Button):
    """Button for frame type filtering"""
    
    def __init__(self, frame_type: str, hotkey: str, count: int = 0, **kwargs):
        self.frame_type = frame_type
        self.count = count
        # Shorten frame type names for 1-row buttons
        short_name = self._shorten_frame_type(frame_type)
        label = f"{hotkey}.{short_name}({count})"  # Remove spaces to be more compact
        # Create valid ID by removing/replacing invalid characters
        safe_id = frame_type.replace('-', '_').replace(':', '_').replace('(', '_').replace(')', '_').replace(' ', '_').replace('.', '_')
        super().__init__(label, id=f"btn-{safe_id}", **kwargs)
        
        # Ensure proper styling from initialization
        self.styles.background = "#404040"
        self.styles.color = "white"
    
    def _shorten_frame_type(self, frame_type: str) -> str:
        """Shorten frame type names for compact 1-row buttons"""
        abbreviations = {
            'CH10-Data': 'CH10',
            'CH10-Multi-Source': 'Multi',
            'CH10-Extended': 'Ext',
            'CH10-ACTTS': 'ACTTS',
            'PTP-Signaling': 'PTP-S',
            'PTP-FollowUp': 'PTP-F',
            'PTP-Sync': 'PTP',
            'PTP-Unknown (0x6)': 'PTP-U',
            'UDP': 'UDP',
            'TMATS': 'TMATS',
            'TCP': 'TCP'
        }
        return abbreviations.get(frame_type, frame_type[:6])  # Max 6 chars for unknown types


import time
import traceback

def debug_log(message):
    """Debug logging with timestamp"""
    timestamp = time.strftime("%H:%M:%S.%f")[:-3]
    print(f"[{timestamp}] 🔍 DEBUG: {message}")
    
def debug_button_state(frame_type_buttons, phase):
    """Log current button state"""
    debug_log(f"=== BUTTON STATE - {phase} ===")
    debug_log(f"Total buttons in dict: {len(frame_type_buttons)}")
    for name, btn in frame_type_buttons.items():
        if hasattr(btn, 'parent') and btn.parent:
            parent_info = f"parent: {btn.parent.__class__.__name__}"
        else:
            parent_info = "NO PARENT"
        debug_log(f"  {name}: {btn.__class__.__name__} ({parent_info})")
    debug_log("=" * 40)
class FilteredFlowView(Vertical):
    """Flow grid with frame type filter buttons"""
    
    BINDINGS = [
        Binding("alt+1", "sort_column(0)", "Sort by column 1", show=False),
        Binding("alt+2", "sort_column(1)", "Sort by column 2", show=False),
        Binding("alt+3", "sort_column(2)", "Sort by column 3", show=False),
        Binding("alt+4", "sort_column(3)", "Sort by column 4", show=False),
        Binding("alt+5", "sort_column(4)", "Sort by column 5", show=False),
        Binding("alt+6", "sort_column(5)", "Sort by column 6", show=False),
        Binding("alt+7", "sort_column(6)", "Sort by column 7", show=False),
        Binding("alt+8", "sort_column(7)", "Sort by column 8", show=False),
        Binding("alt+9", "sort_column(8)", "Sort by column 9", show=False),
        Binding("alt+0", "sort_column(9)", "Sort by column 10", show=False),
    ]
    
    DEFAULT_CSS = """
    FilteredFlowView {
        height: 1fr;
    }
    
    #filter-bar {
        height: 3;  /* Fixed height to match button height */
        min-height: 3;
        max-height: 3;
        background: #262626;
        padding: 0 1;
        dock: top;
        layout: horizontal;
    }
    
    #filter-bar Button {
        margin: 0 1 0 0;  /* Consistent right spacing */
        min-width: 10;  /* Reduced for compact labels */
        height: 3;  /* Fixed height to ensure text visibility */
        max-height: 3;  /* Prevent button from growing */
        padding: 0 1;  /* Minimal horizontal padding for text readability */
        text-align: center;  /* Center text in button */
        content-align: center middle;
        background: #404040;  /* Default gray background - not black */
        color: white;
        border: solid #666666;  /* Visible border - Textual format */
    }
    
    #btn-overview {
        margin: 0 1 0 0;  /* Overview button - same spacing */
        height: 3;  /* Fixed height to ensure text visibility */
        max-height: 3;  /* Prevent button from growing */
        padding: 0 1;  /* Minimal horizontal padding for text readability */
        text-align: center;  /* Center text in button */
        content-align: center middle;
        background: #404040;  /* Default gray background - not black */
        color: white;
        border: solid #666666;  /* Visible border - Textual format */
    }
    
    #filter-bar Button:hover {
        background: #0080ff;
    }
    
    #filter-bar Button.-active {
        background: #0080ff;
        color: white;  /* Ensure text is visible on active state */
        text-style: bold;
        border: solid #0080ff;  /* Match border to background - Textual format */
    }
    
    #filtered-flow-table {
        height: 1fr;
    }
    """
    
    selected_frame_type = reactive("Overview")
    
    class FrameTypeSelected(Message):
        """Message when frame type filter is selected"""
        def __init__(self, frame_type: str) -> None:
            self.frame_type = frame_type
            super().__init__()
    
    def __init__(self, analyzer: 'EthernetAnalyzer', **kwargs):
        debug_log("FilteredFlowView.__init__ called")
        super().__init__(**kwargs)
        self.analyzer = analyzer
        self.frame_type_buttons = {}
        self.flow_table = None
        self._last_frame_types = set()  # Track frame types to avoid unnecessary refreshes
        self._buttons_created = False  # Track if buttons have been created to avoid flicker
        
        # Table sorting state
        self.sort_column = None  # Index of column to sort by (None = no sorting)
        self.sort_reverse = False  # True for descending, False for ascending
        
        # Button refresh throttling to prevent race conditions
        self._last_refresh_time = 0
        self._refresh_throttle_seconds = 1.0  # Only refresh buttons once per second
        
        # Predefined frame types that will have buttons created at initialization
        # Order is now static and will not change based on counts during parsing
        self.predefined_frame_types = [
            'UDP',           # Most common transport protocol
            'CH10-Data',     # Common Chapter 10 data frames
            'PTP-Sync',      # PTP synchronization
            'PTP-Signaling', # PTP signaling
            'TMATS',         # Telemetry metadata
            'TCP',           # TCP transport
            'PTP-FollowUp',  # PTP follow-up
            'CH10-Multi-Source',
            'CH10-Extended'
        ]
        
    def compose(self):
        """Create the filter bar and flow grid - ALL BUTTONS CREATED ONCE, NEVER DESTROYED"""
        debug_log("compose() - Creating filter bar and ALL buttons at initialization")
        debug_button_state(self.frame_type_buttons, "BEFORE_COMPOSE")
        
        # Filter button bar at top
        with Horizontal(id="filter-bar"):
            # Overview button (hotkey 1) - always visible, always active initially
            overview_btn = Button("1.Overview", id="btn-overview", classes="-active")
            overview_btn.styles.background = "#0080ff"  # Active blue background
            overview_btn.styles.color = "white"
            self.frame_type_buttons["Overview"] = overview_btn
            yield overview_btn
            
            # Create ALL possible frame type buttons at initialization - NEVER RECREATED
            # Static order prevents any tab reordering throughout the application lifecycle
            hotkeys = ['2', '3', '4', '5', '6', '7', '8', '9', '0']
            
            # Create buttons for ALL predefined frame types
            for i, frame_type in enumerate(self.predefined_frame_types):
                if i < len(hotkeys):
                    # Start with 0 count, initially hidden - visibility managed by refresh logic
                    btn = FrameTypeButton(frame_type, hotkeys[i], 0)
                    btn.visible = False  # Hidden until data is available
                    self.frame_type_buttons[frame_type] = btn
                    yield btn
            
            # Create placeholder buttons for dynamic frame types discovered during parsing
            # These will be activated/shown as new frame types are discovered
            remaining_hotkeys = len(self.predefined_frame_types)
            for i in range(remaining_hotkeys, len(hotkeys)):
                # Create placeholder button that can be reassigned to new frame types
                placeholder_btn = FrameTypeButton("", hotkeys[i], 0)
                placeholder_btn.visible = False  # Hidden until assigned to a frame type
                placeholder_btn.placeholder_index = i  # Track which placeholder this is
                # Use a special key for placeholders
                self.frame_type_buttons[f"__placeholder_{i}__"] = placeholder_btn
                yield placeholder_btn
        
        # Flow data table
        self.flow_table = DataTable(
            id="filtered-flow-table",
            cursor_type="row",
            zebra_stripes=True,
            show_header=True,
            show_row_labels=False
        )
        yield self.flow_table
        debug_log("compose() - All widgets created")
        debug_button_state(self.frame_type_buttons, "AFTER_COMPOSE")
    
    def on_mount(self):
        """Initialize the view"""
        debug_log("on_mount() - Initializing view")
        debug_button_state(self.frame_type_buttons, "BEFORE_MOUNT_SETUP")
        self._setup_flow_table()
        # Mark buttons as created since we pre-created them in compose()
        self._buttons_created = True
        # Update button counts and data
        self.refresh_frame_types()
        self.refresh_flow_data()
        # Ensure Overview button starts highlighted
        self._update_button_highlighting()
        debug_log("on_mount() - Initialization complete")
        debug_button_state(self.frame_type_buttons, "AFTER_MOUNT_COMPLETE")
    
    def _setup_flow_table(self):
        """Setup table columns based on selected frame type"""
        table = self.flow_table
        table.clear(columns=True)
        
        if self.selected_frame_type == "Overview":
            # Overview columns with individual frame type columns
            table.add_column("#", width=4, key="num")
            table.add_column("Source", width=18, key="source")
            table.add_column("Destination", width=18, key="dest")
            table.add_column("Protocol", width=8, key="protocol")
            table.add_column("Total", width=8, key="total_packets")
            
            # Add columns for each detected frame type
            all_frame_types = self._get_all_frame_types()
            for frame_type in sorted(all_frame_types.keys(), key=lambda x: all_frame_types[x], reverse=True):
                # Shorten column name for better display
                short_name = self._shorten_frame_type_name(frame_type)
                # Create safe key for column
                safe_key = frame_type.replace('-', '_').replace(':', '_').replace('(', '_').replace(')', '_').replace(' ', '_').replace('.', '_')
                table.add_column(short_name, width=8, key=f"ft_{safe_key}")
            
            table.add_column("Status", width=10, key="status")
        else:
            # Frame type specific columns
            table.add_column("#", width=4, key="num")
            table.add_column("Source", width=20, key="source")
            table.add_column("Destination", width=20, key="dest")
            table.add_column("Protocol", width=8, key="protocol")
            table.add_column(f"{self.selected_frame_type} Packets", width=12, key="ft_packets")
            table.add_column("Avg ΔT", width=10, key="avg_delta")
            table.add_column("Std ΔT", width=10, key="std_delta")
            table.add_column("Min ΔT", width=10, key="min_delta")
            table.add_column("Max ΔT", width=10, key="max_delta")
            table.add_column("Outliers", width=8, key="outliers")
            table.add_column("Quality", width=8, key="quality")
    
    def refresh_frame_types(self):
        """Update button visibility and content - NEVER CREATE OR DESTROY BUTTONS"""
        debug_log("refresh_frame_types() - Starting refresh (VISIBILITY-ONLY MODE)")
        debug_button_state(self.frame_type_buttons, "BEFORE_REFRESH")
        # Throttle button refresh to prevent race conditions
        import time
        current_time = time.time()
        if current_time - self._last_refresh_time < self._refresh_throttle_seconds:
            debug_log("refresh_frame_types() - THROTTLED, skipping refresh")
            return  # Skip refresh if called too recently
        self._last_refresh_time = current_time
        
        # Get all detected frame types with their total packet counts
        frame_types = self._get_all_frame_types()
        
        # Calculate flow counts for all frame types
        frame_type_flow_counts = {}
        for frame_type in frame_types.keys():
            flow_count = sum(1 for flow in self.analyzer.flows.values() if frame_type in flow.frame_types)
            frame_type_flow_counts[frame_type] = flow_count
        
        # UPDATE PREDEFINED FRAME TYPE BUTTONS (show/hide and update counts only)
        for frame_type in self.predefined_frame_types:
            if frame_type in self.frame_type_buttons:
                btn = self.frame_type_buttons[frame_type]
                if frame_type in frame_type_flow_counts:
                    flow_count = frame_type_flow_counts[frame_type]
                    # Update button content only
                    hotkey = btn.label.split('.')[0] if '.' in btn.label else '?'
                    short_name = btn._shorten_frame_type(frame_type)
                    btn.label = f"{hotkey}.{short_name}({flow_count})"
                    btn.count = flow_count
                    
                    # Show button if it has data or is predefined (always show predefined during loading)
                    should_show = flow_count > 0 or frame_type in self.predefined_frame_types
                    btn.visible = should_show
                else:
                    # No data for this frame type yet, keep hidden but maintain button
                    btn.visible = False
        
        # HANDLE NEW FRAME TYPES - assign to placeholder buttons only
        new_frame_types = set(frame_type_flow_counts.keys()) - set(self.predefined_frame_types)
        placeholder_keys = [k for k in self.frame_type_buttons.keys() if k.startswith("__placeholder_")]
        
        # Find available placeholders (not already assigned)
        assigned_frame_types = set()
        for frame_type in new_frame_types:
            if frame_type in self.frame_type_buttons:
                assigned_frame_types.add(frame_type)
        
        unassigned_new_types = new_frame_types - assigned_frame_types
        available_placeholders = []
        for placeholder_key in placeholder_keys:
            btn = self.frame_type_buttons[placeholder_key]
            if not hasattr(btn, 'assigned_frame_type') or not btn.visible:
                available_placeholders.append(placeholder_key)
        
        # Assign new frame types to available placeholders
        for i, frame_type in enumerate(sorted(unassigned_new_types)):
            if i < len(available_placeholders) and frame_type_flow_counts[frame_type] > 0:
                placeholder_key = available_placeholders[i]
                btn = self.frame_type_buttons[placeholder_key]
                
                # Assign this placeholder to the new frame type
                flow_count = frame_type_flow_counts[frame_type]
                hotkey = str(btn.placeholder_index + 2)  # hotkeys 2-0
                short_name = btn._shorten_frame_type(frame_type)
                btn.label = f"{hotkey}.{short_name}({flow_count})"
                btn.count = flow_count
                btn.frame_type = frame_type
                btn.assigned_frame_type = frame_type
                btn.visible = True
                
                # Also add to frame_type_buttons with the frame type as key for easy lookup
                self.frame_type_buttons[frame_type] = btn
        
        # Update existing assigned placeholder buttons
        for frame_type in assigned_frame_types:
            if frame_type in self.frame_type_buttons:
                btn = self.frame_type_buttons[frame_type]
                flow_count = frame_type_flow_counts[frame_type]
                hotkey = btn.label.split('.')[0] if '.' in btn.label else '?'
                short_name = btn._shorten_frame_type(frame_type)
                btn.label = f"{hotkey}.{short_name}({flow_count})"
                btn.count = flow_count
                btn.visible = flow_count > 0
        
        # Update button highlighting
        self._update_button_highlighting()
        debug_log("refresh_frame_types() - Button visibility and content updated (NO RECREATION)")
        debug_button_state(self.frame_type_buttons, "AFTER_VISIBILITY_UPDATE")
        
        # Track frame types for change detection
        current_frame_types = set(frame_types.keys())
        if current_frame_types != self._last_frame_types:
            self._last_frame_types = current_frame_types
            
            # CRITICAL: Rebuild table columns when frame types change (for Overview mode)
            if self.selected_frame_type == "Overview":
                self._setup_flow_table()
                # Clear existing data before adding new data with new column structure
                self.flow_table.clear()
    
    # _update_button_counts method removed - buttons are now managed by visibility only
    
    
    def refresh_flow_data(self):
        """Refresh the flow table based on selected filter"""
        self.flow_table.clear()
        
        if self.selected_frame_type == "Overview":
            self._show_overview()
        else:
            self._show_frame_type_flows(self.selected_frame_type)
    
    def _show_overview(self):
        """Show all flows in overview mode with frame type columns"""
        flows = list(self.analyzer.flows.values())
        all_frame_types = self._get_all_frame_types()
        sorted_frame_types = sorted(all_frame_types.keys(), key=lambda x: all_frame_types[x], reverse=True)
        
        # Get current table columns to check what frame types are expected
        try:
            table_columns = [col.key for col in self.flow_table._columns]
        except (AttributeError, TypeError):
            # If columns aren't accessible, fall back to using current frame types
            table_columns = []
            
        expected_frame_types = []
        for col_key in table_columns:
            if col_key.startswith("ft_"):
                # Extract frame type from column key
                expected_frame_types.append(col_key[3:])  # Remove "ft_" prefix
        
        # If no frame type columns detected, use sorted frame types directly
        if not expected_frame_types:
            expected_frame_types = [frame_type.replace('-', '_').replace(':', '_').replace('(', '_').replace(')', '_').replace(' ', '_').replace('.', '_') for frame_type in sorted_frame_types]
        
        # Collect all row data first
        all_rows = []
        
        for i, flow in enumerate(flows):
            # Status based on enhanced analysis
            status = "Enhanced" if flow.enhanced_analysis.decoder_type != "Standard" else "Normal"
            status_style = "green" if status == "Enhanced" else "white"
            
            # Start with basic flow info
            row_data = [
                str(i + 1),
                f"{flow.src_ip}:{flow.src_port}",
                f"{flow.dst_ip}:{flow.dst_port}",
                flow.transport_protocol,
                str(flow.frame_count)
            ]
            
            # Add packet count for each frame type column in the order they appear in table
            for expected_ft_key in expected_frame_types:
                # Find the actual frame type that matches this column key
                matching_frame_type = None
                for frame_type in sorted_frame_types:
                    safe_key = frame_type.replace('-', '_').replace(':', '_').replace('(', '_').replace(')', '_').replace(' ', '_').replace('.', '_')
                    if safe_key == expected_ft_key:
                        matching_frame_type = frame_type
                        break
                
                if matching_frame_type and matching_frame_type in flow.frame_types:
                    count = flow.frame_types[matching_frame_type].count
                    if count > 0:
                        colored_count = self._color_code_packet_count(count, all_frame_types[matching_frame_type])
                        row_data.append(colored_count)
                    else:
                        row_data.append("-")
                else:
                    row_data.append("-")
            
            # Add status
            row_data.append(Text(status, style=status_style))
            
            # Store row data with original flow index for key
            all_rows.append((row_data, i))
        
        # Sort rows if sorting is enabled
        if self.sort_column is not None and all_rows:
            all_rows.sort(key=lambda x: self._get_sort_key(x[0], self.sort_column), reverse=self.sort_reverse)
        
        # Add sorted rows to table
        for row_data, original_index in all_rows:
            # CRITICAL: Validate row data matches column count before adding
            try:
                # Get column count for validation
                column_count = len(self.flow_table.ordered_columns) if hasattr(self.flow_table, 'ordered_columns') else 0
                if column_count > 0 and len(row_data) != column_count:
                    # Skip this row if data doesn't match columns - table structure is being updated
                    continue
                    
                self.flow_table.add_row(*row_data, key=f"flow-{original_index}")
            except (ValueError, AttributeError) as e:
                # Skip this row if there's a column mismatch - table is being rebuilt
                continue
    
    def _show_frame_type_flows(self, frame_type: str):
        """Show flows filtered by frame type with timing statistics"""
        flows_with_type = []
        
        for i, flow in enumerate(self.analyzer.flows.values()):
            if frame_type in flow.frame_types:
                flows_with_type.append((i, flow, flow.frame_types[frame_type]))
        
        # Collect all row data first
        all_rows = []
        
        for flow_idx, flow, ft_stats in flows_with_type:
            # Calculate timing statistics
            if ft_stats.inter_arrival_times:
                min_delta = min(ft_stats.inter_arrival_times) * 1000
                max_delta = max(ft_stats.inter_arrival_times) * 1000
            else:
                min_delta = max_delta = 0
            
            # Quality score
            quality = self._calculate_quality(ft_stats)
            quality_text = self._format_quality(quality)
            
            row_data = [
                str(flow_idx + 1),
                f"{flow.src_ip}:{flow.src_port}",
                f"{flow.dst_ip}:{flow.dst_port}",
                flow.transport_protocol,
                str(ft_stats.count),
                f"{ft_stats.avg_inter_arrival * 1000:.1f}ms" if ft_stats.avg_inter_arrival > 0 else "N/A",
                f"{ft_stats.std_inter_arrival * 1000:.1f}ms" if ft_stats.std_inter_arrival > 0 else "N/A",
                f"{min_delta:.1f}ms" if min_delta > 0 else "N/A",
                f"{max_delta:.1f}ms" if max_delta > 0 else "N/A",
                str(len(ft_stats.outlier_frames)),
                quality_text
            ]
            
            # Store row data with original flow index for key
            all_rows.append((row_data, flow_idx))
        
        # Sort rows if sorting is enabled
        if self.sort_column is not None and all_rows:
            all_rows.sort(key=lambda x: self._get_sort_key(x[0], self.sort_column), reverse=self.sort_reverse)
        
        # Add sorted rows to table
        for row_data, original_index in all_rows:
            # CRITICAL: Validate row data matches column count before adding
            try:
                # Get column count for validation
                column_count = len(self.flow_table.ordered_columns) if hasattr(self.flow_table, 'ordered_columns') else 0
                if column_count > 0 and len(row_data) != column_count:
                    # Skip this row if data doesn't match columns - table structure is being updated
                    continue
                    
                self.flow_table.add_row(*row_data, key=f"flow-{original_index}")
            except (ValueError, AttributeError) as e:
                # Skip this row if there's a column mismatch - table is being rebuilt
                continue
    
    def on_button_pressed(self, event: Button.Pressed) -> None:
        """Handle filter button clicks"""
        button = event.button
        
        # Determine frame type from button
        if button.id == "btn-overview":
            self.select_frame_type("Overview")
        else:
            # Extract frame type from button
            for frame_type, btn in self.frame_type_buttons.items():
                if btn == button:
                    self.select_frame_type(frame_type)
                    break
    
    def select_frame_type(self, frame_type: str):
        """Select a frame type filter"""
        if self.selected_frame_type != frame_type:
            self.selected_frame_type = frame_type
            self._setup_flow_table()
            self.refresh_flow_data()
            self.post_message(self.FrameTypeSelected(frame_type))
        
        # Update button highlighting
        self._update_button_highlighting()
        debug_log("refresh_frame_types() - Buttons recreated")
        debug_button_state(self.frame_type_buttons, "AFTER_BUTTON_CREATION")
        debug_log("on_mount() - Initialization complete")
        debug_button_state(self.frame_type_buttons, "AFTER_MOUNT_COMPLETE")
    
    def _update_button_highlighting(self):
        """Update which button appears active/highlighted"""
        for frame_type, btn in self.frame_type_buttons.items():
            if frame_type == self.selected_frame_type:
                btn.add_class("-active")
            else:
                btn.remove_class("-active")
    
    def action_select_filter(self, number: str):
        """Handle number key press for filter selection"""
        if number == '1':
            # Overview
            self.select_frame_type("Overview")
        else:
            # Frame type buttons - find by hotkey
            hotkeys = ['2', '3', '4', '5', '6', '7', '8', '9', '0']
            if number in hotkeys:
                # Find the button with this hotkey
                for frame_type, btn in self.frame_type_buttons.items():
                    if frame_type != "Overview" and hasattr(btn, 'frame_type'):
                        # Check if this button's label starts with this number
                        if btn.label.plain.startswith(f"{number}."):
                            self.select_frame_type(frame_type)
                            break
    
    def action_sort_column(self, column_index: int):
        """Sort table by specified column index (0-based)"""
        # Check if we have enough columns
        if not self.flow_table or not hasattr(self.flow_table, 'ordered_columns'):
            return
        
        if column_index >= len(self.flow_table.ordered_columns):
            return  # Column doesn't exist
        
        # Toggle sort direction if same column, otherwise start with ascending
        if self.sort_column == column_index:
            self.sort_reverse = not self.sort_reverse
        else:
            self.sort_column = column_index
            self.sort_reverse = False
        
        # Refresh data with new sorting
        self.refresh_flow_data()
    
    def _get_sort_key(self, row_data: list, column_index: int):
        """Get sort key for a row based on column index"""
        if column_index >= len(row_data):
            return ""
        
        value = row_data[column_index]
        
        # Handle Text objects (extract plain text)
        if hasattr(value, 'plain'):
            text_value = value.plain
        else:
            text_value = str(value)
        
        # Try to convert to number for numeric sorting
        try:
            # Handle values like "1,105" (remove commas)
            if ',' in text_value:
                text_value = text_value.replace(',', '')
            
            # Handle values with units like "102.2ms" or "1.5MB"
            if text_value.endswith('ms'):
                return float(text_value[:-2])
            elif text_value.endswith('MB'):
                return float(text_value[:-2]) * 1000000
            elif text_value.endswith('KB'):
                return float(text_value[:-2]) * 1000
            elif text_value.endswith('B'):
                return float(text_value[:-1])
            elif text_value.endswith('%'):
                return float(text_value[:-1])
            elif text_value == "N/A" or text_value == "-":
                return -1  # Sort N/A and "-" values to the end
            else:
                return float(text_value)
        except (ValueError, AttributeError):
            # For string values, use alphabetical sorting
            return text_value.lower()
    
    def _format_bytes(self, bytes_val: int) -> str:
        """Format bytes to human readable"""
        if bytes_val < 1024:
            return f"{bytes_val}B"
        elif bytes_val < 1024 * 1024:
            return f"{bytes_val / 1024:.1f}KB"
        else:
            return f"{bytes_val / (1024 * 1024):.1f}MB"
    
    def _calculate_quality(self, ft_stats) -> float:
        """Calculate quality score for frame type stats"""
        if ft_stats.count == 0:
            return 0.0
        
        outlier_rate = len(ft_stats.outlier_frames) / ft_stats.count
        consistency = 1.0 - min(outlier_rate * 2, 1.0)
        return consistency * 100
    
    def _format_quality(self, quality: float) -> Text:
        """Format quality with color"""
        if quality >= 90:
            return Text(f"{quality:.0f}%", style="green")
        elif quality >= 70:
            return Text(f"{quality:.0f}%", style="yellow")
        else:
            return Text(f"{quality:.0f}%", style="red")
    
    def _get_all_frame_types(self) -> dict:
        """Get all frame types across all flows with their total counts"""
        frame_types = {}
        for flow in self.analyzer.flows.values():
            for frame_type, stats in flow.frame_types.items():
                if frame_type not in frame_types:
                    frame_types[frame_type] = 0
                frame_types[frame_type] += stats.count
        return frame_types
    
    def _shorten_frame_type_name(self, frame_type: str) -> str:
        """Shorten frame type names for better column display"""
        # Common abbreviations for better column display
        abbreviations = {
            'CH10-Data': 'CH10',
            'CH10-Multi-Source': 'Multi',
            'CH10-Extended': 'Ext',
            'CH10-ACTTS': 'ACTTS',
            'PTP-Signaling': 'PTP-Sig',
            'PTP-FollowUp': 'PTP-FU',
            'PTP-Sync': 'PTP-Syn',
            'PTP-Unknown (0x6)': 'PTP-Unk',
            'UDP': 'UDP',
            'TMATS': 'TMATS',
            'TCP': 'TCP'
        }
        return abbreviations.get(frame_type, frame_type[:8])
    
    def _color_code_packet_count(self, count: int, max_count: int) -> Text:
        """Color code packet counts based on relative frequency"""
        if max_count == 0:
            return Text(str(count), style="white")
        
        # Calculate percentage of maximum for this frame type
        percentage = (count / max_count) * 100
        
        if percentage >= 80:  # High volume (80-100% of max)
            return Text(str(count), style="red bold")
        elif percentage >= 50:  # Medium-high volume (50-79% of max)
            return Text(str(count), style="yellow bold")
        elif percentage >= 20:  # Medium volume (20-49% of max)
            return Text(str(count), style="cyan")
        elif percentage >= 5:   # Low volume (5-19% of max)
            return Text(str(count), style="blue")
        else:  # Very low volume (0-4% of max)
            return Text(str(count), style="dim white")