"""
================================================================================
 Complete FastAPI Application for Storigo Content Generation - v7.0
 - Professional slide content generation with inline image fetching
 - Guaranteed image field presence (URL or null)
 - Comprehensive error handling and logging
 - Multiple endpoints support
================================================================================
"""

from fastapi import FastAPI, HTTPException
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel, Field, validator
from fastapi.responses import JSONResponse, FileResponse
import tempfile
import requests
import os
import logging
import traceback
from typing import Union, Dict
import time
from fastapi import Request, Body

# Import functions from other modules
from generate_storigo_content import (
    load_pdf, transcribe, load_txt, split_text_with_semantic_chunker,
    crawlerrr, read_file_url, split_text_with_semantic_chunker_for_url,
    clean_using_llm, count_total_words, save_documents_to_txt,
    create_and_save_embeddings, generate_slide_content, merge_all_faiss,
    parsing, marks_splitter, allocate_slides, create_embeddings,
    generate_slide_content_alloc1
)
from generate_storigo_content_from_prompt import generate_slide_content_from_prompt
from langchain_community.embeddings import OllamaEmbeddings

# Setup logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - [%(levelname)s] - (FastAPI) - %(message)s'
)
logger = logging.getLogger(__name__)

# --------------------------------------------------------------------------
# FastAPI App Initialization
# --------------------------------------------------------------------------
app = FastAPI(
    title="Storigo Content Generation API",
    description="Professional API for generating presentation content with images using Groq AI",
    version="7.0.0"
)

IMAGES_DIR_NAME = "generated_images_for_storigo"
os.makedirs(IMAGES_DIR_NAME, exist_ok=True)   # <-- ensure dir exists before mount
app.mount("/images", StaticFiles(directory=IMAGES_DIR_NAME), name="images")

# --------------------------------------------------------------------------
# Pydantic Request Models
# --------------------------------------------------------------------------

class StorigoContentRequest(BaseModel):
    """Request model for prompt-based content generation"""
    client_id: int
    prompt: str
    num_slides: int
    is_image: Union[int, str]
    is_question: Union[int, str]
    num_mcqs: int
    question_position: str
    isGPU: Union[bool, int]

    class Config:
        json_schema_extra = {
            "example": {
                "client_id": 3,
                "prompt": "Generate a professional presentation about kitchen handling",
                "num_slides": 10,
                "is_image": 1,
                "is_question": 1,
                "num_mcqs": 5,
                "question_position": "end",
                "isGPU": True
            }
        }

class DocumentContentRequest(BaseModel):
    """Request model for document-based content generation"""
    client_id: str
    num_slides: int
    document_url: str
    is_image: Union[int, str]
    document_type: str
    is_question: Union[int, str]
    num_mcqs: int
    question_position: str
    isGPU: Union[bool, int]

    class Config:
        json_schema_extra = {
            "example": {
                "client_id": "client_123",
                "num_slides": 10,
                "document_url": "https://example.com/document.pdf",
                "is_image": "1",
                "document_type": "pdf",
                "is_question": "1",
                "num_mcqs": 5,
                "question_position": "end",
                "isGPU": True
            }
        }

# --------------------------------------------------------------------------
# Utility Functions
# --------------------------------------------------------------------------

def fetch_document_with_curl(url):
    """Fetch document from URL"""
    try:
        response = requests.get(url, timeout=30)
        if response.status_code == 200:
            return response.content
        else:
            raise HTTPException(status_code=404, detail=f"Document not found at {url}")
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Error fetching document: {str(e)}")

def process_and_generate_response(split_documents, client_id, num_slides, num_mcqs, is_image, is_question, question_position, isGPU):
    """Process documents and generate response"""
    try:
        # Save embeddings
        create_and_save_embeddings(split_documents, client_id)
        merge_embeddings = merge_all_faiss(client_id)

        # Generate slide content
        slide_content = generate_slide_content(
            merge_embeddings, client_id, num_slides, num_mcqs, 
            is_image, is_question, question_position, isGPU
        )
        logger.info(f"Slide content generated for {num_slides} slides.")

        # Serialize and return response
        response_data = slide_content.dict()
        return JSONResponse(content=response_data)

    except Exception as e:
        logger.error(f"Error generating content: {str(e)}")
        return JSONResponse(
            content={"error": f"Error: {str(e)}", "details": traceback.format_exc()},
            status_code=500
        )

