#!/usr/bin/env python3
"""
🚀 智慧拓撲排序算法演示 (獨立版本)

不依賴外部模組，純粹展示拓撲排序算法的核心功能
"""

import json
from typing import List, Dict, Any, Tuple
from collections import deque

class TopologyDemo:
    """演示拓撲排序算法的類別"""
    
    def __init__(self):
        self.stage_order = []
        
    def analyze_pipeline(self, pipeline_data: Dict[str, Any]):
        """分析pipeline並執行拓撲排序"""
        print("🔍 Starting intelligent pipeline topology analysis...")
        
        # 提取模型節點
        model_nodes = [node for node in pipeline_data.get('nodes', []) 
                      if 'model' in node.get('type', '').lower()]
        connections = pipeline_data.get('connections', [])
        
        if not model_nodes:
            print("   ⚠️  No model nodes found!")
            return []
            
        # 建立依賴圖
        dependency_graph = self._build_dependency_graph(model_nodes, connections)
        
        # 檢測循環
        cycles = self._detect_cycles(dependency_graph)
        if cycles:
            print(f"   ⚠️  Found {len(cycles)} cycles!")
            dependency_graph = self._resolve_cycles(dependency_graph, cycles)
        
        # 執行拓撲排序
        sorted_stages = self._topological_sort_with_optimization(dependency_graph, model_nodes)
        
        # 計算指標
        metrics = self._calculate_pipeline_metrics(sorted_stages, dependency_graph)
        self._display_pipeline_analysis(sorted_stages, metrics)
        
        return sorted_stages
    
    def _build_dependency_graph(self, model_nodes: List[Dict], connections: List[Dict]) -> Dict[str, Dict]:
        """建立依賴圖"""
        print("   📊 Building dependency graph...")
        
        graph = {}
        for node in model_nodes:
            graph[node['id']] = {
                'node': node,
                'dependencies': set(),
                'dependents': set(),
                'depth': 0
            }
        
        # 分析連接
        for conn in connections:
            output_node_id = conn.get('output_node')
            input_node_id = conn.get('input_node')
            
            if output_node_id in graph and input_node_id in graph:
                graph[input_node_id]['dependencies'].add(output_node_id)
                graph[output_node_id]['dependents'].add(input_node_id)
        
        dep_count = sum(len(data['dependencies']) for data in graph.values())
        print(f"   ✅ Graph built: {len(graph)} nodes, {dep_count} dependencies")
        return graph
    
    def _detect_cycles(self, graph: Dict[str, Dict]) -> List[List[str]]:
        """檢測循環"""
        print("   🔍 Checking for dependency cycles...")
        
        cycles = []
        visited = set()
        rec_stack = set()
        
        def dfs_cycle_detect(node_id, path):
            if node_id in rec_stack:
                cycle_start = path.index(node_id)
                cycle = path[cycle_start:] + [node_id]
                cycles.append(cycle)
                return True
                
            if node_id in visited:
                return False
                
            visited.add(node_id)
            rec_stack.add(node_id)
            path.append(node_id)
            
            for dependent in graph[node_id]['dependents']:
                if dfs_cycle_detect(dependent, path):
                    return True
                    
            path.pop()
            rec_stack.remove(node_id)
            return False
        
        for node_id in graph:
            if node_id not in visited:
                dfs_cycle_detect(node_id, [])
        
        if cycles:
            print(f"   ⚠️  Found {len(cycles)} cycles")
        else:
            print("   ✅ No cycles detected")
            
        return cycles
    
    def _resolve_cycles(self, graph: Dict[str, Dict], cycles: List[List[str]]) -> Dict[str, Dict]:
        """解決循環"""
        print("   🔧 Resolving dependency cycles...")
        
        for cycle in cycles:
            node_names = [graph[nid]['node']['name'] for nid in cycle]
            print(f"      Breaking cycle: {' → '.join(node_names)}")
            
            if len(cycle) >= 2:
                node_to_break = cycle[-2]
                dependent_to_break = cycle[-1]
                
                graph[dependent_to_break]['dependencies'].discard(node_to_break)
                graph[node_to_break]['dependents'].discard(dependent_to_break)
                
                print(f"      🔗 Broke dependency: {graph[node_to_break]['node']['name']} → {graph[dependent_to_break]['node']['name']}")
        
        return graph
    
    def _topological_sort_with_optimization(self, graph: Dict[str, Dict], model_nodes: List[Dict]) -> List[Dict]:
        """執行優化的拓撲排序"""
        print("   🎯 Performing optimized topological sort...")
        
        # 計算深度層級
        self._calculate_depth_levels(graph)
        
        # 按深度分組
        depth_groups = self._group_by_depth(graph)
        
        # 排序
        sorted_nodes = []
        for depth in sorted(depth_groups.keys()):
            group_nodes = depth_groups[depth]
            
            group_nodes.sort(key=lambda nid: (
                len(graph[nid]['dependencies']),
                -len(graph[nid]['dependents']),
                graph[nid]['node']['name']
            ))
            
            for node_id in group_nodes:
                sorted_nodes.append(graph[node_id]['node'])
                
        print(f"   ✅ Sorted {len(sorted_nodes)} stages into {len(depth_groups)} execution levels")
        return sorted_nodes
    
    def _calculate_depth_levels(self, graph: Dict[str, Dict]):
        """計算深度層級"""
        print("      📏 Calculating execution depth levels...")
        
        no_deps = [nid for nid, data in graph.items() if not data['dependencies']]
        queue = deque([(nid, 0) for nid in no_deps])
        
        while queue:
            node_id, depth = queue.popleft()
            
            if graph[node_id]['depth'] < depth:
                graph[node_id]['depth'] = depth
                
                for dependent in graph[node_id]['dependents']:
                    queue.append((dependent, depth + 1))
    
    def _group_by_depth(self, graph: Dict[str, Dict]) -> Dict[int, List[str]]:
        """按深度分組"""
        depth_groups = {}
        
        for node_id, data in graph.items():
            depth = data['depth']
            if depth not in depth_groups:
                depth_groups[depth] = []
            depth_groups[depth].append(node_id)
            
        return depth_groups
    
    def _calculate_pipeline_metrics(self, sorted_stages: List[Dict], graph: Dict[str, Dict]) -> Dict[str, Any]:
        """計算指標"""
        print("   📈 Calculating pipeline metrics...")
        
        total_stages = len(sorted_stages)
        max_depth = max([data['depth'] for data in graph.values()]) + 1 if graph else 1
        
        depth_distribution = {}
        for data in graph.values():
            depth = data['depth']
            depth_distribution[depth] = depth_distribution.get(depth, 0) + 1
            
        max_parallel = max(depth_distribution.values()) if depth_distribution else 1
        critical_path = self._find_critical_path(graph)
        
        return {
            'total_stages': total_stages,
            'pipeline_depth': max_depth,
            'max_parallel_stages': max_parallel,
            'parallelization_efficiency': (total_stages / max_depth) if max_depth > 0 else 1.0,
            'critical_path_length': len(critical_path),
            'critical_path': critical_path
        }
    
    def _find_critical_path(self, graph: Dict[str, Dict]) -> List[str]:
        """找出關鍵路徑"""
        longest_path = []
        
        def dfs_longest_path(node_id, current_path):
            nonlocal longest_path
            
            current_path.append(node_id)
            
            if not graph[node_id]['dependents']:
                if len(current_path) > len(longest_path):
                    longest_path = current_path.copy()
            else:
                for dependent in graph[node_id]['dependents']:
                    dfs_longest_path(dependent, current_path)
                    
            current_path.pop()
        
        for node_id, data in graph.items():
            if not data['dependencies']:
                dfs_longest_path(node_id, [])
                
        return longest_path
    
    def _display_pipeline_analysis(self, sorted_stages: List[Dict], metrics: Dict[str, Any]):
        """顯示分析結果"""
        print("\n" + "="*60)
        print("🚀 INTELLIGENT PIPELINE TOPOLOGY ANALYSIS COMPLETE")
        print("="*60)
        
        print(f"📊 Pipeline Metrics:")
        print(f"   • Total Stages: {metrics['total_stages']}")
        print(f"   • Pipeline Depth: {metrics['pipeline_depth']} levels")
        print(f"   • Max Parallel Stages: {metrics['max_parallel_stages']}")
        print(f"   • Parallelization Efficiency: {metrics['parallelization_efficiency']:.1%}")
        
        print(f"\n🎯 Optimized Execution Order:")
        for i, stage in enumerate(sorted_stages, 1):
            print(f"   {i:2d}. {stage['name']} (ID: {stage['id'][:8]}...)")
        
        if metrics['critical_path']:
            print(f"\n⚡ Critical Path ({metrics['critical_path_length']} stages):")
            critical_names = []
            for node_id in metrics['critical_path']:
                node_name = next((stage['name'] for stage in sorted_stages if stage['id'] == node_id), 'Unknown')
                critical_names.append(node_name)
            print(f"   {' → '.join(critical_names)}")
        
        print(f"\n💡 Performance Insights:")
        if metrics['parallelization_efficiency'] > 0.8:
            print("   ✅ Excellent parallelization potential!")
        elif metrics['parallelization_efficiency'] > 0.6:
            print("   ✨ Good parallelization opportunities available")
        else:
            print("   ⚠️  Limited parallelization - consider pipeline redesign")
            
        if metrics['pipeline_depth'] <= 3:
            print("   ⚡ Low latency pipeline - great for real-time applications")
        elif metrics['pipeline_depth'] <= 6:
            print("   ⚖️  Balanced pipeline depth - good throughput/latency trade-off")
        else:
            print("   🎯 Deep pipeline - optimized for maximum throughput")
            
        print("="*60 + "\n")

