"""
Web Interface for ABI Agents
Generated by ABI-Core scaffolding
"""

from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import asyncio
import json
import time
import logging

logger = logging.getLogger(__name__)

class AgentWebInterface:
    """
    Generic web interface for ABI agents
    
    Provides FastAPI endpoints for streaming agent responses via Server-Sent Events (SSE)
    """
    
    def __init__(self, agent_instance, interface_name: str = "Agent Web Interface"):
        self.agent_instance = agent_instance
        self.interface_name = interface_name
        self.app = FastAPI(
            title=f"{interface_name}",
            description=f"Web interface for {interface_name}",
            version="1.0.0"
        )
        self.setup_routes()
        
        logger.info(f"🌐 {interface_name} web interface initialized")

    def setup_routes(self):
        """Setup FastAPI routes for the web interface"""
        
        @self.app.get("/")
        async def root():
            """Root endpoint with interface information"""
            return {
                "interface": self.interface_name,
                "status": "running",
                "endpoints": {
                    "stream": "/stream - POST - Stream agent responses",
                    "health": "/health - GET - Health check"
                }
            }
        
        @self.app.get("/health")
        async def health():
            """Health check endpoint"""
            return {
                "status": "healthy",
                "interface": self.interface_name,
                "agent_available": self.agent_instance is not None
            }

        @self.app.post("/stream")
        async def stream_query(request: dict):
            """
            Stream agent responses via Server-Sent Events (SSE)
            
            Expected request format:
            {
                "query": "Your query here",
                "context_id": "optional-context-id",
                "task_id": "optional-task-id"
            }
            """
            query = request.get("query")
            if not query:
                return {"error": "Query is required"}
            
            context_id = request.get("context_id", "web-session")
            task_id = request.get("task_id", f"task-{int(time.time())}")

            async def generate_response():
                """Generate SSE stream from agent responses"""
                try:
                    # 1) Open the SSE channel
                    yield b"event: ping\\ndata: {}\\n\\n"
                    
                    logger.info(f"🔄 Starting stream for query: {query[:100]}...")
                    
                    # 2) Stream real agent responses (MUST be async generator)
                    async for chunk in self.agent_instance.stream(
                        query=query, 
                        context_id=context_id, 
                        task_id=task_id
                    ):
                        # Convert chunk to serializable format
                        try:
                            if hasattr(chunk, 'model_dump'):
                                # Pydantic model (v2)
                                chunk_data = chunk.model_dump()
                            elif hasattr(chunk, 'dict'):
                                # Pydantic model (v1)
                                chunk_data = chunk.dict()
                            elif hasattr(chunk, '__dict__'):
                                # Regular object
                                chunk_data = chunk.__dict__
                            elif isinstance(chunk, dict):
                                # Already a dictionary
                                chunk_data = chunk
                            else:
                                # Fallback to string representation
                                chunk_data = {
                                    "message": str(chunk), 
                                    "type": type(chunk).__name__
                                }
                            
                            # Add metadata for debugging
                            chunk_data["_meta"] = {
                                "chunk_type": type(chunk).__name__,
                                "timestamp": time.time(),
                                "interface": self.interface_name
                            }
                            
                            logger.debug(f"📤 Chunk sent: {type(chunk).__name__} - {str(chunk)[:100]}...")
                            
                            yield (f"data: {json.dumps(chunk_data)}\\n\\n").encode()
                            
                        except Exception as serialize_error:
                            # If serialization fails, send error info
                            error_data = {
                                "error": "Serialization failed",
                                "chunk_type": type(chunk).__name__,
                                "details": str(serialize_error),
                                "_meta": {
                                    "timestamp": time.time(),
                                    "interface": self.interface_name
                                }
                            }
                            logger.error(f"❌ Serialization error: {serialize_error}")
                            yield (f"data: {json.dumps(error_data)}\\n\\n").encode()
                    
                    # 3) Normal completion
                    completion_data = {
                        "event": "done",
                        "message": "Stream completed successfully",
                        "_meta": {
                            "timestamp": time.time(),
                            "interface": self.interface_name
                        }
                    }
                    yield (f"event: done\\ndata: {json.dumps(completion_data)}\\n\\n").encode()
                    logger.info("✅ Stream completed successfully")
                    
                except asyncio.CancelledError:
                    # Client closed connection; exit silently
                    logger.info("🔌 Client disconnected")
                    raise
                except Exception as e:
                    # 4) Log + error event so client receives something before close
                    logger.exception("💥 Error in SSE generate_response: %s", e)
                    error_data = {
                        "error": str(e),
                        "error_type": type(e).__name__,
                        "_meta": {
                            "timestamp": time.time(),
                            "interface": self.interface_name
                        }
                    }
                    yield (f"event: error\\ndata: {json.dumps(error_data)}\\n\\n").encode()
                    # Small pause to drain buffer
                    await asyncio.sleep(0.05)

            return StreamingResponse(
                generate_response(),
                media_type="text/event-stream",
                headers={
                    "Cache-Control": "no-cache",
                    "Connection": "keep-alive",
                    "Access-Control-Allow-Origin": "*",
                    "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
                    "Access-Control-Allow-Headers": "Content-Type",
                },
            )
        
        @self.app.post("/query")
        async def single_query(request: dict):
            """
            Single query endpoint (non-streaming)
            
            Expected request format:
            {
                "query": "Your query here",
                "context_id": "optional-context-id",
                "task_id": "optional-task-id"
            }
            """
            query = request.get("query")
            if not query:
                return {"error": "Query is required"}
            
            context_id = request.get("context_id", "web-session")
            task_id = request.get("task_id", f"task-{int(time.time())}")
            
            try:
                # Collect all chunks from the stream
                chunks = []
                async for chunk in self.agent_instance.stream(
                    query=query, 
                    context_id=context_id, 
                    task_id=task_id
                ):
                    if hasattr(chunk, 'model_dump'):
                        chunks.append(chunk.model_dump())
                    elif hasattr(chunk, 'dict'):
                        chunks.append(chunk.dict())
                    elif isinstance(chunk, dict):
                        chunks.append(chunk)
                    else:
                        chunks.append({"message": str(chunk), "type": type(chunk).__name__})
                
                return {
                    "query": query,
                    "context_id": context_id,
                    "task_id": task_id,
                    "chunks": chunks,
                    "total_chunks": len(chunks),
                    "interface": self.interface_name
                }
                
            except Exception as e:
                logger.error(f"Error in single query: {e}")
                return {
                    "error": str(e),
                    "error_type": type(e).__name__,
                    "query": query,
                    "interface": self.interface_name
                }

    def get_app(self) -> FastAPI:
        """Get the FastAPI application instance"""
        return self.app