# --------------------------------------------------------------------------
# API Endpoints
# --------------------------------------------------------------------------

@app.get("/")
async def root():
    """API information endpoint"""
    return {
        "name": "Storigo Content Generation API",
        "version": "7.0.0",
        "status": "operational",
        "endpoints": {
            "generate_from_prompt": "/generate-storigo-content-from-prompt",
            "generate_from_document": "/generate-storigo-content",
            "health": "/health",
            "images": "/images/{filename}"
        },
        "features": [
            "Professional slide content generation using Groq AI",
            "Inline synchronous image generation (no null values)",
            "MCQ generation with customizable positioning",
            "Document-based content generation (PDF/HTML)",
            "Comprehensive error handling and logging",
            "Static image serving"
        ]
    }

@app.get("/health")
async def health_check():
    """Health check endpoint"""
    return {
        "status": "healthy",
        "version": "7.0.0",
        "timestamp": time.time(),
        "message": "Storigo Content Generation API is running"
    }

@app.post("/generate-storigo-content-from-prompt")
async def generate_storigo_content_from_prompt_endpoint(
    payload: StorigoContentRequest,
    request: Request
) -> JSONResponse:
    """
    Generate professional Storigo content from a text prompt with inline image generation.

    Returns JSONResponse where every slide has an 'image' field (absolute URL or null).
    """
    try:
        logger.info("=" * 80)
        logger.info("🚀 API ENDPOINT: /generate-storigo-content-from-prompt")
        logger.info("=" * 80)
        logger.info(f"📋 Request Parameters:")
        logger.info(f"   - Client ID: {payload.client_id}")
        logger.info(f"   - Prompt: {payload.prompt[:80]}...")
        logger.info(f"   - Slides: {payload.num_slides}")
        logger.info(f"   - MCQs: {payload.num_mcqs}")
        logger.info(f"   - Images: {payload.is_image}")
        logger.info(f"   - Questions: {payload.is_question}")
        logger.info(f"   - Question Position: {payload.question_position}")
        logger.info("=" * 80)

        # Convert is_image to boolean
        if isinstance(payload.is_image, int):
            is_image_bool = payload.is_image == 1
        elif isinstance(payload.is_image, str):
            is_image_bool = payload.is_image.strip() in ["1", "true", "True", "yes"]
        else:
            is_image_bool = bool(payload.is_image)

        logger.info(f"🎨 Image generation enabled: {is_image_bool}")

        # Build a safe base_url for absolute image URLs (respects common proxy headers)
        def _compute_base_url(req: Request) -> str:
            forwarded_proto = req.headers.get("x-forwarded-proto")
            forwarded_host = req.headers.get("x-forwarded-host")
            if forwarded_proto and forwarded_host:
                proto = forwarded_proto.split(",")[0].strip()
                host = forwarded_host.split(",")[0].strip()
                return f"{proto}://{host}"
            # Fallback to Host header combined with detected scheme
            host = req.headers.get("host")
            if host:
                return f"{req.url.scheme}://{host}"
            # Final fallback to request.base_url
            return str(req.base_url).rstrip("/")

        base_url = _compute_base_url(request).rstrip("/")
        logger.info(f"🌐 Computed base URL for images: {base_url}")

        # Generate content with inline image fetching
        generation_start = time.time()

        storigo_content = generate_slide_content_from_prompt(
            prompt=payload.prompt,
            num_slides=payload.num_slides,
            num_mcqs=payload.num_mcqs,
            is_image=is_image_bool,
            is_question=str(payload.is_question),
            question_position=payload.question_position,
            GPU=int(payload.isGPU) if isinstance(payload.isGPU, bool) else payload.isGPU
        )

        generation_time = time.time() - generation_start
        logger.info(f"⏱️  Total generation time: {generation_time:.2f}s")

        # PROFESSIONAL SERIALIZATION
        logger.info("=" * 80)
        logger.info("📦 PREPARING API RESPONSE")
        logger.info("=" * 80)

        response_data = {
            "slides": {},
            "token_count": getattr(storigo_content, "token_count", None)
        }

        # Serialize each slide/MCQ with explicit image handling
        for slide_key, slide_obj in storigo_content.slides.items():
            if hasattr(slide_obj, 'question'):
                # MCQ Content
                response_data["slides"][slide_key] = {
                    "type": slide_obj.type,
                    "question": slide_obj.question,
                    "options": slide_obj.options,
                    "correct_answer": slide_obj.correct_answer
                }
            else:
                # Slide Content with EXPLICIT image handling
                image_value = getattr(slide_obj, 'image', None)

                # Validate and clean image value
                if image_value is not None:
                    image_value = str(image_value).strip()

                    # Treat empty or "none" as no image
                    if not image_value or image_value.lower() == "none":
                        image_value = None
                    else:
                        # If generator returned relative images path like "/images/..."
                        if image_value.startswith("/images/"):
                            image_value = base_url + image_value
                        # If already absolute URL, keep it
                        elif image_value.startswith("http://") or image_value.startswith("https://"):
                            # keep as-is
                            pass
                        else:
                            # If the generator returns just a filename like "slide_1.jpg",
                            # uncomment the line below to convert to /images/<filename>
                            # image_value = base_url + "/images/" + image_value.lstrip("/")
                            # For safety, set to None when not clearly an image path/URL
                            image_value = None

                response_data["slides"][slide_key] = {
                    "type": slide_obj.type,
                    "subheading": getattr(slide_obj, "subheading", None),
                    "paragraphs": getattr(slide_obj, "paragraphs", None),
                    "visualization_suggestion": getattr(slide_obj, "visualization_suggestion", None),
                    "image": image_value  # ALWAYS present: absolute URL string or null
                }

        # Response validation and statistics
        logger.info("🔍 Response Validation:")

        total_slides = sum(1 for k in response_data['slides'].keys() if k.startswith('slide_'))
        total_mcqs = sum(1 for k in response_data['slides'].keys() if k.startswith('mcq_'))
        images_assigned = 0
        images_null = 0

        for key, data in response_data['slides'].items():
            if key.startswith('slide_'):
                if 'image' not in data:
                    logger.error(f"❌ CRITICAL: {key} missing 'image' field!")
                elif data['image']:
                    images_assigned += 1
                    logger.info(f"  ✅ {key}: image = {data['image']}")
                else:
                    images_null += 1
                    logger.info(f"  ⚠️ {key}: image = null")

        logger.info("=" * 80)
        logger.info("📊 RESPONSE SUMMARY:")
        logger.info(f"   - Generation time: {generation_time:.2f}s")
        logger.info(f"   - Total items: {len(response_data['slides'])}")
        logger.info(f"   - Slides: {total_slides}")
        logger.info(f"   - MCQs: {total_mcqs}")
        if is_image_bool and total_slides > 0:
            logger.info(f"   - Images assigned: {images_assigned}")
            logger.info(f"   - Images null: {images_null}")
            logger.info(f"   - Success rate: {(images_assigned/total_slides*100):.1f}%")
        logger.info(f"   - Token count: {response_data['token_count']}")
        logger.info("=" * 80)

        # Final validation: Ensure all slides have 'image' field
        missing_image_field = []
        for key, data in response_data['slides'].items():
            if key.startswith('slide_') and 'image' not in data:
                missing_image_field.append(key)

        if missing_image_field:
            logger.error(f"❌ CRITICAL ERROR: {len(missing_image_field)} slides missing 'image' field")
            raise ValueError(f"Internal error: slides missing image field: {missing_image_field}")

        logger.info("✅ Response validation passed - returning to client")
        logger.info("=" * 80 + "\n")

        return JSONResponse(content=response_data, status_code=200)

    except Exception as e:
        logger.error("=" * 80)
        logger.error("❌ API ENDPOINT ERROR")
        logger.error(f"Error: {str(e)}")
        logger.error("=" * 80)
        logger.error(traceback.format_exc())

        return JSONResponse(
            content={
                "error": str(e),
                "details": traceback.format_exc()
            },
            status_code=500
        )



