"""MCP server — exposes PPTX generation and review tools via Model Context Protocol."""

from __future__ import annotations

import json
from typing import Annotated, Literal, Optional

from mcp.server.fastmcp import FastMCP
from mcp.server.fastmcp.exceptions import ToolError
from pydantic import Field

from pptx_mcp.agents.designer import DesignAgent, DesignerInput
from pptx_mcp.agents.iterator import IterationAgent, IterationInput
from pptx_mcp.agents.planner import PlannerInput, SlidePlannerAgent
from pptx_mcp.agents.reviewer import ReviewAgent, ReviewerInput
from pptx_mcp.agents.archetypes import ARCHETYPES
from pptx_mcp.brand.config import resolve_brand_config
from pptx_mcp.brand.defaults import PRESETS
from pptx_mcp.engine.generator import (
    DeckGenerator,
    _delete_slide,
    _duplicate_slide,
    _move_slide,
)
from pptx_mcp.engine.parser import parse_presentation
from pptx_mcp.models.slides import (
    PresentationDefinition,
    SlideDefinition,
    SlideType,
)
from pptx_mcp.review.engine import ReviewEngine
from pptx_mcp.utils.errors import PptxMcpError
from pptx_mcp.utils.logging import get_logger
from pptx_mcp.utils.paths import validate_config_path, validate_output_path, validate_pptx_input

# Type aliases for constrained tool parameters
BrandPreset = Optional[Literal["executive", "technical", "minimal", "creative", "corporate"]]
AudienceType = Literal["executive", "technical", "general"]

logger = get_logger("server")

mcp = FastMCP(
    "pptx-mcp",
    instructions="Model Context Protocol server for creating, editing, and reviewing PowerPoint presentations.",
)


def _raise_tool_error(tool_name: str, e: Exception) -> None:
    """Log the error and raise a ToolError with a safe message.

    Raises ToolError so the MCP response includes isError=True,
    letting the LLM client know the call failed.
    """
    logger.error("%s failed: %s", tool_name, e)
    if isinstance(e, PptxMcpError):
        raise ToolError(str(e)) from e
    error_type = type(e).__name__
    # Include message for common safe exception types so LLMs can recover
    if isinstance(e, (ValueError, TypeError, KeyError, IndexError)):
        raise ToolError(f"{error_type}: {e}") from e
    raise ToolError(f"Operation failed ({error_type}). Check server logs for details.") from e


def _resolve_brand(
    brand_config_path: Optional[str] = None,
    brand_preset: Optional[str] = None,
):
    """Resolve brand config with path validation."""
    if brand_config_path:
        validated = validate_config_path(brand_config_path)
        return resolve_brand_config(path=validated)
    return resolve_brand_config(preset=brand_preset)


# ---------------------------------------------------------------------------
# Tools
# ---------------------------------------------------------------------------


@mcp.tool()
def create_presentation(
    definition: PresentationDefinition,
    output_path: str,
    brand_config_path: Optional[str] = None,
    brand_preset: BrandPreset = None,
) -> str:
    """Create a PPTX presentation from a structured slide definition.

    Typical workflow: Call plan_presentation first to get the definition,
    then pass it here. Or use generate_presentation for single-step creation.
    If unsure about the schema, call get_slide_templates for examples.

    Args:
        definition: Presentation definition with metadata and slides.
        output_path: File path where the .pptx will be saved (must end in .pptx).
        brand_config_path: Optional path to a brand config YAML/JSON file.
        brand_preset: Optional preset name (executive, technical, minimal, creative, corporate).

    Returns:
        JSON with status and output path.
    """
    try:
        validate_output_path(output_path)
        brand = _resolve_brand(brand_config_path, brand_preset)
        generator = DeckGenerator(brand=brand)
        result_path = generator.generate(definition, output_path)
        return json.dumps({
            "status": "success",
            "output_path": str(result_path),
            "slide_count": len(definition.slides),
        })
    except ToolError:
        raise
    except Exception as e:
        _raise_tool_error("create_presentation", e)
        return ""  # unreachable, satisfies type checker


