#!/usr/bin/env python3
"""
BZZZ SDK Python Async Client Example
====================================

Demonstrates asynchronous operations with the BZZZ SDK Python bindings.
Shows decision publishing, event streaming, and collaborative workflows.
"""

import asyncio
import json
import logging
import sys
from datetime import datetime, timedelta
from typing import Dict, List, Any, Optional

# BZZZ SDK imports (would be installed via pip install bzzz-sdk)
try:
    from bzzz_sdk import BzzzClient, DecisionType, EventType
    from bzzz_sdk.decisions import CodeDecision, ArchitecturalDecision, TestResults
    from bzzz_sdk.crypto import AgeKeyPair
    from bzzz_sdk.exceptions import BzzzError, PermissionError, NetworkError
except ImportError:
    print("⚠️ BZZZ SDK not installed. Run: pip install bzzz-sdk")
    print("   This example shows the expected API structure")
    sys.exit(1)

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)


class BzzzAsyncExample:
    """Comprehensive async example using BZZZ SDK"""
    
    def __init__(self, endpoint: str = "http://localhost:8080"):
        self.endpoint = endpoint
        self.client: Optional[BzzzClient] = None
        self.event_count = 0
        self.decision_count = 0
        
    async def initialize(self, role: str = "backend_developer"):
        """Initialize the BZZZ client connection"""
        try:
            self.client = BzzzClient(
                endpoint=self.endpoint,
                role=role,
                timeout=30.0,
                max_retries=3
            )
            
            # Test connection
            status = await self.client.get_status()
            logger.info(f"✅ Connected as {status.agent_id} ({status.role})")
            logger.info(f"   Node ID: {status.node_id}")
            logger.info(f"   Authority: {status.authority_level}")
            logger.info(f"   Can decrypt: {status.can_decrypt}")
            
            return True
            
        except NetworkError as e:
            logger.error(f"❌ Network error connecting to BZZZ: {e}")
            return False
        except BzzzError as e:
            logger.error(f"❌ BZZZ error during initialization: {e}")
            return False
            
    async def example_basic_operations(self):
        """Example 1: Basic client operations"""
        logger.info("📋 Example 1: Basic Operations")
        
        try:
            # Get status
            status = await self.client.get_status()
            logger.info(f"   Status: {status.role} with {status.active_tasks} active tasks")
            
            # Get peers
            peers = await self.client.get_peers()
            logger.info(f"   Connected peers: {len(peers)}")
            for peer in peers[:3]:  # Show first 3
                logger.info(f"     - {peer.agent_id} ({peer.role})")
                
            # Get capabilities  
            capabilities = await self.client.get_capabilities()
            logger.info(f"   Capabilities: {capabilities.capabilities}")
            logger.info(f"   Models: {capabilities.models}")
            
        except BzzzError as e:
            logger.error(f"   ❌ Basic operations failed: {e}")
            
    async def example_decision_publishing(self):
        """Example 2: Publishing different types of decisions"""
        logger.info("📝 Example 2: Decision Publishing")
        
        try:
            # Publish code decision
            code_decision = await self.client.decisions.publish_code(
                task="implement_async_client",
                decision="Implemented Python async client with comprehensive examples",
                files_modified=[
                    "examples/sdk/python/async_client.py",
                    "bzzz_sdk/client.py",
                    "tests/test_async_client.py"
                ],
                lines_changed=250,
                test_results=TestResults(
                    passed=15,
                    failed=0,
                    skipped=1,
                    coverage=94.5,
                    failed_tests=[]
                ),
                dependencies=[
                    "asyncio",
                    "aiohttp", 
                    "websockets"
                ],
                language="python"
            )
            logger.info(f"   ✅ Code decision published: {code_decision.address}")
            
            # Publish architectural decision
            arch_decision = await self.client.decisions.publish_architectural(
                task="design_async_architecture",
                decision="Adopt asyncio-based architecture for better concurrency",
                rationale="Async operations improve performance for I/O-bound tasks",
                alternatives=[
                    "Threading-based approach",
                    "Synchronous with process pools",
                    "Hybrid sync/async model"
                ],
                implications=[
                    "Requires Python 3.7+",
                    "All network operations become async",
                    "Better resource utilization",
                    "More complex error handling"
                ],
                next_steps=[
                    "Update all SDK methods to async",
                    "Add async connection pooling", 
                    "Implement proper timeout handling",
                    "Add async example documentation"
                ]
            )
            logger.info(f"   ✅ Architectural decision published: {arch_decision.address}")
            
        except PermissionError as e:
            logger.error(f"   ❌ Permission denied publishing decision: {e}")
        except BzzzError as e:
            logger.error(f"   ❌ Decision publishing failed: {e}")
            
    async def example_event_streaming(self, duration: int = 30):
        """Example 3: Real-time event streaming"""
        logger.info(f"🎧 Example 3: Event Streaming ({duration}s)")
        
        try:
            # Subscribe to all events
            event_stream = self.client.subscribe_events()
            
            # Subscribe to specific role decisions
            decision_stream = self.client.decisions.stream_decisions(
                role="backend_developer",
                content_type="decision"
            )
            
            # Process events for specified duration
            end_time = datetime.now() + timedelta(seconds=duration)
            
            while datetime.now() < end_time:
                try:
                    # Wait for events with timeout
                    event = await asyncio.wait_for(event_stream.get_event(), timeout=1.0)
                    await self.handle_event(event)
                    
                except asyncio.TimeoutError:
                    # Check for decisions
                    try:
                        decision = await asyncio.wait_for(decision_stream.get_decision(), timeout=0.1)
                        await self.handle_decision(decision)
                    except asyncio.TimeoutError:
                        continue
                        
            logger.info(f"   📊 Processed {self.event_count} events, {self.decision_count} decisions")
            
        except BzzzError as e:
            logger.error(f"   ❌ Event streaming failed: {e}")
            
    async def handle_event(self, event):
        """Handle incoming system events"""
        self.event_count += 1
        
        event_handlers = {
            EventType.DECISION_PUBLISHED: self.handle_decision_published,
            EventType.ADMIN_CHANGED: self.handle_admin_changed,
            EventType.PEER_CONNECTED: self.handle_peer_connected,
            EventType.PEER_DISCONNECTED: self.handle_peer_disconnected
        }
        
        handler = event_handlers.get(event.type, self.handle_unknown_event)
        await handler(event)
        
    async def handle_decision_published(self, event):
        """Handle decision published events"""
        logger.info(f"   📝 Decision published: {event.data.get('address', 'unknown')}")
        logger.info(f"      Creator: {event.data.get('creator_role', 'unknown')}")
        
    async def handle_admin_changed(self, event):
        """Handle admin change events"""
        old_admin = event.data.get('old_admin', 'unknown')
        new_admin = event.data.get('new_admin', 'unknown')
        reason = event.data.get('election_reason', 'unknown')
        logger.info(f"   👑 Admin changed: {old_admin} -> {new_admin} ({reason})")
        
    async def handle_peer_connected(self, event):
        """Handle peer connection events"""
        agent_id = event.data.get('agent_id', 'unknown')
        role = event.data.get('role', 'unknown')
        logger.info(f"   🌐 Peer connected: {agent_id} ({role})")
        
    async def handle_peer_disconnected(self, event):
        """Handle peer disconnection events"""
        agent_id = event.data.get('agent_id', 'unknown')
        logger.info(f"   🔌 Peer disconnected: {agent_id}")
        
    async def handle_unknown_event(self, event):
        """Handle unknown event types"""
        logger.info(f"   ❓ Unknown event: {event.type}")
        
    async def handle_decision(self, decision):
        """Handle incoming decisions"""
        self.decision_count += 1
        logger.info(f"   📋 Decision: {decision.task} - Success: {decision.success}")
        
    async def example_crypto_operations(self):
        """Example 4: Cryptographic operations"""
        logger.info("🔐 Example 4: Crypto Operations")
        
        try:
            # Generate Age key pair
            key_pair = await self.client.crypto.generate_keys()
            logger.info(f"   🔑 Generated Age key pair")
            logger.info(f"      Public: {key_pair.public_key[:20]}...")
            logger.info(f"      Private: {key_pair.private_key[:25]}...")
            
            # Test encryption
            test_content = "Sensitive Python development data"
            
            # Encrypt for current role
            encrypted = await self.client.crypto.encrypt_for_role(
                content=test_content.encode(),
                role="backend_developer"
            )
            logger.info(f"   🔒 Encrypted {len(test_content)} bytes -> {len(encrypted)} bytes")
            
            # Decrypt content
            decrypted = await self.client.crypto.decrypt_with_role(encrypted)
            decrypted_text = decrypted.decode()
            
            if decrypted_text == test_content:
                logger.info(f"   ✅ Decryption successful: {decrypted_text}")
            else:
                logger.error(f"   ❌ Decryption mismatch")
                
            # Check permissions
            permissions = await self.client.crypto.get_permissions()
            logger.info(f"   🛡️ Role permissions:")
            logger.info(f"      Current role: {permissions.current_role}")
            logger.info(f"      Can decrypt: {permissions.can_decrypt}")
            logger.info(f"      Authority: {permissions.authority_level}")
            
        except BzzzError as e:
            logger.error(f"   ❌ Crypto operations failed: {e}")
            
    async def example_query_operations(self):
        """Example 5: Querying and data retrieval"""
        logger.info("📊 Example 5: Query Operations")
        
        try:
            # Query recent decisions
            recent_decisions = await self.client.decisions.query_recent(
                role="backend_developer",
                project="bzzz_sdk",
                since=datetime.now() - timedelta(hours=24),
                limit=10
            )
            
            logger.info(f"   📋 Found {len(recent_decisions)} recent decisions")
            
            for i, decision in enumerate(recent_decisions[:3]):
                logger.info(f"      {i+1}. {decision.task} - {decision.timestamp}")
                logger.info(f"         Success: {decision.success}")
                
            # Get specific decision content
            if recent_decisions:
                first_decision = recent_decisions[0]
                content = await self.client.decisions.get_content(first_decision.address)
                
                logger.info(f"   📄 Decision content preview:")
                logger.info(f"      Address: {content.address}")
                logger.info(f"      Decision: {content.decision[:100]}...")
                logger.info(f"      Files modified: {len(content.files_modified or [])}")
                
        except PermissionError as e:
            logger.error(f"   ❌ Permission denied querying decisions: {e}")
        except BzzzError as e:
            logger.error(f"   ❌ Query operations failed: {e}")
            
    async def example_collaborative_workflow(self):
        """Example 6: Collaborative workflow simulation"""
        logger.info("🤝 Example 6: Collaborative Workflow")
        
        try:
            # Simulate a collaborative code review workflow
            logger.info("   Starting collaborative code review...")
            
            # Step 1: Announce code change
            await self.client.decisions.publish_code(
                task="refactor_authentication",
                decision="Refactored authentication module for better security",
                files_modified=[
                    "auth/jwt_handler.py",
                    "auth/middleware.py",
                    "tests/test_auth.py"
                ],
                lines_changed=180,
                test_results=TestResults(
                    passed=12,
                    failed=0,
                    coverage=88.0
                ),
                language="python"
            )
            logger.info("   ✅ Step 1: Code change announced")
            
            # Step 2: Request reviews (simulate)
            await asyncio.sleep(1)  # Simulate processing time
            logger.info("   📋 Step 2: Review requests sent to:")
            logger.info("      - Senior Software Architect")
            logger.info("      - Security Expert") 
            logger.info("      - QA Engineer")
            
            # Step 3: Simulate review responses
            await asyncio.sleep(2)
            reviews_completed = 0
            
            # Simulate architect review
            await self.client.decisions.publish_architectural(
                task="review_auth_refactor", 
                decision="Architecture review approved with minor suggestions",
                rationale="Refactoring improves separation of concerns",
                next_steps=["Add input validation documentation"]
            )
            reviews_completed += 1
            logger.info(f"   ✅ Step 3.{reviews_completed}: Architect review completed")
            
            # Step 4: Aggregate and finalize
            await asyncio.sleep(1)
            logger.info("   📊 Step 4: All reviews completed")
            logger.info("      Status: APPROVED with minor changes")
            logger.info("      Next steps: Address documentation suggestions")
            
        except BzzzError as e:
            logger.error(f"   ❌ Collaborative workflow failed: {e}")
            
    async def run_all_examples(self):
        """Run all examples in sequence"""
        logger.info("🚀 Starting BZZZ SDK Python Async Examples")
        logger.info("=" * 60)
        
        examples = [
            self.example_basic_operations,
            self.example_decision_publishing,
            self.example_crypto_operations,
            self.example_query_operations,
            self.example_collaborative_workflow,
            # Note: event_streaming runs last as it takes time
        ]
        
        for example in examples:
            try:
                await example()
                await asyncio.sleep(0.5)  # Brief pause between examples
            except Exception as e:
                logger.error(f"❌ Example {example.__name__} failed: {e}")
                
        # Run event streaming for a shorter duration
        await self.example_event_streaming(duration=10)
        
        logger.info("=" * 60)
        logger.info("✅ All BZZZ SDK Python examples completed")
        
    async def cleanup(self):
        """Clean up resources"""
        if self.client:
            await self.client.close()
            logger.info("🧹 Client connection closed")


async def main():
    """Main entry point"""
    example = BzzzAsyncExample()
    
    try:
        # Initialize connection
        if not await example.initialize("backend_developer"):
            logger.error("Failed to initialize BZZZ client")
            return 1
            
        # Run all examples
        await example.run_all_examples()
        
    except KeyboardInterrupt:
        logger.info("\n🛑 Examples interrupted by user")
    except Exception as e:
        logger.error(f"❌ Unexpected error: {e}")
        return 1
    finally:
        await example.cleanup()
        
    return 0


if __name__ == "__main__":
    # Run the async example
    exit_code = asyncio.run(main())
    sys.exit(exit_code)