@app.post("/generate-storigo-content")
async def generate_storigo_content(request: DocumentContentRequest) -> JSONResponse:
    """
    Generate Storigo content from document URL (PDF or HTML)
    
    This endpoint processes documents and generates presentation content:
    - PDF documents with text extraction
    - HTML content from web pages
    - YouTube video transcriptions
    - Semantic chunking and embeddings
    
    Args:
        request: DocumentContentRequest with document URL and configuration
        
    Returns:
        JSONResponse with slides and optional MCQs
    """
    try:
        logger.info("=" * 80)
        logger.info("🚀 API ENDPOINT: /generate-storigo-content")
        logger.info("=" * 80)
        logger.info(f"📋 Request Parameters:")
        logger.info(f"   - Client ID: {request.client_id}")
        logger.info(f"   - Document URL: {request.document_url}")
        logger.info(f"   - Document Type: {request.document_type}")
        logger.info(f"   - Slides: {request.num_slides}")
        logger.info(f"   - MCQs: {request.num_mcqs}")
        logger.info("=" * 80)
        
        if request.document_type == "pdf":
            logger.info(f"📄 Processing PDF document...")
            logger.info(f"   Fetching from: {request.document_url}")
            
            document_content = fetch_document_with_curl(request.document_url)
            logger.info(f"   ✅ Document fetched: {len(document_content)} bytes")

            # Save PDF to temporary file
            with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as temp_file:
                temp_file.write(document_content)
                temp_file_path = temp_file.name
                logger.info(f"   📁 Temporary file: {temp_file_path}")

            try:
                # Parse PDF
                parse_data = parsing(temp_file_path)
                with open("parse_data.md", "w", encoding="utf-8") as f:
                    f.write(parse_data)
                
                # Split into chunks
                headers_to_split_on = [
                    ("#", "Header 1"),
                    ("##", "Header 2")
                ]
                with open("parse_data.md", "r", encoding="utf-8") as f:
                    content = f.read()
                
                chunks = marks_splitter(headers_to_split_on, content)
                logger.info(f"   ✅ Split into {len(chunks)} chunks")
                
                # Allocate slides
                allocation = allocate_slides(chunks, request.num_slides, min_chars=100)
                
                # Create embeddings
                embeddings = create_embeddings(chunks, request.client_id)
                
                # Generate slide content
                slide_content = generate_slide_content_alloc1(
                    chunks, allocation, request.num_slides, request.num_mcqs,
                    request.is_image, request.is_question, 
                    request.question_position, request.isGPU
                )

                response_data = slide_content.dict()
                logger.info("✅ Content generation complete")
                logger.info("=" * 80 + "\n")
                
                return JSONResponse(content=response_data)

            finally:
                # Cleanup temporary file
                if os.path.exists(temp_file_path):
                    os.remove(temp_file_path)
                    logger.info(f"   🗑️  Cleaned up temporary file")

        elif request.document_type == "html":
            logger.info(f"🌐 Processing HTML content...")
            logger.info(f"   URL: {request.document_url}")
            
            if "youtube" in request.document_url.lower():
                logger.info("   📹 YouTube video detected - transcribing...")
                file_path = transcribe(request.document_url)
                embeddings = OllamaEmbeddings(model='nomic-embed-text')
                text = load_txt(file_path)
                split_documents1 = split_text_with_semantic_chunker(text, embeddings)
                meaningful_content = clean_using_llm(split_documents1)
                split_documents = split_text_with_semantic_chunker_for_url(meaningful_content, embeddings)
            else:
                # Crawl the URL for content
                logger.info("   🕷️  Crawling web page...")
                filename = await crawlerrr(request.document_url)
                logger.info(f"   ✅ Content saved to: {filename}")

                # Read the content
                raw_content = read_file_url(filename)
                if not raw_content:
                    raise HTTPException(status_code=500, detail="Failed to read content from URL")

                # Clean and extract meaningful content
                meaningful_content = clean_using_llm(raw_content)
                logger.info("   ✅ Extracted meaningful content")

                # Split into semantic chunks
                embeddings = OllamaEmbeddings(model='nomic-embed-text')
                split_documents = split_text_with_semantic_chunker_for_url(meaningful_content, embeddings)

            # Process and generate response
            logger.info("   🔄 Processing documents and generating content...")
            return process_and_generate_response(
                split_documents, request.client_id, request.num_slides, 
                request.num_mcqs, request.is_image, request.is_question, 
                request.question_position, request.isGPU
            )

        else:
            raise HTTPException(
                status_code=400, 
                detail="Unsupported document_type. Use 'pdf' or 'html'."
            )

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"❌ Error in document processing: {str(e)}")
        logger.error(traceback.format_exc())
        
        return JSONResponse(
            content={
                "error": f"Error: {str(e)}", 
                "details": traceback.format_exc()
            },
            status_code=500
        )