@mcp.tool()
def add_slide(
    file_path: str,
    slide: SlideDefinition,
    position: Optional[Annotated[int, Field(ge=0)]] = None,
    brand_config_path: Optional[str] = None,
    brand_preset: BrandPreset = None,
) -> str:
    """Add a slide to an existing PPTX presentation.

    Args:
        file_path: Path to the existing .pptx file.
        slide: Slide definition with slide_type, title, content, etc.
        position: 0-based position to insert. Appends if omitted.
        brand_config_path: Optional brand config path.
        brand_preset: Optional brand preset name.

    Returns:
        JSON with status.
    """
    try:
        validate_pptx_input(file_path)
        brand = _resolve_brand(brand_config_path, brand_preset)
        generator = DeckGenerator(brand=brand)
        generator.add_slide(file_path, slide, position=position)
        return json.dumps({"status": "success", "file_path": file_path})
    except ToolError:
        raise
    except Exception as e:
        _raise_tool_error("add_slide", e)
        return ""


@mcp.tool()
def update_slide(
    file_path: str,
    slide_index: Annotated[int, Field(ge=0)],
    slide: SlideDefinition,
    brand_config_path: Optional[str] = None,
    brand_preset: BrandPreset = None,
) -> str:
    """Replace a specific slide in an existing presentation.

    Args:
        file_path: Path to the existing .pptx file.
        slide_index: 0-based index of the slide to replace.
        slide: New slide definition to replace the existing slide.
        brand_config_path: Optional brand config path.
        brand_preset: Optional brand preset name.

    Returns:
        JSON with status.
    """
    try:
        validate_pptx_input(file_path)
        brand = _resolve_brand(brand_config_path, brand_preset)
        generator = DeckGenerator(brand=brand)
        generator.update_slide(file_path, slide_index, slide)
        return json.dumps({"status": "success", "file_path": file_path, "slide_index": slide_index})
    except ToolError:
        raise
    except Exception as e:
        _raise_tool_error("update_slide", e)
        return ""


@mcp.tool()
def delete_slide(file_path: str, slide_index: Annotated[int, Field(ge=0)]) -> str:
    """Delete a slide from a presentation.

    Args:
        file_path: Path to the .pptx file.
        slide_index: 0-based index of the slide to delete.

    Returns:
        JSON with status.
    """
    try:
        validate_pptx_input(file_path)
        from pptx import Presentation

        prs = Presentation(file_path)
        _delete_slide(prs, slide_index)
        prs.save(file_path)

        return json.dumps({"status": "success", "file_path": file_path})
    except ToolError:
        raise
    except Exception as e:
        _raise_tool_error("delete_slide", e)
        return ""


@mcp.tool()
def move_slide(
    file_path: str,
    from_index: Annotated[int, Field(ge=0)],
    to_index: Annotated[int, Field(ge=0)],
) -> str:
    """Move a slide from one position to another within a presentation.

    Args:
        file_path: Path to the .pptx file.
        from_index: Current 0-based index of the slide to move.
        to_index: Target 0-based index where the slide should end up.

    Returns:
        JSON with status.
    """
    try:
        validate_pptx_input(file_path)
        from pptx import Presentation

        prs = Presentation(file_path)
        _move_slide(prs, from_index, to_index)
        prs.save(file_path)

        return json.dumps({
            "status": "success",
            "file_path": file_path,
            "moved": {"from": from_index, "to": to_index},
        })
    except ToolError:
        raise
    except Exception as e:
        _raise_tool_error("move_slide", e)
        return ""


@mcp.tool()
def duplicate_slide(
    file_path: str,
    slide_index: Annotated[int, Field(ge=0)],
) -> str:
    """Duplicate a slide, placing the copy immediately after the original.

    Args:
        file_path: Path to the .pptx file.
        slide_index: 0-based index of the slide to duplicate.

    Returns:
        JSON with status and the index of the new slide.
    """
    try:
        validate_pptx_input(file_path)
        from pptx import Presentation

        prs = Presentation(file_path)
        new_index = _duplicate_slide(prs, slide_index)
        prs.save(file_path)

        return json.dumps({
            "status": "success",
            "file_path": file_path,
            "new_slide_index": new_index,
        })
    except ToolError:
        raise
    except Exception as e:
        _raise_tool_error("duplicate_slide", e)
        return ""


