"""
FastAPI application for ReadAnyBook REST API.

Provides HTTP endpoints for cheat sheet generation.
"""

import asyncio
import tempfile
import uuid
from contextlib import asynccontextmanager
from pathlib import Path
from typing import Any, Dict, List, Optional

try:
    from fastapi import FastAPI, HTTPException, BackgroundTasks, UploadFile, File, Query
    from fastapi.responses import FileResponse, JSONResponse
    from fastapi.middleware.cors import CORSMiddleware
    from pydantic import BaseModel, Field
    FASTAPI_AVAILABLE = True
except ImportError:
    FASTAPI_AVAILABLE = False


# Pydantic models for API
if FASTAPI_AVAILABLE:
    
    class GenerationRequest(BaseModel):
        """Request to generate a cheat sheet."""
        title: Optional[str] = Field(None, description="Title for the cheat sheet")
        collection_name: Optional[str] = Field("default", description="Vector store collection")
        profile: Optional[str] = Field(None, description="Configuration profile")
        include_concepts: bool = Field(True, description="Include concepts section")
        include_formulas: bool = Field(True, description="Include formulas section")
        include_algorithms: bool = Field(True, description="Include algorithms section")
        include_models: bool = Field(True, description="Include models section")
        latex_only: bool = Field(False, description="Return LaTeX only, don't compile")
    
    
    class SearchRequest(BaseModel):
        """Request to search indexed documents."""
        query: str = Field(..., description="Search query")
        collection_name: str = Field("default", description="Collection to search")
        top_k: int = Field(5, ge=1, le=50, description="Number of results")
        mode: str = Field("hybrid", description="Search mode: dense, sparse, hybrid")
    
    
    class SearchResult(BaseModel):
        """Search result."""
        content: str
        score: float
        metadata: Dict[str, Any] = {}
    
    
    class IndexRequest(BaseModel):
        """Request to index a document."""
        collection_name: str = Field("default", description="Collection name")
    
    
    class TaskStatus(BaseModel):
        """Background task status."""
        task_id: str
        status: str  # pending, processing, completed, failed
        progress: float = 0.0
        result: Optional[Dict[str, Any]] = None
        error: Optional[str] = None
    
    
    class CheatSheetContent(BaseModel):
        """Generated cheat sheet content."""
        title: str
        overview: Optional[str] = None
        concepts: List[Dict[str, Any]] = []
        formulas: List[Dict[str, Any]] = []
        algorithms: List[Dict[str, Any]] = []
        models: List[Dict[str, Any]] = []
    
    
    # Store for background tasks
    task_store: Dict[str, TaskStatus] = {}
    
    # Pipeline instance (singleton)
    _pipeline = None
    
    
    @asynccontextmanager
    async def lifespan(app: FastAPI):
        """Application lifespan handler."""
        # Startup
        from ..infra.logging import setup_logging
        setup_logging(level="INFO", pretty=True)
        
        yield
        
        # Shutdown
        global _pipeline
        _pipeline = None
    
    
    def create_app(settings=None) -> FastAPI:
        """
        Create FastAPI application.
        
        Args:
            settings: Optional Settings instance
            
        Returns:
            FastAPI application
        """
        app = FastAPI(
            title="ReadAnyBook API",
            description="Generate cheat sheets from books and papers using RAG",
            version="0.1.0",
            lifespan=lifespan,
        )
        
        # Add CORS middleware
        app.add_middleware(
            CORSMiddleware,
            allow_origins=["*"],
            allow_credentials=True,
            allow_methods=["*"],
            allow_headers=["*"],
        )
        
        # Store settings in app state
        app.state.settings = settings
        
        # Register routes
        register_routes(app)
        
        return app
    
    
    def get_pipeline():
        """Get or create pipeline instance."""
        global _pipeline
        
        if _pipeline is None:
            from ..core.pipeline import CheatSheetPipeline
            from ..infra.settings import Settings
            
            settings = Settings()
            _pipeline = CheatSheetPipeline(settings)
        
        return _pipeline
    
    
    def register_routes(app: FastAPI):
        """Register API routes."""
        
        @app.get("/")
        async def root():
            """Root endpoint."""
            return {
                "name": "ReadAnyBook API",
                "version": "0.1.0",
                "docs": "/docs",
            }
        
        @app.get("/health")
        async def health():
            """Health check endpoint."""
            return {"status": "healthy"}
        
        @app.post("/upload")
        async def upload_document(
            file: UploadFile = File(...),
            collection_name: str = Query("default"),
            background_tasks: BackgroundTasks = None,
        ):
            """
            Upload and index a document.
            
            Accepts PDF, EPUB, HTML, Markdown files.
            """
            # Validate file type
            allowed_extensions = {".pdf", ".epub", ".html", ".md", ".tex"}
            file_ext = Path(file.filename).suffix.lower()
            
            if file_ext not in allowed_extensions:
                raise HTTPException(
                    status_code=400,
                    detail=f"Unsupported file type: {file_ext}. Allowed: {allowed_extensions}"
                )
            
            # Create task ID
            task_id = str(uuid.uuid4())
            task_store[task_id] = TaskStatus(
                task_id=task_id,
                status="pending",
            )
            
            # Save uploaded file
            with tempfile.NamedTemporaryFile(delete=False, suffix=file_ext) as tmp:
                content = await file.read()
                tmp.write(content)
                tmp_path = tmp.name
            
            # Process in background
            if background_tasks:
                background_tasks.add_task(
                    process_upload,
                    task_id,
                    tmp_path,
                    collection_name,
                )
            else:
                # Synchronous processing
                await asyncio.to_thread(
                    process_upload_sync,
                    task_id,
                    tmp_path,
                    collection_name,
                )
            
            return {
                "task_id": task_id,
                "status": "pending",
                "message": "Document uploaded and queued for processing",
            }
        
        @app.get("/task/{task_id}")
        async def get_task_status(task_id: str):
            """Get status of a background task."""
            if task_id not in task_store:
                raise HTTPException(status_code=404, detail="Task not found")
            
            return task_store[task_id]
        
        @app.post("/search")
        async def search(request: SearchRequest):
            """
            Search indexed documents.
            """
            try:
                pipeline = get_pipeline()
                
                # Ensure index is connected
                if not pipeline._retriever:
                    raise HTTPException(
                        status_code=400,
                        detail="No index loaded. Upload a document first."
                    )
                
                # Perform search
                results = pipeline._retriever.retrieve(
                    request.query,
                    top_k=request.top_k,
                    collection_name=request.collection_name,
                )
                
                return {
                    "query": request.query,
                    "results": [
                        SearchResult(
                            content=r.get("content", ""),
                            score=r.get("score", 0.0),
                            metadata=r.get("metadata", {}),
                        )
                        for r in results
                    ],
                }
            
            except Exception as e:
                raise HTTPException(status_code=500, detail=str(e))
        
        @app.post("/generate")
        async def generate_cheatsheet(
            request: GenerationRequest,
            background_tasks: BackgroundTasks,
        ):
            """
            Generate a cheat sheet from indexed documents.
            
            Returns task ID for tracking progress.
            """
            task_id = str(uuid.uuid4())
            task_store[task_id] = TaskStatus(
                task_id=task_id,
                status="pending",
            )
            
            background_tasks.add_task(
                process_generation,
                task_id,
                request,
            )
            
            return {
                "task_id": task_id,
                "status": "pending",
                "message": "Generation started",
            }
        
        @app.get("/download/{task_id}")
        async def download_result(task_id: str):
            """
            Download generated cheat sheet.
            """
            if task_id not in task_store:
                raise HTTPException(status_code=404, detail="Task not found")
            
            task = task_store[task_id]
            
            if task.status != "completed":
                raise HTTPException(
                    status_code=400,
                    detail=f"Task not completed. Status: {task.status}"
                )
            
            if not task.result or "file_path" not in task.result:
                raise HTTPException(status_code=404, detail="No file generated")
            
            file_path = Path(task.result["file_path"])
            if not file_path.exists():
                raise HTTPException(status_code=404, detail="File not found")
            
            return FileResponse(
                file_path,
                media_type="application/pdf" if file_path.suffix == ".pdf" else "text/plain",
                filename=file_path.name,
            )
        
        @app.get("/collections")
        async def list_collections():
            """List available collections."""
            try:
                pipeline = get_pipeline()
                
                if pipeline._vector_store:
                    collections = pipeline._vector_store.list_collections()
                    return {"collections": collections}
                
                return {"collections": []}
            
            except Exception as e:
                raise HTTPException(status_code=500, detail=str(e))
        
        @app.delete("/collections/{name}")
        async def delete_collection(name: str):
            """Delete a collection."""
            try:
                pipeline = get_pipeline()
                
                if pipeline._vector_store:
                    pipeline._vector_store.delete_collection(name)
                    return {"message": f"Collection '{name}' deleted"}
                
                raise HTTPException(status_code=400, detail="No vector store configured")
            
            except Exception as e:
                raise HTTPException(status_code=500, detail=str(e))
    
    
    def process_upload_sync(task_id: str, file_path: str, collection_name: str):
        """Synchronously process uploaded document."""
        try:
            task_store[task_id].status = "processing"
            task_store[task_id].progress = 0.1
            
            pipeline = get_pipeline()
            
            # Ingest
            task_store[task_id].progress = 0.3
            pipeline.ingest(file_path)
            
            # Index
            task_store[task_id].progress = 0.6
            num_chunks = pipeline.index(collection_name=collection_name)
            
            task_store[task_id].status = "completed"
            task_store[task_id].progress = 1.0
            task_store[task_id].result = {
                "chunks_indexed": num_chunks,
                "collection": collection_name,
            }
            
        except Exception as e:
            task_store[task_id].status = "failed"
            task_store[task_id].error = str(e)
        
        finally:
            # Clean up temp file
            Path(file_path).unlink(missing_ok=True)
    
    
    async def process_upload(task_id: str, file_path: str, collection_name: str):
        """Process uploaded document in background."""
        await asyncio.to_thread(process_upload_sync, task_id, file_path, collection_name)
    
    
    async def process_generation(task_id: str, request: GenerationRequest):
        """Process cheat sheet generation in background."""
        try:
            task_store[task_id].status = "processing"
            task_store[task_id].progress = 0.1
            
            pipeline = get_pipeline()
            
            # Generate content
            task_store[task_id].progress = 0.3
            content = await asyncio.to_thread(
                pipeline.generate_content,
                request.collection_name,
            )
            
            # Set title
            if request.title:
                content.title = request.title
            
            task_store[task_id].progress = 0.7
            
            # Build output
            with tempfile.NamedTemporaryFile(
                delete=False,
                suffix=".tex" if request.latex_only else ".pdf"
            ) as tmp:
                output_path = tmp.name
            
            if request.latex_only:
                latex = pipeline.build_latex(content)
                Path(output_path).write_text(latex, encoding='utf-8')
            else:
                await asyncio.to_thread(pipeline.build, content, output_path)
            
            task_store[task_id].status = "completed"
            task_store[task_id].progress = 1.0
            task_store[task_id].result = {
                "file_path": output_path,
                "title": content.title,
                "concepts_count": len(content.concepts),
                "formulas_count": len(content.formulas),
                "algorithms_count": len(content.algorithms),
                "models_count": len(content.models),
            }
            
        except Exception as e:
            task_store[task_id].status = "failed"
            task_store[task_id].error = str(e)
    
    
    # Create default app instance
    app = create_app()

else:
    # Fallback when FastAPI not installed
    def create_app(settings=None):
        raise ImportError("FastAPI not installed. Run: pip install fastapi uvicorn")
    
    app = None
