"""
PPTX Router - PowerPoint template generation and parsing endpoints.
"""

import base64
import json

from fastapi import APIRouter, File, Form, HTTPException, UploadFile, status
from fastapi.responses import Response
from loguru import logger

from app.models import (
    FileMetadataResponse,
    GeneratePptxResponse,
    HtmlToPptxRequest,
    MarkdownToPptxRequest,
    ParseFileResponse,
)
from app.services.file_parser_service import get_file_parser_service
from app.services.template_service import get_template_service

router = APIRouter(prefix="/api/v1/pptx", tags=["PPTX"])

_FILE_TEMPLATE = File(None, description="Optional template PPTX file to use as base")
_FILE_UPLOAD = File(..., description="PPTX file to parse")


@router.post("", response_model=GeneratePptxResponse)
async def generate_pptx_from_json(
    slides_content: str = Form(..., description="JSON array of slide content"),
    branding: str = Form(None, description="Optional JSON branding object"),
    template_file: UploadFile = _FILE_TEMPLATE,
):
    """
    Generate a PPTX from JSON content with optional template and branding.

    When template_file is provided, the template is used as a base,
    preserving all styling, backgrounds, and decorative elements. New slides
    are created using the template's layouts.

    When no template is provided, creates a new presentation and optionally
    applies branding (fonts, colors) if specified.

    Each slide in the array can have:
    - title: Slide title
    - subtitle: Slide subtitle
    - textContent: List of text paragraphs
    - bulletPoints: List of bullet point items
    - tables: List of tables with headers and rows
    - layoutName: Optional layout name hint (e.g., "Title Slide", "Blank")

    Branding (used when no template provided) can include:
    - titleFontName, bodyFontName: Font names
    - titleFontSize, bodyFontSize: Font sizes in points
    - primaryColor, secondaryColor, accentColor: Hex colors
    - slideWidth, slideHeight: Slide dimensions in inches

    Args:
        slides_content: JSON string of slide content array
        branding: Optional JSON string of branding settings
        template_file: Optional template PPTX file upload

    Returns:
        Generated PPTX as base64 string
    """
    try:
        # Parse JSON string
        try:
            slides_content_list = json.loads(slides_content)
        except json.JSONDecodeError as e:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"Invalid slides_content JSON: {e!s}",
            ) from e

        # Parse optional branding
        branding_dict = None
        if branding:
            try:
                branding_dict = json.loads(branding)
            except json.JSONDecodeError as e:
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail=f"Invalid branding JSON: {e!s}",
                ) from e

        # Read optional template file
        template_bytes = None
        if template_file:
            try:
                template_bytes = await template_file.read()
            except OSError as e:
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail=f"Failed to read template file: {e!s}",
                ) from e

        template_service = get_template_service()

        pptx_bytes = await template_service.generate_pptx_from_content(
            slides_content=slides_content_list,
            branding=branding_dict,
            template_bytes=template_bytes,
        )

        file_base64 = base64.b64encode(pptx_bytes).decode("utf-8")

        return GeneratePptxResponse(
            success=True,
            file_base64=file_base64,
            file_size=len(pptx_bytes),
        )

    except HTTPException:
        raise
    except ValueError as e:
        # Validation errors - pass through the detailed message to help AI adjust
        logger.warning(f"PPTX validation error: {e}")
        return GeneratePptxResponse(
            success=False,
            error=str(e),
        )
    except RuntimeError as e:
        # Strict validation failures - pass through detailed message
        logger.error(f"PPTX generation runtime error: {e}")
        return GeneratePptxResponse(
            success=False,
            error=str(e),
        )
    except Exception as e:
        # Unexpected errors - log full traceback but still expose message
        logger.exception("Unexpected error generating PPTX")
        return GeneratePptxResponse(
            success=False,
            error=f"Unexpected error: {e!s}",
        )


_PPTX_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.presentation"