@mcp.tool()
def read_presentation(file_path: str, summary: bool = True) -> str:
    """Read and parse an existing PPTX file into structured JSON.

    Args:
        file_path: Path to the .pptx file.
        summary: If True (default), returns a compact outline. Set to False for full detail.

    Returns:
        JSON representation — compact summary or full PresentationDefinition.
    """
    try:
        validate_pptx_input(file_path)
        definition = parse_presentation(file_path)
        if summary:
            slides_summary = []
            for i, s in enumerate(definition.slides):
                entry: dict = {
                    "index": i,
                    "type": s.slide_type.value if s.slide_type else "unknown",
                    "title": s.title or "(untitled)",
                }
                if s.bullets:
                    entry["bullet_count"] = len(s.bullets)
                if s.columns:
                    entry["column_count"] = len(s.columns)
                if s.elements:
                    entry["element_count"] = len(s.elements)
                if s.speaker_notes:
                    entry["has_notes"] = True
                slides_summary.append(entry)
            return json.dumps({
                "title": definition.metadata.title,
                "author": definition.metadata.author,
                "slide_count": len(definition.slides),
                "slides": slides_summary,
            }, indent=2)
        return definition.model_dump_json(indent=2)
    except ToolError:
        raise
    except Exception as e:
        _raise_tool_error("read_presentation", e)
        return ""


@mcp.tool()
def review_presentation(
    file_path: str,
    brand_config_path: Optional[str] = None,
    brand_preset: BrandPreset = None,
) -> str:
    """Review a presentation and provide structured design feedback.

    Args:
        file_path: Path to the .pptx file.
        brand_config_path: Optional brand config path.
        brand_preset: Optional brand preset name.

    Returns:
        JSON review result with scores, issues, and suggestions.
    """
    try:
        validate_pptx_input(file_path)
        brand = _resolve_brand(brand_config_path, brand_preset)
        engine = ReviewEngine(brand=brand)
        result = engine.review_file(file_path)
        return result.model_dump_json(indent=2)
    except ToolError:
        raise
    except Exception as e:
        _raise_tool_error("review_presentation", e)
        return ""


@mcp.tool()
def apply_brand_guidelines(
    file_path: str,
    output_path: Optional[str] = None,
    brand_config_path: Optional[str] = None,
    brand_preset: BrandPreset = None,
) -> str:
    """Re-render a presentation with brand guidelines applied.

    Parses the existing deck, then re-generates it with the specified brand config.

    Args:
        file_path: Path to the existing .pptx file.
        output_path: Where to save the branded version. Overwrites original if omitted.
        brand_config_path: Brand config file path.
        brand_preset: Brand preset name.

    Returns:
        JSON with status and output path.
    """
    try:
        validate_pptx_input(file_path)
        definition = parse_presentation(file_path)
        brand = _resolve_brand(brand_config_path, brand_preset)
        generator = DeckGenerator(brand=brand)
        out = output_path or file_path
        result_path = generator.generate(definition, out)
        return json.dumps({"status": "success", "output_path": str(result_path)})
    except ToolError:
        raise
    except Exception as e:
        _raise_tool_error("apply_brand_guidelines", e)
        return ""


@mcp.tool()
def plan_presentation(
    topic: str,
    audience: AudienceType = "executive",
    num_slides: Annotated[int, Field(ge=3, le=50)] = 10,
    key_points: Optional[list[str]] = None,
    context: str = "",
    author: Optional[str] = None,
    company: Optional[str] = None,
    deck_type: Optional[str] = None,
) -> str:
    """Generate a structured slide plan for a topic.

    Uses the SlidePlanner agent to create a presentation skeleton
    with appropriate slide types and narrative flow.

    Typical workflow: Call this first, then pass the output to create_presentation.
    Or use generate_presentation for a single-step workflow.

    Args:
        topic: Main topic or title.
        audience: Target audience.
        num_slides: Target number of slides (3-50).
        key_points: List of key points to cover.
        context: Additional context or instructions.
        author: Author name.
        company: Company name.
        deck_type: Deck archetype name (e.g. 'pitch_deck', 'board_deck'). Call get_deck_types for options.

    Returns:
        JSON PresentationDefinition (the plan).
    """
    try:
        planner = SlidePlannerAgent()
        plan_input = PlannerInput(
            topic=topic,
            audience=audience,
            num_slides=num_slides,
            key_points=key_points or [],
            context=context,
            author=author,
            company=company,
            deck_type=deck_type,
        )
        result = planner.run(plan_input)
        return result.model_dump_json(indent=2)
    except ToolError:
        raise
    except Exception as e:
        _raise_tool_error("plan_presentation", e)
        return ""


