"""
Left panel - Flow list with frame type breakdowns
"""

from typing import List, Optional
import curses

from ...models import FlowStats


class FlowListPanel:
    """Left panel showing flows and frame type breakdowns"""
    
    def __init__(self):
        self.selected_item = 0
        self.scroll_offset = 0
    
    def draw(self, stdscr, x_offset: int, y_offset: int, width: int, height: int, 
             flows_list: List[FlowStats], selected_flow: int):
        """Draw the flow list panel"""
        
        # Draw flows table header with enhanced analysis columns
        stdscr.addstr(y_offset, x_offset, "FLOWS (Enhanced Analysis):", curses.A_BOLD)
        headers = f"{'Src:Port':22} {'Dst:Port':22} {'Proto':6} {'Cast':5} {'#Frames':>7} {'Bytes':>7} {'Encoding':12} {'Quality':>7} {'Drift':>8} {'ΔT Avg':>9}"
        stdscr.addstr(y_offset + 1, x_offset, headers[:width-1], curses.A_UNDERLINE)
        
        # Calculate scrolling parameters
        start_row = y_offset + 2
        max_rows = height - 3  # Account for header and title
        total_items = self._get_total_display_items(flows_list)
        
        # Calculate scroll offset to keep selected item visible
        scroll_offset = self._calculate_scroll_offset(selected_flow, max_rows, total_items)
        
        # Draw flows list with frame type breakdowns
        current_row = start_row
        display_item = 0  # Track selectable items (flows + frame types)
        visible_items = 0  # Track items actually drawn
        
        for flow_idx, flow in enumerate(flows_list):
            # Check if main flow line should be displayed
            if display_item >= scroll_offset and visible_items < max_rows:
                # Draw main flow line with new column layout
                src_endpoint = f"{flow.src_ip}:{flow.src_port}" if flow.src_port > 0 else flow.src_ip
                dst_endpoint = f"{flow.dst_ip}:{flow.dst_port}" if flow.dst_port > 0 else flow.dst_ip
                
                # Format bytes with K/M suffix
                bytes_str = self._format_bytes(flow.total_bytes)
                
                # Get encoding information (primary detected protocol)
                encoding_str = self._get_encoding_display(flow)
                
                # Format average time
                avg_time = f"{flow.avg_inter_arrival:.3f}s" if flow.avg_inter_arrival > 0 else "N/A"
                
                # Abbreviate traffic classification
                cast_abbrev = flow.traffic_classification[:4] if flow.traffic_classification != "Unknown" else "Unk"
                
                # Enhanced analysis data
                quality_str = self._format_quality_score(flow)
                drift_str = self._format_drift_info(flow)
                
                line = f"{src_endpoint:22} {dst_endpoint:22} {flow.transport_protocol:6} {cast_abbrev:5} {flow.frame_count:>7} {bytes_str:>7} {encoding_str:12} {quality_str:>7} {drift_str:>8} {avg_time:>9}"
                
                if display_item == selected_flow:
                    stdscr.addstr(current_row, x_offset, line[:width-1], curses.A_REVERSE)
                else:
                    stdscr.addstr(current_row, x_offset, line[:width-1], curses.A_BOLD)
                
                current_row += 1
                visible_items += 1
            
            display_item += 1
            
            # Draw frame type breakdowns for this flow
            if flow.frame_types:
                sorted_frame_types = sorted(flow.frame_types.items(), key=lambda x: x[1].count, reverse=True)
                
                for frame_type, ft_stats in sorted_frame_types:
                    if display_item >= scroll_offset and visible_items < max_rows:
                        # Calculate frame type timing display
                        ft_avg = f"{ft_stats.avg_inter_arrival:.3f}s" if ft_stats.avg_inter_arrival > 0 else "N/A"
                        outlier_count = len(ft_stats.outlier_details) if ft_stats.outlier_details else 0
                        
                        # Create frame type line aligned with enhanced column layout
                        bytes_str_ft = self._format_bytes(ft_stats.total_bytes)
                        
                        # Enhanced analysis for frame types (inherit from parent flow)
                        quality_str_ft = self._format_quality_score(flow) if frame_type.startswith('CH10') or frame_type == 'TMATS' else ""
                        drift_str_ft = self._format_drift_info(flow) if frame_type.startswith('CH10') or frame_type == 'TMATS' else ""
                        
                        ft_line = f"  └─{frame_type:18} {'':22} {'':6} {'':5} {ft_stats.count:>7} {bytes_str_ft:>7} {'':12} {quality_str_ft:>7} {drift_str_ft:>8} {ft_avg:>9}"
                        
                        if display_item == selected_flow:
                            stdscr.addstr(current_row, x_offset, ft_line[:width-1], curses.A_REVERSE)
                        else:
                            stdscr.addstr(current_row, x_offset, ft_line[:width-1])
                        
                        current_row += 1
                        visible_items += 1
                    
                    display_item += 1
    
    def _get_protocol_display(self, flow: FlowStats) -> str:
        """Get display string for flow protocols"""
        if flow.detected_protocol_types:
            # Prioritize specialized protocols
            specialized = {'CHAPTER10', 'PTP', 'IENA'}
            found_specialized = flow.detected_protocol_types & specialized
            if found_specialized:
                return list(found_specialized)[0]
            
            # Use first detected protocol type
            return list(flow.detected_protocol_types)[0]
        
        # Fallback to basic protocols
        if flow.protocols:
            return list(flow.protocols)[0]
        
        return "Unknown"
    
    def _get_total_display_items(self, flows_list: List[FlowStats]) -> int:
        """Calculate total number of selectable items (flows + frame types)"""
        total = 0
        for flow in flows_list:
            total += 1  # Flow itself
            total += len(flow.frame_types)  # Frame types under this flow
        return total
    
    def _calculate_scroll_offset(self, selected_item: int, max_visible: int, total_items: int) -> int:
        """Calculate scroll offset to keep selected item visible"""
        if total_items <= max_visible:
            return 0  # No scrolling needed
        
        # Keep selected item in the middle third of visible area when possible
        middle_position = max_visible // 3
        
        # Calculate ideal scroll offset
        scroll_offset = max(0, selected_item - middle_position)
        
        # Ensure we don't scroll past the end
        max_scroll = max(0, total_items - max_visible)
        scroll_offset = min(scroll_offset, max_scroll)
        
        return scroll_offset
    
    def get_total_display_items(self, flows_list: List[FlowStats]) -> int:
        """Public method to get total display items"""
        return self._get_total_display_items(flows_list)
    
    def _format_bytes(self, bytes_count: int) -> str:
        """Format byte count with K/M/G suffixes, always include magnitude indicator"""
        if bytes_count >= 1_000_000_000:
            return f"{bytes_count / 1_000_000_000:.1f}G"
        elif bytes_count >= 1_000_000:
            return f"{bytes_count / 1_000_000:.1f}M"
        elif bytes_count >= 1_000:
            return f"{bytes_count / 1_000:.1f}K"
        else:
            return f"{bytes_count}B"  # Add "B" for plain bytes
    
    def _get_encoding_display(self, flow: FlowStats) -> str:
        """Get the primary encoding/application protocol for display"""
        # Prioritize specialized protocols (Chapter 10, PTP, IENA)
        if flow.detected_protocol_types:
            specialized = {'CH10', 'PTP', 'IENA', 'Chapter10', 'TMATS'}
            found_specialized = flow.detected_protocol_types.intersection(specialized)
            if found_specialized:
                return list(found_specialized)[0]
            
            # Use first detected protocol type
            return list(flow.detected_protocol_types)[0]
        
        # Fallback to frame types if available
        if flow.frame_types:
            frame_types = list(flow.frame_types.keys())
            # Look for interesting frame types first
            priority_types = ['TMATS', 'CH10-Data', 'PTP-Sync', 'IENA-P', 'IENA-D']
            for ptype in priority_types:
                if ptype in frame_types:
                    return ptype
            return frame_types[0]
        
        # Last resort - check basic protocols
        if flow.protocols:
            app_protocols = {'DNS', 'HTTP', 'HTTPS', 'NTP', 'DHCP'}
            found_app = flow.protocols.intersection(app_protocols)
            if found_app:
                return list(found_app)[0]
        
        return "Unknown"
    
    def _format_quality_score(self, flow: FlowStats) -> str:
        """Format quality score for display"""
        enhanced = flow.enhanced_analysis
        
        if enhanced.decoder_type == "Standard" or enhanced.avg_frame_quality == 0:
            return "N/A"
        
        # Format quality as percentage
        quality = enhanced.avg_frame_quality
        if quality >= 90:
            return f"{quality:.0f}%"
        elif quality >= 70:
            return f"{quality:.0f}%"
        else:
            return f"{quality:.0f}%"
    
    def _format_drift_info(self, flow: FlowStats) -> str:
        """Format clock drift information for display"""
        enhanced = flow.enhanced_analysis
        
        if not enhanced.has_internal_timing or enhanced.avg_clock_drift_ppm == 0:
            return "N/A"
        
        # Format drift in PPM
        drift_ppm = abs(enhanced.avg_clock_drift_ppm)
        if drift_ppm >= 1000:
            return f"{drift_ppm/1000:.1f}K"  # Show in thousands
        elif drift_ppm >= 100:
            return f"{drift_ppm:.0f}ppm"
        elif drift_ppm >= 10:
            return f"{drift_ppm:.1f}ppm"
        else:
            return f"{drift_ppm:.2f}ppm"