@router.post("/from-markdown")
async def convert_markdown_to_pptx(request: MarkdownToPptxRequest):
    """
    Convert Markdown content to PPTX.

    Parses markdown into HTML, then extracts slide structure (headings become
    slide titles, lists become bullet points, etc.) and generates a PowerPoint.

    Args:
        request: Markdown content

    Returns:
        PPTX file as binary response
    """
    try:
        from app.services.base_converter import BaseConverterService
        from app.services.html_to_pptx_converter import html_to_slides

        converter = BaseConverterService()
        html = await converter.markdown_to_html(request.content)
        slides_content = html_to_slides(html)

        template_service = get_template_service()
        pptx_bytes = await template_service.generate_pptx_from_content(
            slides_content=slides_content,
        )

        return Response(
            content=pptx_bytes,
            media_type=_PPTX_CONTENT_TYPE,
            headers={"Content-Disposition": "attachment; filename=presentation.pptx"},
        )

    except Exception:
        logger.exception("Error converting markdown to PPTX")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to convert markdown to PPTX",
        ) from None


@router.post("/from-html")
async def convert_html_to_pptx(request: HtmlToPptxRequest):
    """
    Convert HTML content to PPTX.

    Parses HTML to extract slide structure (h1/h2 headings become slide titles,
    lists become bullet points, tables preserved) and generates a PowerPoint.

    Args:
        request: HTML content

    Returns:
        PPTX file as binary response
    """
    try:
        from app.services.html_to_pptx_converter import html_to_slides

        slides_content = html_to_slides(request.html)

        template_service = get_template_service()
        pptx_bytes = await template_service.generate_pptx_from_content(
            slides_content=slides_content,
        )

        return Response(
            content=pptx_bytes,
            media_type=_PPTX_CONTENT_TYPE,
            headers={"Content-Disposition": "attachment; filename=presentation.pptx"},
        )

    except Exception:
        logger.exception("Error converting HTML to PPTX")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to convert HTML to PPTX",
        ) from None


@router.post("/parse", response_model=ParseFileResponse)
async def parse_pptx_file(
    file: UploadFile = _FILE_UPLOAD,
    user_input: str | None = Form(None, description="User instruction for AI extraction per slide"),
    process_images: bool = Form(True, description="Extract and describe embedded images"),
    model: str | None = Form(None, description="LLM model for text processing"),
):
    """
    Parse a PPTX file and extract its text content using Vision API.

    Uses Vision API to:
    - Extract and describe embedded images
    - Optionally process with user instructions

    Args:
        file: The PPTX file to parse
        user_input: Optional user instruction for AI extraction per slide
        process_images: Whether to extract and describe embedded images

    Returns:
        Parsed content including full text and metadata
    """
    try:
        file_bytes = await file.read()

        if not file_bytes:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Empty file uploaded",
            )

        filename = file.filename or "unknown.pptx"

        parser = get_file_parser_service()
        result = await parser.parse_pptx_with_vision(
            file_bytes,
            filename,
            user_input=user_input,
            process_images=process_images,
            model=model,
        )

        return ParseFileResponse(**result)

    except HTTPException:
        raise
    except Exception as e:
        logger.exception("Error parsing PPTX file")
        return ParseFileResponse(
            success=False,
            filename=file.filename or "unknown",
            error=f"Failed to parse PPTX file: {e!s}",
        )


@router.post("/extract-metadata", response_model=FileMetadataResponse)
async def extract_pptx_metadata(file: UploadFile = _FILE_UPLOAD):
    """Extract metadata from a PPTX file without full text extraction."""
    from app.services.file_parser_service import _extract_ooxml_metadata

    try:
        file_bytes = await file.read()
        if not file_bytes:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Empty file uploaded")

        filename = file.filename or "unknown.pptx"
        meta = _extract_ooxml_metadata(file_bytes, "pptx")

        from io import BytesIO

        from pptx import Presentation

        prs = Presentation(BytesIO(file_bytes))

        return FileMetadataResponse(
            success=True,
            filename=filename,
            file_type="application/vnd.openxmlformats-officedocument.presentationml.presentation",
            title=meta["title"] or None,
            author=meta["author"] or None,
            slide_count=len(prs.slides),
            created_at=meta["created_at"],
            modified_at=meta["modified_at"],
            scanned_pages_detected=0,
        )
    except HTTPException:
        raise
    except Exception:
        logger.exception("Error extracting PPTX metadata")
        return FileMetadataResponse(
            success=False,
            filename=file.filename or "unknown",
            error="Failed to extract PPTX metadata",
        )