@mcp.tool()
def generate_presentation(
    topic: str,
    output_path: str,
    audience: AudienceType = "executive",
    num_slides: Annotated[int, Field(ge=3, le=50)] = 10,
    key_points: Optional[list[str]] = None,
    context: str = "",
    author: Optional[str] = None,
    company: Optional[str] = None,
    brand_config_path: Optional[str] = None,
    brand_preset: BrandPreset = None,
    deck_type: Optional[str] = None,
) -> str:
    """Plan and create a presentation in a single step.

    Combines plan_presentation + optimize_design + create_presentation into one call.
    Use this when you want to go from topic to .pptx file directly.

    Args:
        topic: Main topic or title.
        output_path: File path where the .pptx will be saved (must end in .pptx).
        audience: Target audience.
        num_slides: Target number of slides (3-50).
        key_points: List of key points to cover.
        context: Additional context or instructions.
        author: Author name.
        company: Company name.
        brand_config_path: Optional path to a brand config YAML/JSON file.
        brand_preset: Optional preset name (executive, technical, minimal, creative, corporate).
        deck_type: Deck archetype name (e.g. 'pitch_deck', 'board_deck'). Call get_deck_types for options.

    Returns:
        JSON with status, output path, and slide count.
    """
    try:
        validate_output_path(output_path)
        # Step 1: Plan
        planner = SlidePlannerAgent()
        plan_input = PlannerInput(
            topic=topic,
            audience=audience,
            num_slides=num_slides,
            key_points=key_points or [],
            context=context,
            author=author,
            company=company,
            deck_type=deck_type,
        )
        definition = planner.run(plan_input)

        # Step 2: Design optimize
        designer = DesignAgent()
        design_result = designer.run(DesignerInput(
            definition=definition,
            brand_config_path=brand_config_path,
            brand_preset=brand_preset,
        ))

        # Step 3: Generate PPTX
        brand = _resolve_brand(brand_config_path, brand_preset)
        generator = DeckGenerator(brand=brand)
        result_path = generator.generate(design_result.definition, output_path)

        return json.dumps({
            "status": "success",
            "output_path": str(result_path),
            "slide_count": len(design_result.definition.slides),
            "design_changes": design_result.changes_made,
        })
    except ToolError:
        raise
    except Exception as e:
        _raise_tool_error("generate_presentation", e)
        return ""


@mcp.tool()
def optimize_design(
    definition: PresentationDefinition,
    brand_config_path: Optional[str] = None,
    brand_preset: BrandPreset = None,
) -> str:
    """Optimize the design/layout of a presentation definition.

    Uses the DesignAgent to apply composition rules, balance columns,
    split overloaded slides, and improve visual hierarchy.

    Args:
        definition: Presentation definition to optimize.
        brand_config_path: Optional brand config path.
        brand_preset: Optional brand preset name.

    Returns:
        JSON with optimized definition and list of changes made.
    """
    try:
        designer = DesignAgent()
        result = designer.run(DesignerInput(
            definition=definition,
            brand_config_path=brand_config_path,
            brand_preset=brand_preset,
        ))
        return json.dumps({
            "definition": json.loads(result.definition.model_dump_json()),
            "changes_made": result.changes_made,
        }, indent=2)
    except ToolError:
        raise
    except Exception as e:
        _raise_tool_error("optimize_design", e)
        return ""


@mcp.tool()
def improve_presentation(
    file_path: str,
    output_path: Optional[str] = None,
    brand_config_path: Optional[str] = None,
    brand_preset: BrandPreset = None,
) -> str:
    """Review and automatically improve a presentation.

    Runs the full pipeline: parse -> review -> iterate -> re-generate.

    Args:
        file_path: Path to the existing .pptx file.
        output_path: Where to save the improved version.
        brand_config_path: Optional brand config path.
        brand_preset: Optional brand preset name.

    Returns:
        JSON with review results and improvement details.
    """
    try:
        validate_pptx_input(file_path)
        brand = _resolve_brand(brand_config_path, brand_preset)

        # Parse
        definition = parse_presentation(file_path)

        # Review
        review_engine = ReviewEngine(brand=brand)
        review = review_engine.review_definition(definition)

        # Iterate
        iterator = IterationAgent()
        iteration_result = iterator.run(IterationInput(
            definition=definition,
            review=review,
        ))

        # Design optimize
        designer = DesignAgent()
        design_result = designer.run(DesignerInput(
            definition=iteration_result.definition,
            brand_config_path=brand_config_path,
            brand_preset=brand_preset,
        ))

        # Re-generate
        out = output_path or file_path
        generator = DeckGenerator(brand=brand)
        result_path = generator.generate(design_result.definition, out)

        # Re-review to get improved score
        improved_review = review_engine.review_definition(design_result.definition)

        return json.dumps({
            "status": "success",
            "output_path": str(result_path),
            "original_score": review.overall_score,
            "improved_score": improved_review.overall_score,
            "fixes_applied": iteration_result.fixes_applied + design_result.changes_made,
            "remaining_issues": len(iteration_result.remaining_issues),
        }, indent=2)
    except ToolError:
        raise
    except Exception as e:
        _raise_tool_error("improve_presentation", e)
        return ""


