"""
Flow Analysis View - Visual flow overview with protocol detection
Focuses on understanding communication patterns and flow characteristics
"""

import curses
from typing import TYPE_CHECKING, List, Optional, Tuple
from ...models import FlowStats

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


class FlowAnalysisView:
    """
    Flow Analysis View - F1
    
    Primary view for understanding network flows:
    - Flow overview with visual indicators
    - Protocol detection and classification
    - Traffic patterns and volume analysis
    - Enhanced decoder availability
    """
    
    def __init__(self, analyzer: 'EthernetAnalyzer'):
        self.analyzer = analyzer
        self.selected_flow = 0
        self.scroll_offset = 0
        self.show_frame_types = True
        self.column_widths = {}
        
    def draw(self, stdscr, selected_flow_key: Optional[str]):
        """Draw the Flow Analysis view"""
        height, width = stdscr.getmaxyx()
        start_y = 3  # After header
        max_height = height - 2  # Reserve for status bar
        
        flows_list = self._get_flows_list()
        
        if not flows_list:
            stdscr.addstr(start_y + 2, 4, "No network flows detected", curses.A_DIM)
            stdscr.addstr(start_y + 3, 4, "Waiting for packets...", curses.A_DIM)
            return
        
        # Flow summary header
        summary = self.analyzer.get_summary()
        summary_text = (f"Flows: {summary['unique_flows']} | "
                       f"Packets: {summary['total_packets']} | "
                       f"Endpoints: {summary['unique_ips']}")
        
        if self.analyzer.is_live:
            rt_summary = self.analyzer.statistics_engine.get_realtime_summary()
            summary_text += f" | Outliers: {rt_summary.get('total_outliers', 0)}"
        
        stdscr.addstr(start_y, 4, summary_text, curses.A_BOLD)
        
        # Flow analysis area
        flow_start_y = start_y + 2
        self._draw_flow_overview(stdscr, flow_start_y, width, max_height - flow_start_y, flows_list)
    
    def _draw_flow_overview(self, stdscr, start_y: int, width: int, max_height: int, flows_list: List[FlowStats]):
        """Draw comprehensive flow overview"""
        
        # Header
        stdscr.addstr(start_y, 4, "FLOW ANALYSIS", curses.A_BOLD | curses.A_UNDERLINE)
        current_y = start_y + 2
        
        # Calculate dynamic column widths based on available space
        self.column_widths = self._calculate_column_widths(width)
        
        # Column headers with dynamic widths
        headers = self._format_headers()
        stdscr.addstr(current_y, 4, headers, curses.A_UNDERLINE)
        current_y += 1
        
        # Calculate visible range
        visible_flows = max_height - (current_y - start_y) - 2
        start_idx = self.scroll_offset
        end_idx = min(start_idx + visible_flows, len(flows_list))
        
        # Draw flows with sub-rows for each extended protocol/frame type variation
        display_row = 0
        for i in range(start_idx, end_idx):
            flow = flows_list[i]
            
            # Flow selection
            is_selected = (i == self.selected_flow)
            
            # Get distinct extended protocol/frame type combinations
            protocol_frame_combinations = self._get_protocol_frame_combinations(flow)
            
            # Main flow line (summary)
            attr = curses.A_REVERSE if is_selected else curses.A_BOLD
            flow_line = self._format_flow_summary_line(i + 1, flow)
            stdscr.addstr(current_y + display_row, 4, flow_line, attr)
            
            # Enhanced indicator
            if flow.enhanced_analysis.decoder_type != "Standard":
                stdscr.addstr(current_y + display_row, 2, "●", curses.A_BOLD | curses.color_pair(1))
            
            display_row += 1
            
            # Sub-rows for each protocol/frame type combination
            for j, (extended_proto, frame_type, count, percentage) in enumerate(protocol_frame_combinations):
                if current_y + display_row >= current_y + visible_flows:
                    break
                    
                sub_attr = curses.A_REVERSE if (is_selected and j == 0) else curses.A_DIM
                sub_line = self._format_protocol_frame_line(flow, extended_proto, frame_type, count, percentage)
                stdscr.addstr(current_y + display_row, 4, sub_line, sub_attr)
                display_row += 1
            
            # Stop if we've filled the visible area
            if current_y + display_row >= current_y + visible_flows:
                break
        
        # Scroll indicators
        if start_idx > 0:
            stdscr.addstr(current_y, width - 10, "↑ More", curses.A_DIM)
        if end_idx < len(flows_list):
            stdscr.addstr(current_y + visible_flows - 1, width - 10, "↓ More", curses.A_DIM)
    
    def _calculate_column_widths(self, terminal_width: int) -> dict:
        """Calculate dynamic column widths based on available terminal width"""
        # Reserve space for margins and prevent line wrapping
        # 4 chars left margin + 4 chars right margin + 8 safety margin to prevent wrapping
        available_width = terminal_width - 16
        
        # Fixed minimum widths for critical columns
        min_widths = {
            'flow_num': 3,      # " #" 
            'source': 15,       # Compact IP:port
            'proto': 4,         # "UDP", "TCP"
            'destination': 15,  # Compact IP:port
            'extended': 6,      # "CH10", "PTP"
            'frame_type': 8,    # Compact frame type
            'pkts': 6,          # Right-aligned numbers
            'volume': 8,        # Right-aligned with units
            'timing': 8,        # Right-aligned with units
            'quality': 8        # Right-aligned percentages
        }
        
        # Calculate total minimum width needed
        min_total = sum(min_widths.values())
        
        # If we have extra space, distribute it proportionally
        if available_width > min_total:
            extra_space = available_width - min_total
            
            # Distribute extra space to text columns (source, destination, extended, frame_type)
            expandable_columns = ['source', 'destination', 'extended', 'frame_type']
            extra_per_column = extra_space // len(expandable_columns)
            
            widths = min_widths.copy()
            for col in expandable_columns:
                widths[col] += extra_per_column
            
            # Give any remaining space to source and destination
            remaining = extra_space % len(expandable_columns)
            if remaining > 0:
                widths['source'] += remaining // 2
                widths['destination'] += remaining // 2
                if remaining % 2:
                    widths['source'] += 1
        else:
            # Use minimum widths if terminal is too narrow
            widths = min_widths
        
        return widths
    
    def _format_headers(self) -> str:
        """Format column headers using dynamic widths"""
        cw = self.column_widths
        return (
            f"{'#':>{cw['flow_num']-1}} "
            f"{'Source':<{cw['source']}} "
            f"{'Proto':<{cw['proto']}} "
            f"{'Destination':<{cw['destination']}} "
            f"{'Extended':<{cw['extended']}} "
            f"{'Frame Type':<{cw['frame_type']}} "
            f"{'Pkts':>{cw['pkts']}} "
            f"{'Volume':>{cw['volume']}} "
            f"{'Timing':>{cw['timing']}} "
            f"{'Quality':>{cw['quality']}}"
        )
    
    def _get_protocol_frame_combinations(self, flow: FlowStats) -> List[Tuple[str, str, int, float]]:
        """Get distinct extended protocol/frame type combinations for a flow"""
        combinations = []
        total_packets = flow.frame_count
        
        # Group frame types by extended protocol
        protocol_frames = {}
        
        if flow.frame_types:
            for frame_type, ft_stats in flow.frame_types.items():
                # Determine extended protocol for this frame type
                extended_proto = self._get_extended_protocol_for_frame(flow, frame_type)
                
                if extended_proto not in protocol_frames:
                    protocol_frames[extended_proto] = []
                
                protocol_frames[extended_proto].append((frame_type, ft_stats.count))
        else:
            # No frame types, just show the flow-level extended protocol
            extended_proto = self._get_extended_protocol(flow)
            protocol_frames[extended_proto] = [("General", total_packets)]
        
        # Convert to list of tuples with percentages
        for extended_proto, frame_list in protocol_frames.items():
            for frame_type, count in frame_list:
                percentage = (count / total_packets * 100) if total_packets > 0 else 0
                combinations.append((extended_proto, frame_type, count, percentage))
        
        # Sort by count (descending)
        combinations.sort(key=lambda x: x[2], reverse=True)
        return combinations
    
    def _get_extended_protocol_for_frame(self, flow: FlowStats, frame_type: str) -> str:
        """Get extended protocol for a specific frame type"""
        if frame_type.startswith('CH10') or frame_type == 'TMATS':
            return 'CH10'
        elif frame_type.startswith('PTP'):
            return 'PTP'
        elif frame_type == 'IENA':
            return 'IENA'
        elif frame_type == 'NTP':
            return 'NTP'
        else:
            # Fallback to flow-level extended protocol
            return self._get_extended_protocol(flow)
    
    def _format_flow_summary_line(self, flow_num: int, flow: FlowStats) -> str:
        """Format the main flow summary line"""
        # Source with port (left-aligned)
        source = f"{flow.src_ip}:{flow.src_port}"
        max_source_len = self.column_widths.get('source', 24) - 2
        if len(source) > max_source_len:
            ip_space = max_source_len - len(f":{flow.src_port}") - 1  # -1 for ellipsis
            source = f"{flow.src_ip[:ip_space]}…:{flow.src_port}"
        
        # Transport protocol
        protocol = flow.transport_protocol
        
        # Destination with port (left-aligned)
        destination = f"{flow.dst_ip}:{flow.dst_port}"
        max_dest_len = self.column_widths.get('destination', 24) - 2
        if len(destination) > max_dest_len:
            ip_space = max_dest_len - len(f":{flow.dst_port}") - 1  # -1 for ellipsis
            destination = f"{flow.dst_ip[:ip_space]}…:{flow.dst_port}"
        
        # Summary info instead of specific extended/frame
        extended_summary = f"{len(self._get_protocol_frame_combinations(flow))} types"
        frame_summary = "Mixed" if len(flow.frame_types) > 1 else "Single"
        
        # Packet count
        pkt_count = f"{flow.frame_count}"
        
        # Volume with units
        volume = self._format_bytes(flow.total_bytes)
        
        # Timing quality
        if flow.avg_inter_arrival > 0:
            timing_ms = flow.avg_inter_arrival * 1000
            if timing_ms >= 1000:
                timing = f"{timing_ms/1000:.1f}s"
            else:
                timing = f"{timing_ms:.1f}ms"
        else:
            timing = "N/A"
        
        # Quality score
        if flow.enhanced_analysis.decoder_type != "Standard":
            if flow.enhanced_analysis.avg_frame_quality > 0:
                quality = f"{flow.enhanced_analysis.avg_frame_quality:.0f}%"
            else:
                quality = "Enhanced"
        else:
            # Check for outliers
            outlier_pct = len(flow.outlier_frames) / flow.frame_count * 100 if flow.frame_count > 0 else 0
            if outlier_pct > 5:
                quality = f"{outlier_pct:.0f}% Out"
            else:
                quality = "Normal"
        
        cw = self.column_widths
        return (f"{flow_num:>{cw['flow_num']-1}} "
                f"{source:<{cw['source']}} "
                f"{protocol:<{cw['proto']}}  "
                f"{destination:<{cw['destination']}} "
                f"{extended_summary:<{cw['extended']}} "
                f"{frame_summary:<{cw['frame_type']}} "
                f"{pkt_count:>{cw['pkts']}} "
                f"{volume:>{cw['volume']}} "
                f"{timing:>{cw['timing']}} "
                f"{quality:>{cw['quality']}}")
    
    def _format_protocol_frame_line(self, flow: FlowStats, extended_proto: str, frame_type: str, count: int, percentage: float) -> str:
        """Format a sub-row line for a specific protocol/frame type combination"""
        # Empty source/protocol/destination for sub-rows
        source = ""
        protocol = ""
        destination = ""
        
        # Extended protocol and frame type
        extended = extended_proto if extended_proto != '-' else ""
        frame = frame_type
        
        # Packet count for this combination
        pkt_count = f"{count}"
        
        # Volume calculation (approximate based on percentage)
        volume_bytes = int(flow.total_bytes * (percentage / 100))
        volume = self._format_bytes(volume_bytes)
        
        # Timing for this frame type if available
        if frame_type in flow.frame_types and flow.frame_types[frame_type].avg_inter_arrival > 0:
            timing_ms = flow.frame_types[frame_type].avg_inter_arrival * 1000
            if timing_ms >= 1000:
                timing = f"{timing_ms/1000:.1f}s"  # Convert to seconds for large values
            else:
                timing = f"{timing_ms:.1f}ms"
        else:
            timing = "-"
        
        # Percentage as quality indicator  
        quality = f"{percentage:.1f}%"
        
        cw = self.column_widths
        indent = " " * cw['flow_num']  # Match flow_num space allocation
        return (f"{indent}"
                f"{source:<{cw['source']}} "
                f"{protocol:<{cw['proto']}}  "
                f"{destination:<{cw['destination']}} "
                f"{extended:<{cw['extended']}} "
                f"{frame:<{cw['frame_type']}} "
                f"{pkt_count:>{cw['pkts']}} "
                f"{volume:>{cw['volume']}} "
                f"{timing:>{cw['timing']}} "
                f"{quality:>{cw['quality']}}")
    
    def _format_flow_line(self, flow_num: int, flow: FlowStats) -> str:
        """Format a single flow line with comprehensive information"""
        
        # Source with port (left-aligned)
        source = f"{flow.src_ip}:{flow.src_port}"
        max_source_len = self.column_widths.get('source', 24) - 2
        if len(source) > max_source_len:
            ip_space = max_source_len - len(f":{flow.src_port}") - 1  # -1 for ellipsis
            source = f"{flow.src_ip[:ip_space]}…:{flow.src_port}"
        
        # Transport protocol (TCP, UDP, ICMP, IGMP, etc.)
        protocol = flow.transport_protocol
        
        # Destination with port (left-aligned)
        destination = f"{flow.dst_ip}:{flow.dst_port}"
        max_dest_len = self.column_widths.get('destination', 24) - 2
        if len(destination) > max_dest_len:
            ip_space = max_dest_len - len(f":{flow.dst_port}") - 1  # -1 for ellipsis
            destination = f"{flow.dst_ip[:ip_space]}…:{flow.dst_port}"
        
        # Extended protocol (Chapter 10, PTP, IENA, etc.)
        extended_protocol = self._get_extended_protocol(flow)
        
        # Frame type (most common frame type in this flow)
        frame_type = self._get_primary_frame_type(flow)
        
        # Packet count
        pkt_count = f"{flow.frame_count}"
        
        # Volume with units
        volume = self._format_bytes(flow.total_bytes)
        
        # Timing quality
        if flow.avg_inter_arrival > 0:
            timing_ms = flow.avg_inter_arrival * 1000
            if timing_ms >= 1000:
                timing = f"{timing_ms/1000:.1f}s"
            else:
                timing = f"{timing_ms:.1f}ms"
        else:
            timing = "N/A"
        
        # Quality score
        if flow.enhanced_analysis.decoder_type != "Standard":
            if flow.enhanced_analysis.avg_frame_quality > 0:
                quality = f"{flow.enhanced_analysis.avg_frame_quality:.0f}%"
            else:
                quality = "Enhanced"
        else:
            # Check for outliers
            outlier_pct = len(flow.outlier_frames) / flow.frame_count * 100 if flow.frame_count > 0 else 0
            if outlier_pct > 5:
                quality = f"{outlier_pct:.0f}% Out"
            else:
                quality = "Normal"
        
        return (f"{flow_num:>2} "
                f"{source:20} "
                f"{protocol:6} "
                f"{destination:20} "
                f"{extended_protocol:10} "
                f"{frame_type:12} "
                f"{pkt_count:>6} "
                f"{volume:>8} "
                f"{timing:>8} "
                f"{quality:>8}")
    
    def _draw_frame_types_compact(self, stdscr, y: int, width: int, flow: FlowStats):
        """Draw compact frame type breakdown for selected flow"""
        frame_types = sorted(flow.frame_types.items(), key=lambda x: x[1].count, reverse=True)
        
        # Compact frame type display
        type_summary = []
        for frame_type, ft_stats in frame_types[:4]:  # Show top 4
            type_summary.append(f"{frame_type}({ft_stats.count})")
        
        if len(frame_types) > 4:
            type_summary.append(f"+{len(frame_types)-4} more")
        
        frame_line = f"      └─ Frame Types: {', '.join(type_summary)}"
        stdscr.addstr(y, 4, frame_line[:width-8], curses.A_DIM)
    
    def _get_primary_protocol(self, flow: FlowStats) -> str:
        """Get the most relevant protocol for display"""
        # Prioritize enhanced protocols
        if flow.detected_protocol_types:
            enhanced_protocols = {'CHAPTER10', 'PTP', 'IENA', 'CH10'}
            found_enhanced = flow.detected_protocol_types & enhanced_protocols
            if found_enhanced:
                return list(found_enhanced)[0]
            
            # Use first detected protocol
            return list(flow.detected_protocol_types)[0]
        
        # Fallback to transport protocol
        return flow.transport_protocol
    
    def _get_extended_protocol(self, flow: FlowStats) -> str:
        """Get extended protocol (Chapter 10, PTP, IENA, etc.)"""
        if flow.detected_protocol_types:
            # Look for specialized protocols
            enhanced_protocols = {'CHAPTER10', 'CH10', 'PTP', 'IENA'}
            found_enhanced = flow.detected_protocol_types & enhanced_protocols
            if found_enhanced:
                protocol = list(found_enhanced)[0]
                # Simplify display names
                if protocol in ['CHAPTER10', 'CH10']:
                    return 'CH10'
                return protocol
        
        # Check for other common protocols
        if flow.detected_protocol_types and 'NTP' in flow.detected_protocol_types:
            return 'NTP'
        
        return '-'
    
    def _get_primary_frame_type(self, flow: FlowStats) -> str:
        """Get the most common frame type in this flow"""
        if not flow.frame_types:
            return '-'
        
        # Find the frame type with the most packets
        most_common = max(flow.frame_types.items(), key=lambda x: x[1].count)
        frame_type = most_common[0]
        
        # Simplify frame type names for display
        if frame_type == 'CH10-Data':
            return 'CH10-Data'
        elif frame_type == 'TMATS':
            return 'TMATS'
        elif frame_type.startswith('PTP-'):
            return frame_type.replace('PTP-', 'PTP ')[:11]  # PTP Sync, PTP Signal
        elif frame_type == 'UDP':
            return 'UDP'
        elif frame_type == 'IGMP':
            return 'IGMP'
        else:
            return frame_type[:11]  # Truncate to fit column
    
    def _format_bytes(self, bytes_count: int) -> str:
        """Format byte count with appropriate units"""
        if bytes_count >= 1_000_000_000:
            return f"{bytes_count / 1_000_000_000:.1f}GB"
        elif bytes_count >= 1_000_000:
            return f"{bytes_count / 1_000_000:.1f}MB"
        elif bytes_count >= 1_000:
            return f"{bytes_count / 1_000:.1f}KB"
        else:
            return f"{bytes_count}B"
    
    def _get_flows_list(self) -> List[FlowStats]:
        """Get flows sorted by importance for flow analysis"""
        flows_list = list(self.analyzer.flows.values())
        
        # Sort by: Enhanced protocols first, then outliers, then packet count
        flows_list.sort(key=lambda x: (
            x.enhanced_analysis.decoder_type != "Standard",
            len(x.outlier_frames),
            x.frame_count
        ), reverse=True)
        
        return flows_list
    
    def handle_input(self, key: int, flows_list: List[FlowStats]) -> str:
        """Handle input for Flow Analysis view"""
        if key == curses.KEY_UP:
            self.selected_flow = max(0, self.selected_flow - 1)
            self._adjust_scroll()
            return 'selection_change'
        elif key == curses.KEY_DOWN:
            self.selected_flow = min(len(flows_list) - 1, self.selected_flow + 1)
            self._adjust_scroll()
            return 'selection_change'
        elif key == curses.KEY_PPAGE:  # Page Up
            self.selected_flow = max(0, self.selected_flow - 10)
            self._adjust_scroll()
            return 'selection_change'
        elif key == curses.KEY_NPAGE:  # Page Down
            self.selected_flow = min(len(flows_list) - 1, self.selected_flow + 10)
            self._adjust_scroll()
            return 'selection_change'
        elif key == ord('\n') or key == curses.KEY_ENTER:
            # Select flow for detailed analysis
            if flows_list and self.selected_flow < len(flows_list):
                selected_flow = flows_list[self.selected_flow]
                # Signal flow selection for other views
                return 'flow_selected'
        elif key == ord('v') or key == ord('V'):
            # Visualize selected flow
            return 'visualize'
        elif key == ord('d') or key == ord('D'):
            # Switch to decoder view for selected flow
            return 'decode_flow'
        elif key == ord('t') or key == ord('T'):
            # Toggle frame types display
            self.show_frame_types = not self.show_frame_types
            return 'toggle_frame_types'
        
        return 'none'
    
    def _adjust_scroll(self):
        """Adjust scroll position to keep selected item visible"""
        # This would be implemented based on visible area calculations
        pass
    
    def get_selected_flow(self, flows_list: List[FlowStats]) -> Optional[FlowStats]:
        """Get currently selected flow"""
        if flows_list and 0 <= self.selected_flow < len(flows_list):
            return flows_list[self.selected_flow]
        return None