def create_demo_pipelines():
    """創建演示用的pipeline"""
    
    # Demo 1: 簡單線性pipeline
    simple_pipeline = {
        "project_name": "Simple Linear Pipeline",
        "nodes": [
            {"id": "model_001", "name": "Object Detection", "type": "ExactModelNode"},
            {"id": "model_002", "name": "Fire Classification", "type": "ExactModelNode"},
            {"id": "model_003", "name": "Result Verification", "type": "ExactModelNode"}
        ],
        "connections": [
            {"output_node": "model_001", "input_node": "model_002"},
            {"output_node": "model_002", "input_node": "model_003"}
        ]
    }
    
    # Demo 2: 並行pipeline
    parallel_pipeline = {
        "project_name": "Parallel Processing Pipeline",
        "nodes": [
            {"id": "model_001", "name": "RGB Processor", "type": "ExactModelNode"},
            {"id": "model_002", "name": "IR Processor", "type": "ExactModelNode"},
            {"id": "model_003", "name": "Depth Processor", "type": "ExactModelNode"},
            {"id": "model_004", "name": "Fusion Engine", "type": "ExactModelNode"}
        ],
        "connections": [
            {"output_node": "model_001", "input_node": "model_004"},
            {"output_node": "model_002", "input_node": "model_004"},
            {"output_node": "model_003", "input_node": "model_004"}
        ]
    }
    
    # Demo 3: 複雜多層pipeline
    complex_pipeline = {
        "project_name": "Advanced Multi-Stage Fire Detection Pipeline",
        "nodes": [
            {"id": "model_rgb_001", "name": "RGB Feature Extractor", "type": "ExactModelNode"},
            {"id": "model_edge_002", "name": "Edge Feature Extractor", "type": "ExactModelNode"},
            {"id": "model_thermal_003", "name": "Thermal Feature Extractor", "type": "ExactModelNode"},
            {"id": "model_fusion_004", "name": "Feature Fusion", "type": "ExactModelNode"},
            {"id": "model_attention_005", "name": "Attention Mechanism", "type": "ExactModelNode"},
            {"id": "model_classifier_006", "name": "Fire Classifier", "type": "ExactModelNode"}
        ],
        "connections": [
            {"output_node": "model_rgb_001", "input_node": "model_fusion_004"},
            {"output_node": "model_edge_002", "input_node": "model_fusion_004"},
            {"output_node": "model_thermal_003", "input_node": "model_attention_005"},
            {"output_node": "model_fusion_004", "input_node": "model_classifier_006"},
            {"output_node": "model_attention_005", "input_node": "model_classifier_006"}
        ]
    }
    
    # Demo 4: 有循環的pipeline (測試循環檢測)
    cycle_pipeline = {
        "project_name": "Pipeline with Cycles (Testing)",
        "nodes": [
            {"id": "model_A", "name": "Model A", "type": "ExactModelNode"},
            {"id": "model_B", "name": "Model B", "type": "ExactModelNode"},
            {"id": "model_C", "name": "Model C", "type": "ExactModelNode"}
        ],
        "connections": [
            {"output_node": "model_A", "input_node": "model_B"},
            {"output_node": "model_B", "input_node": "model_C"},
            {"output_node": "model_C", "input_node": "model_A"}  # 創建循環!
        ]
    }
    
    return [simple_pipeline, parallel_pipeline, complex_pipeline, cycle_pipeline]

def main():
    """主演示函數"""
    print("🚀 INTELLIGENT PIPELINE TOPOLOGY SORTING DEMONSTRATION")
    print("="*60)
    print("This demo showcases our advanced pipeline analysis capabilities:")
    print("• Automatic dependency resolution")
    print("• Parallel execution optimization")
    print("• Cycle detection and prevention")
    print("• Critical path analysis")
    print("• Performance metrics calculation")
    print("="*60 + "\n")
    
    demo = TopologyDemo()
    pipelines = create_demo_pipelines()
    demo_names = ["Simple Linear", "Parallel Processing", "Complex Multi-Stage", "Cycle Detection"]
    
    for i, (pipeline, name) in enumerate(zip(pipelines, demo_names), 1):
        print(f"🎯 DEMO {i}: {name} Pipeline")
        print("="*50)
        demo.analyze_pipeline(pipeline)
        print("\n")
    
    print("🎉 ALL DEMONSTRATIONS COMPLETED SUCCESSFULLY!")
    print("Ready for production deployment and progress reporting! 🚀")

if __name__ == "__main__":
    main()