@mcp.tool()
def get_slide_templates() -> str:
    """List all available slide types with their schemas and descriptions.

    Returns:
        JSON with slide types, required fields, and example structures.
    """
    templates = {
        "title": {
            "description": "Title slide with centered title and optional subtitle",
            "fields": ["title", "subtitle"],
            "example": {"slide_type": "title", "title": "Quarterly Review", "subtitle": "Q4 2025"},
        },
        "section": {
            "description": "Section divider with accent bar",
            "fields": ["title", "subtitle"],
            "example": {"slide_type": "section", "title": "Market Analysis"},
        },
        "content": {
            "description": "Standard content with title, body text, and/or bullets",
            "fields": ["title", "body", "bullets"],
            "example": {
                "slide_type": "content",
                "title": "Key Findings",
                "bullets": ["Revenue grew 15%", "Customer retention at 92%", "NPS improved to 67"],
            },
        },
        "two_column": {
            "description": "Two-column layout with independent content",
            "fields": ["title", "columns"],
            "example": {
                "slide_type": "two_column",
                "title": "Before vs After",
                "columns": [
                    {"title": "Before", "bullets": ["Manual process", "5-day turnaround"]},
                    {"title": "After", "bullets": ["Automated", "Same-day delivery"]},
                ],
            },
        },
        "pros_cons": {
            "description": "Pros and cons comparison with color-coded lists",
            "fields": ["title", "pros", "cons"],
            "example": {
                "slide_type": "pros_cons",
                "title": "Migration Assessment",
                "pros": ["Improved performance", "Lower cost"],
                "cons": ["Migration risk", "Learning curve"],
            },
        },
        "chart": {
            "description": "Chart slide (bar, line, pie, column, area)",
            "fields": ["title", "elements (ChartElement)"],
            "example": {
                "slide_type": "chart",
                "title": "Revenue by Quarter",
                "elements": [{
                    "type": "chart",
                    "chart_type": "bar",
                    "data": {
                        "categories": ["Q1", "Q2", "Q3", "Q4"],
                        "series": [{"name": "Revenue", "values": [100, 120, 115, 140]}],
                    },
                }],
            },
        },
        "comparison": {
            "description": "Multi-column comparison with header bars",
            "fields": ["title", "columns"],
            "example": {
                "slide_type": "comparison",
                "title": "Platform Comparison",
                "columns": [
                    {"title": "AWS", "bullets": ["Mature", "Broadest services"]},
                    {"title": "Azure", "bullets": ["Enterprise focus", "AD integration"]},
                    {"title": "GCP", "bullets": ["ML/AI strength", "Competitive pricing"]},
                ],
            },
        },
        "diagram": {
            "description": "Shapes and connectors for visual diagrams",
            "fields": ["title", "elements (ShapeElement, ConnectorElement)"],
        },
        "table": {
            "description": "Table with header row and data rows",
            "fields": ["title", "elements (TableElement)"],
        },
        "blank": {
            "description": "Empty slide for freeform element placement",
            "fields": ["elements"],
        },
        "quote": {
            "description": "Quote slide with large quote text, attribution, and decorative elements",
            "fields": ["body (the quote)", "quote_attribution", "quote_attribution_role"],
            "example": {
                "slide_type": "quote",
                "body": "The best way to predict the future is to create it.",
                "quote_attribution": "Peter Drucker",
                "quote_attribution_role": "Management Consultant",
            },
        },
        "big_number": {
            "description": "Hero metric slide with a large number, label, and optional context",
            "fields": ["body (the number)", "title (label)", "subtitle (context)", "bullets (comparison)"],
            "example": {
                "slide_type": "big_number",
                "body": "47%",
                "title": "Customer Satisfaction Score",
                "subtitle": "Q4 2025 Results",
                "bullets": ["Up from 32% last quarter"],
            },
        },
        "process": {
            "description": "Horizontal chevron process flow (2-5 steps)",
            "fields": ["title", "steps", "active_step"],
            "example": {
                "slide_type": "process",
                "title": "Implementation Plan",
                "active_step": 1,
                "steps": [
                    {"title": "Discovery", "description": "Gather requirements", "status": "completed"},
                    {"title": "Design", "description": "Create architecture", "status": "active"},
                    {"title": "Build", "description": "Implement solution", "status": "future"},
                ],
            },
        },
        "timeline": {
            "description": "Vertical timeline with milestone dots and content",
            "fields": ["title", "steps (with date field)", "active_step"],
            "example": {
                "slide_type": "timeline",
                "title": "Project Milestones",
                "steps": [
                    {"title": "Kickoff", "date": "Jan 2025", "status": "completed"},
                    {"title": "Beta Launch", "date": "Mar 2025", "status": "active"},
                    {"title": "GA Release", "date": "Jun 2025", "status": "future"},
                ],
            },
        },
        "step_cards": {
            "description": "Numbered step cards in a horizontal row (2-5 cards)",
            "fields": ["title", "steps"],
            "example": {
                "slide_type": "step_cards",
                "title": "How It Works",
                "steps": [
                    {"title": "Sign Up", "description": "Create your account in minutes"},
                    {"title": "Configure", "description": "Set your preferences"},
                    {"title": "Launch", "description": "Go live with one click"},
                ],
            },
        },
        "dashboard": {
            "description": "KPI dashboard with metric cards in an auto-grid (2-12 cards)",
            "fields": ["title", "kpis"],
            "example": {
                "slide_type": "dashboard",
                "title": "Key Metrics — Q4 2025",
                "kpis": [
                    {"value": "$1.2M", "label": "Revenue", "change": "+15%", "trend": "up"},
                    {"value": "92%", "label": "Retention", "change": "+3%", "trend": "up"},
                    {"value": "67", "label": "NPS Score", "change": "-2", "trend": "down"},
                    {"value": "4.2s", "label": "Avg Response", "trend": "neutral"},
                ],
            },
        },
        "image_text": {
            "description": "Split layout with image on one side and text/bullets on the other",
            "fields": ["title", "image_path", "image_position", "body", "bullets"],
            "example": {
                "slide_type": "image_text",
                "title": "Our Product",
                "image_path": "/path/to/product.png",
                "image_position": "left",
                "body": "Next-generation platform for enterprise teams.",
                "bullets": ["Fast deployment", "Built-in analytics", "24/7 support"],
            },
        },
        "icon_grid": {
            "description": "Grid of icon + title + description cards (2-12 items)",
            "fields": ["title", "icons"],
            "example": {
                "slide_type": "icon_grid",
                "title": "Our Values",
                "icons": [
                    {"icon": "\U0001F680", "title": "Speed", "description": "Ship fast, iterate often"},
                    {"icon": "\U0001F91D", "title": "Trust", "description": "Transparent by default"},
                    {"icon": "\U0001F4A1", "title": "Innovation", "description": "Question everything"},
                    {"icon": "\U0001F4AA", "title": "Resilience", "description": "Embrace failure"},
                ],
            },
        },
        "team": {
            "description": "Team bio cards with avatar circles, names, roles, and optional bios",
            "fields": ["title", "team_members"],
            "example": {
                "slide_type": "team",
                "title": "Leadership Team",
                "team_members": [
                    {"name": "Jane Smith", "role": "CEO", "bio": "20 years in enterprise SaaS"},
                    {"name": "John Doe", "role": "CTO", "bio": "Former VP Engineering at Acme"},
                    {"name": "Alice Chen", "role": "CFO", "bio": "IPO experience across 3 companies"},
                ],
            },
        },
        "closing": {
            "description": "Closing / thank-you slide with optional CTA button and contact info",
            "fields": ["title", "subtitle", "cta_text", "contact_info"],
            "example": {
                "slide_type": "closing",
                "title": "Thank You",
                "subtitle": "Questions? Let's connect.",
                "cta_text": "Schedule a Demo",
                "contact_info": ["hello@company.com", "+1 (555) 123-4567"],
            },
        },
        "swot": {
            "description": "SWOT analysis 2x2 quadrant matrix with color-coded sections",
            "fields": ["title", "swot"],
            "example": {
                "slide_type": "swot",
                "title": "SWOT Analysis",
                "swot": {
                    "strengths": ["Strong brand", "Loyal customers"],
                    "weaknesses": ["High costs", "Limited reach"],
                    "opportunities": ["New markets", "Partnerships"],
                    "threats": ["Competition", "Regulation"],
                },
            },
        },
        "funnel": {
            "description": "Vertical funnel with 3-6 stages of decreasing width",
            "fields": ["title", "funnel_stages"],
            "example": {
                "slide_type": "funnel",
                "title": "Sales Pipeline",
                "funnel_stages": [
                    {"label": "Prospects", "value": "10,000", "description": "Website visitors"},
                    {"label": "Leads", "value": "2,500", "description": "Form submissions"},
                    {"label": "Qualified", "value": "800", "description": "Sales-qualified leads"},
                    {"label": "Closed", "value": "120", "description": "Won deals"},
                ],
            },
        },
        "agenda": {
            "description": "Visual agenda with numbered circles and status indicators",
            "fields": ["title", "agenda_items", "active_agenda_item"],
            "example": {
                "slide_type": "agenda",
                "title": "Today's Agenda",
                "active_agenda_item": 1,
                "agenda_items": [
                    {"title": "Opening Remarks", "status": "completed"},
                    {"title": "Q4 Financial Review", "description": "Revenue, margins, and forecast", "status": "active"},
                    {"title": "2026 Strategic Priorities", "description": "Growth plan and investments", "status": "upcoming"},
                    {"title": "Q&A", "status": "upcoming"},
                ],
            },
        },
    }
    return json.dumps(templates, indent=2)


