"""
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)
    
    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


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: auto;
        min-height: 1;
        max-height: 1;
        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: auto;
        min-height: 1;
        padding: 0;  /* Remove padding to fit text in 1 row */
        text-align: center;  /* Center text in button */
        content-align: center middle;
    }
    
    #btn-overview {
        margin: 0 1 0 0;  /* Overview button - same spacing */
        height: auto;
        min-height: 1;
        padding: 0;  /* Remove padding to fit text in 1 row */
        text-align: center;  /* Center text in button */
        content-align: center middle;
    }
    
    #filter-bar Button:hover {
        background: #0080ff;
    }
    
    #filter-bar Button.-active {
        background: #0080ff;
        text-style: bold;
    }
    
    #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):
        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
        self.predefined_frame_types = [
            'CH10-Data',
            'UDP', 
            'PTP-Sync',
            'PTP-Signaling',
            'PTP-FollowUp',
            'TMATS',
            'TCP',
            'CH10-Multi-Source',
            'CH10-Extended'
        ]
        
    def compose(self):
        """Create the filter bar and flow grid"""
        # Filter button bar at top
        with Horizontal(id="filter-bar"):
            # Overview button (hotkey 1) - compact format
            overview_btn = Button("1.Overview", id="btn-overview", classes="-active")
            self.frame_type_buttons["Overview"] = overview_btn
            yield overview_btn
            
            # Create predefined frame type buttons at initialization
            # Note: Initial order will be updated by refresh_frame_types() to sort by count
            hotkeys = ['2', '3', '4', '5', '6', '7', '8', '9', '0']
            for i, frame_type in enumerate(self.predefined_frame_types):
                if i < len(hotkeys):
                    # Start with 0 count - will be updated during data refresh
                    btn = FrameTypeButton(frame_type, hotkeys[i], 0)
                    self.frame_type_buttons[frame_type] = btn
                    yield 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
    
    def on_mount(self):
        """Initialize the view"""
        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()
    
    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 frame type button counts and reorder by count (highest to left)"""
        # Throttle button refresh to prevent race conditions
        import time
        current_time = time.time()
        if current_time - self._last_refresh_time < self._refresh_throttle_seconds:
            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()
        
        # If no frame types yet, skip button update
        if not frame_types:
            return
        
        # Calculate flow counts for all frame types (including new ones)
        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
        
        # Sort frame types by count (highest first)
        sorted_frame_types = sorted(frame_type_flow_counts.items(), key=lambda x: x[1], reverse=True)
        
        # Check if the order has actually changed to avoid unnecessary updates
        # Include predefined frame types even with 0 count to avoid unnecessary recreation
        current_order = [ft for ft, _ in sorted_frame_types[:9] 
                        if frame_type_flow_counts[ft] > 0 or ft in self.predefined_frame_types]
        
        # Get the previous order from button tracking
        previous_order = [ft for ft in self.frame_type_buttons.keys() if ft != "Overview"]
        
        # Check if we can just update counts instead of recreating buttons
        # During early loading, be more flexible about order changes for predefined types
        can_update_counts_only = False
        
        if len(current_order) == len(previous_order):
            # Same number of buttons - check if they're the same set (order can be different during loading)
            current_set = set(current_order)
            previous_set = set(previous_order)
            
            if current_set == previous_set:
                # Same frame types, just update counts without recreating
                can_update_counts_only = True
            elif all(ft in self.predefined_frame_types for ft in current_set.symmetric_difference(previous_set)):
                # Only predefined types differ - still safe to just update counts during loading  
                can_update_counts_only = True
        
        if can_update_counts_only:
            # Just update counts in existing buttons
            self._update_button_counts(frame_type_flow_counts)
            return
        
        # Order changed, need to recreate buttons
        try:
            filter_bar = self.query_one("#filter-bar", Horizontal)
        except Exception:
            # Filter bar not available yet
            return
        
        # Remove all buttons except Overview - use a safer approach
        overview_btn = None
        buttons_to_remove = []
        
        for widget in list(filter_bar.children):
            if widget.id == "btn-overview":
                overview_btn = widget
            else:
                buttons_to_remove.append(widget)
        
        # Remove non-overview buttons
        for widget in buttons_to_remove:
            try:
                if widget.parent:  # Only remove if still has parent
                    widget.remove()
            except Exception:
                pass
        
        # Clear frame type buttons dict and keep overview
        self.frame_type_buttons.clear()
        if overview_btn:
            self.frame_type_buttons["Overview"] = overview_btn
        
        # Add new buttons in sorted order
        hotkeys = ['2', '3', '4', '5', '6', '7', '8', '9', '0']
        for i, (frame_type, flow_count) in enumerate(sorted_frame_types[:9]):
            # Always show predefined frame types, even with 0 count during early loading
            # Only skip if count is 0 AND it's not a predefined frame type
            should_show = (flow_count > 0) or (frame_type in self.predefined_frame_types)
            
            if i < len(hotkeys) and should_show:
                btn = FrameTypeButton(frame_type, hotkeys[i], flow_count)
                self.frame_type_buttons[frame_type] = btn
                try:
                    filter_bar.mount(btn)
                except Exception:
                    # If mount fails, skip this button
                    pass
        
        # Update button highlighting
        self._update_button_highlighting()
        
        # 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()
    
    def _update_button_counts(self, frame_type_flow_counts: dict):
        """Update button counts without recreating buttons"""
        for frame_type, btn in self.frame_type_buttons.items():
            if frame_type == "Overview":
                continue
            
            if frame_type in frame_type_flow_counts:
                flow_count = frame_type_flow_counts[frame_type]
                # Extract hotkey from current label
                try:
                    hotkey = btn.label.split('.')[0]
                    short_name = btn._shorten_frame_type(frame_type)
                    btn.label = f"{hotkey}.{short_name}({flow_count})"
                    btn.count = flow_count
                except Exception:
                    # If label update fails, ignore
                    pass
    
    
    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()
    
    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")