@mcp.tool()
def get_brand_presets() -> str:
    """List available brand configuration presets.

    Returns:
        JSON with preset names and their key properties.
    """
    result = {}
    for name, config in PRESETS.items():
        result[name] = {
            "name": config.name,
            "tone": config.tone.value,
            "primary_color": config.colors.primary,
            "secondary_color": config.colors.secondary,
            "accent_color": config.colors.accent,
            "heading_font": config.fonts.heading_font,
            "body_font": config.fonts.body_font,
        }
    return json.dumps(result, indent=2)


@mcp.tool()
def get_deck_types() -> str:
    """List all available deck archetypes with descriptions and slot sequences.

    Each archetype defines a recommended slide structure for a specific
    presentation type (pitch deck, board update, sales deck, etc.).
    Pass the archetype name as the deck_type parameter to plan_presentation
    or generate_presentation for intelligent slide selection.

    Returns:
        JSON with archetype names, descriptions, slide sequences, and limits.
    """
    result = {}
    for name, arch in ARCHETYPES.items():
        result[name] = {
            "display_name": arch.display_name,
            "description": arch.description,
            "min_slides": arch.min_slides,
            "max_slides": arch.max_slides,
            "slot_sequence": [
                {"slide_type": slot.slide_type.value, "title_template": slot.title_template, "required": slot.required}
                for slot in arch.slots
            ],
        }
    return json.dumps(result, indent=2)


# ---------------------------------------------------------------------------
# Prompts — pre-built templates for common workflows
# ---------------------------------------------------------------------------


@mcp.prompt()
def create_pitch_deck(topic: str, company: str = "", audience: str = "executive") -> str:
    """Generate a complete pitch deck for a topic.

    Creates a polished pitch presentation with problem, solution, traction,
    team, and ask slides using the pitch_deck archetype.
    """
    return (
        f"Create a professional pitch deck about: {topic}\n"
        f"Company: {company or '(not specified)'}\n"
        f"Target audience: {audience}\n\n"
        "Steps:\n"
        f'1. Call generate_presentation with topic="{topic}", '
        f'audience="{audience}", num_slides=10, deck_type="pitch_deck"'
        f'{f", company={company!r}" if company else ""}\n'
        "2. Review the output path and confirm success\n\n"
        "The deck uses the pitch_deck archetype: title, problem, solution, "
        "traction, product, metrics, differentiators, market, team, and closing."
    )


@mcp.prompt()
def review_and_improve(file_path: str) -> str:
    """Review an existing presentation and apply automatic improvements.

    Runs the full review-iterate-optimize pipeline on a deck.
    """
    return (
        f"Review and improve the presentation at: {file_path}\n\n"
        "Steps:\n"
        f'1. Call read_presentation with file_path="{file_path}" to see the current structure\n'
        f'2. Call review_presentation with file_path="{file_path}" to get design feedback\n'
        f'3. Call improve_presentation with file_path="{file_path}" to apply fixes\n'
        "4. Report the before/after scores and changes made"
    )


@mcp.prompt()
def add_process_flow(file_path: str, num_steps: int = 4) -> str:
    """Add a process flow slide to an existing presentation.

    Creates a chevron process flow or step cards slide.
    """
    return (
        f"Add a process flow slide to: {file_path}\n\n"
        f"Create a slide with {num_steps} steps using the 'process' or 'step_cards' slide type.\n"
        "Each step needs a title and brief description.\n\n"
        "Use add_slide with a slide definition containing:\n"
        '- slide_type: "process" (for chevron flow) or "step_cards" (for numbered cards)\n'
        "- title: descriptive title for the slide\n"
        f'- steps: array of {num_steps} objects with title, description, and status fields\n'
        '- active_step: 0-based index of the current step (optional)\n'
    )


@mcp.prompt()
def create_board_update(topic: str, company: str = "", quarter: str = "") -> str:
    """Generate a board update deck with financials, strategy, and operational review."""
    return (
        f"Create a board update presentation about: {topic}\n"
        f"Company: {company or '(not specified)'}\n"
        f"Quarter: {quarter or '(current)'}\n\n"
        "Steps:\n"
        f'1. Call generate_presentation with topic="{topic}", '
        f'audience="executive", num_slides=12, deck_type="board_deck"'
        f'{f", company={company!r}" if company else ""}\n'
        "2. Review the output path and confirm success\n\n"
        "The deck uses the board_deck archetype: title, agenda, financial dashboard, "
        "revenue charts, strategic initiatives, SWOT analysis, operational highlights, "
        "risks, and next steps."
    )


@mcp.prompt()
def create_sales_deck(topic: str, company: str = "") -> str:
    """Generate a customer-facing sales presentation with value proposition and social proof."""
    return (
        f"Create a sales presentation about: {topic}\n"
        f"Company: {company or '(not specified)'}\n\n"
        "Steps:\n"
        f'1. Call generate_presentation with topic="{topic}", '
        f'audience="general", num_slides=10, deck_type="sales_deck"'
        f'{f", company={company!r}" if company else ""}\n'
        "2. Review the output path and confirm success\n\n"
        "The deck uses the sales_deck archetype: title, challenge, approach, "
        "comparison, results dashboard, customer quote, pricing, and CTA."
    )


@mcp.prompt()
def create_all_hands(topic: str, company: str = "") -> str:
    """Generate an all-hands meeting deck with achievements, metrics, and team recognition."""
    return (
        f"Create an all-hands meeting presentation about: {topic}\n"
        f"Company: {company or '(not specified)'}\n\n"
        "Steps:\n"
        f'1. Call generate_presentation with topic="{topic}", '
        f'audience="general", num_slides=10, deck_type="all_hands"'
        f'{f", company={company!r}" if company else ""}\n'
        "2. Review the output path and confirm success\n\n"
        "The deck uses the all_hands archetype: title, agenda, highlights, "
        "key metrics, roadmap, new team members, and closing."
    )


# ---------------------------------------------------------------------------
# Resources
# ---------------------------------------------------------------------------


@mcp.resource("pptx://schema/slide-definition")
def slide_definition_schema() -> str:
    """JSON schema for SlideDefinition."""
    return json.dumps(SlideDefinition.model_json_schema(), indent=2)


@mcp.resource("pptx://schema/presentation-definition")
def presentation_definition_schema() -> str:
    """JSON schema for PresentationDefinition."""
    return json.dumps(PresentationDefinition.model_json_schema(), indent=2)


# ---------------------------------------------------------------------------
# Entrypoint
# ---------------------------------------------------------------------------


def run_server() -> None:
    """Start the MCP server."""
    logger.info("Starting PPTX MCP server")
    mcp.run()


if __name__ == "__main__":
    run_server()
