from __future__ import annotations

from .deck_models import (
    FOUR_CARD_TEXT_BODY_MAX_BEATS,
    KPI_CARD_DETAIL_MAX_BEATS,
    KPI_CARD_DETAIL_MAX_CHARS,
    KPI_CARD_LABEL_MAX_CHARS,
    KPI_CARD_LABEL_MAX_WORDS,
    KPI_CARD_VALUE_MAX_CHARS,
    KPI_CARD_VALUE_MAX_WORDS,
    TABLE_CARD_MAX_COLUMNS,
    TABLE_CARD_MAX_ROWS,
)
from .planning import STANDARD_GENERATION_SLIDE_TYPES

STANDARD_GENERATION_SLIDE_TYPES_PROMPT = "\n".join(f"- {slide_type}" for slide_type in STANDARD_GENERATION_SLIDE_TYPES)


def build_pptx_plan_system_prompt() -> str:
    return f"""
You generate YAML only.

Your job is to plan a PPTX deck before the final deck YAML is generated.

Output schema:
- target_slide_count: integer
- slides: ordered list
- each slide item must include:
  - index
  - type
  - goal
  - key_data_points
  - must_be_informative

Allowed slide types:
{STANDARD_GENERATION_SLIDE_TYPES_PROMPT}

Planning rules:
- Respect exact slide-count requests strictly
- Build a narrative that progresses clearly from setup to analysis to action
- Prefer informative slides over decorative slides
- Use section and quote slides sparingly
- Avoid using too many weak transition slides
- Prefer standard slide types
- For slides that should carry real substance, set must_be_informative: true
- key_data_points should name the facts, metrics, or messages that the final deck must cover on that slide
- PlannedSlide.type must be one of the allowed slide types above, using those exact values
- Do not invent new slide types
- Do not use custom slides in the standard generation flow
- Use kpi_grid only when each card is a compact KPI; if a 4-card slide needs takeaways, actions, or explainers, prefer columns

Valid compact DeckPlan example:
target_slide_count: 3
slides:
  - index: 1
    type: title
    goal: "Introduce the topic"
    key_data_points:
      - "Presentation topic"
    must_be_informative: false
  - index: 2
    type: columns
    goal: "Explain the core dimensions"
    key_data_points:
      - "Dimension A"
      - "Dimension B"
      - "Dimension C"
    must_be_informative: true
  - index: 3
    type: quote
    goal: "Close with one memorable takeaway"
    key_data_points:
      - "Closing message"
    must_be_informative: true

Return YAML only.
""".strip()


def build_pptx_targeted_repair_system_prompt() -> str:
    return """
You generate YAML only.

Your job is to return slide replacements for a PPTX deck.

Output schema:
- slides: list
- each item must include:
  - index
  - slide

The slide field must contain one valid slide specification.
Do not return the full deck.
Do not return explanations or markdown fences.
""".strip()


def build_pptx_plan_user_prompt(user_prompt: str) -> str:
    return f"""
Create a compact YAML deck plan for the following request.

User request:
{user_prompt}

Planning constraints:
- Return YAML only
- Do not generate the final deck YAML yet
- target_slide_count must match any exact slide-count request in the user prompt
- The slides list must have exactly target_slide_count items
- PlannedSlide.type must be one of the allowed slide types from the system prompt
- Do not invent new slide types
- Use section or quote slides only when they add clear value to the narrative
- Use kpi_grid only for compact KPI cards, not for narrative takeaways or action lists
- Make the plan concrete enough that a second model pass can execute it faithfully
""".strip()


def build_pptx_system_prompt() -> str:
    return f"""
You generate YAML only. Do not output explanations, prose, or markdown fences unless strictly necessary.

Your job is to produce a valid YAML deck specification that will be rendered into an editable PPTX.

Core contract:
- Unless the user asks otherwise, produce a simple, clear, professional, business-ready deck.
- Output a YAML mapping with exactly two top-level keys: deck, slides
- deck.size must be wide_16_9 unless the user clearly asks for another format
- Keep the deck fully self-contained and editable
- Do not attempt HTML, CSS, or browser-specific layouts
- Prefer 6 to 8 slides unless the user explicitly asks otherwise
- Be faithful to the user's request, including controversial or nuanced conclusions

Visual model: global theme first, local slide overrides second
- deck.theme.* defines the global visual base for the whole deck
- slides[].design.* defines optional slide-level overrides
- If the user asks for a general brand color, accent color, or palette, put it in deck.theme.primary_color
- If the user asks for a general deck background color, put it in deck.theme.background_color
- If the user asks for one specific slide to feel dark, light, or gradient, use slides[].design.background for that slide
- slides[].design.background is not a freeform color field; it only accepts: light, dark, gradient
- Omit slide-level design overrides unless a slide genuinely needs a local stylistic treatment

Theme schema:
- deck.theme.font_family
- deck.theme.primary_color
- deck.theme.background_color
- When you specify colors in deck.theme, use hex color strings such as "#06312E"

Slide-level design schema:
- slides[].design.variant: plain, cards, bordered, accent_left
- slides[].design.density: compact, medium, spacious
- slides[].design.tone: professional, minimal, bold
- slides[].design.background: light, dark, gradient

Allowed slide types:
{STANDARD_GENERATION_SLIDE_TYPES_PROMPT}

Slide schema reminders:
- title: requires title, optional subtitle and body
- section: requires title, optional subtitle and body
- columns: requires layout.columns set to 2, 3, or 4 and a columns list with exactly that many items; each item requires title and at least one of body or table
- comparison: requires top-level left and right objects; each one requires title and at least one of body or table
- timeline: requires an items list with at least two items; each item requires title and body
- kpi_grid: requires a metrics list with at least two items; each item requires value and label, optional detail; use it only for compact KPI cards
- quote: requires a top-level quote field; use attribution for the source name
- image_text: requires a top-level image_text object with body; optional fields inside it are asset_id, title, position, and image_label
- image: requires a top-level image object with asset_id; optional field inside it is image_label
- image_text.position only accepts these exact values: left, right, bottom
- Do not invent alternate field names such as text for quote slides or image for image_text slides

Table schema reminders for columns and comparison cards:
- use table.columns for the ordered header labels
- use table.rows for the ordered row values
- every row must have exactly the same number of cells as table.columns
- tables are only appropriate for compact datasets that fit in one card
- keep tables to at most {TABLE_CARD_MAX_COLUMNS} columns and {TABLE_CARD_MAX_ROWS} data rows
- if the data is larger than that, do not display a table
- do not encode tables as pipe-delimited text inside body; use the structured table object instead

YAML safety rules:
- Quote any inline string that contains a colon `:`
- Use block scalar `|` for multi-line text
- For free-text fields such as title, subtitle, body, value, label, quote, attribution, and image_label, never emit an unquoted inline scalar if it contains `:`
- If a text payload is awkward to serialize safely inline, switch that field to a quoted string or a block scalar instead of improvising YAML
- Invalid example: `text: Progression Rapide : Astuces Essentielles`
- Valid example: `title: "Progression Rapide : Astuces Essentielles"`

Design token intent:
- tone: professional = restrained and credible, minimal = light and airy, bold = stronger contrast and larger typography
- background: light = warm off-white canvas, dark = charcoal canvas, gradient = layered editorial background
- variant: plain = low-ink composition, cards = soft editorial panels, bordered = crisp framed panels, accent_left = strong accent rail
- density: compact = tighter spacing, medium = balanced spacing, spacious = generous whitespace

Content guidance:
- Use a clear cover slide
- Structure the argument progressively so the viewer learns something new from slide to slide
- Keep slide copy concise, but not thin
- Avoid decks that feel decorative but empty; every non-title slide should teach, compare, explain, quantify, or sequence something concrete
- Avoid filler slides such as vague intros, generic benefits, or one-line conclusions unless the user explicitly asked for them
- On content slides, prefer 3 to 5 short lines per body field, separated by newlines
- For columns and comparison slides, each body should contain multiple concrete points when body text is used
- For timeline slides, each step should explain what happens, why it matters, or what comes next
- For image_text slides, the text block should feel like a mini-explainer, not a caption
- Use image_text.position: bottom when the slide should be mostly visual with a short explanatory band underneath
- Never invent localized or freeform position values such as "en bas"; use only left, right, or bottom
- Use slide type `image` when a slide should be almost entirely one source image without a side explainer block
- When source image assets are listed in the user request context, use only those exact asset ids such as `img_1`
- Do not use `image` or `image_text` unless source image assets are explicitly listed in the request context
- Preserve existing asset ids in revisions unless the user explicitly asks to remove, replace, or reorder the images
- For section slides, include a subtitle or body when useful; avoid title-only dividers unless they are genuinely needed
- For kpi_grid slides, include detail on most metrics
- Use kpi_grid only when each metric is visually compact:
  - value must stay on one line, at most {KPI_CARD_VALUE_MAX_CHARS} characters, and at most {KPI_CARD_VALUE_MAX_WORDS} words
  - label must stay on one line, at most {KPI_CARD_LABEL_MAX_CHARS} characters, and at most {KPI_CARD_LABEL_MAX_WORDS} words
  - detail must stay concise, at most {KPI_CARD_DETAIL_MAX_BEATS} short beats and at most {KPI_CARD_DETAIL_MAX_CHARS} characters
- If a 4-card slide contains takeaways, actions, explanations, or mini-paragraphs, do not use kpi_grid; use columns or comparison instead
- For 4-card text layouts, keep each card concise: title-like text should stay short, and body text should stay within about {FOUR_CARD_TEXT_BODY_MAX_BEATS} short beats
- If a compact table is the clearest way to present data inside a columns or comparison card, use the structured table object
- If a table would exceed {TABLE_CARD_MAX_COLUMNS} columns or {TABLE_CARD_MAX_ROWS} rows, do not display a table
- Prefer at least one section, one emphasis slide (quote or section), and one non-columns layout when the topic allows it
- Prefer standard slide types by default
- Do not use custom slides in the standard generation flow
- If a standard slide type can express the idea cleanly, use it instead of custom

Topic-specific guidance:
- For a process, procedure, training path, or how-to:
  - include prerequisites, major steps, effort or timing, common pitfalls, and a practical wrap-up
  - make the sequence actionable enough that a viewer could follow it after reading the deck
- For an explanatory or strategic topic:
  - include mechanism, tradeoffs, implications, and at least one concrete example or metric where relevant

Correct pattern example with a global theme:
deck:
  size: wide_16_9
  theme:
    primary_color: "#06312E"
    background_color: "#FFFFFF"
slides:
  - type: title
    title: "Title here"

Correct pattern example with a local slide override:
deck:
  size: wide_16_9
  theme:
    primary_color: "#06312E"
slides:
  - type: section
    title: "Overview"
  - type: quote
    quote: "A short quote"
    design:
      background: dark
      tone: bold

Correct compact table pattern inside a columns slide:
deck:
  size: wide_16_9
slides:
  - type: columns
    title: "Sales Snapshot"
    layout:
      columns: 2
    columns:
      - title: "Data Table"
        table:
          columns: ["City", "Units Sold"]
          rows:
            - ["Toulouse", "587"]
            - ["Bordeaux", "448"]
      - title: "Key Takeaways"
        body: |
          Toulouse leads the ranking
          Bordeaux and Paris form the middle tier
          Marseille trails the group

Incorrect pattern:
- Do not use slides[].design.background: red
- If the user asks for a red deck background, use deck.theme.background_color: "#FF0000"
- Do not emit unquoted inline strings that contain `:`
- Do not use custom slides in the standard generation flow
- Do not encode tables as ASCII art in body text
""".strip()


def build_pptx_revision_user_prompt(*, source_deck_yaml: str, revision_instructions: str) -> str:
    return f"""
Revise the following PPTX deck YAML according to the requested changes.

Revision contract:
- Return the full revised deck YAML, not a patch
- Preserve the source deck's slide order, slide count, theme, tone, and content unless the requested changes require otherwise
- Preserve unchanged slides as closely as possible
- Preserve existing image asset ids for visuals already present in the source deck unless the revision explicitly changes them
- If new source images are provided in the source artifact context, you may introduce those new asset ids when the revision asks to add or replace visuals
- Apply the user's requested changes precisely
- Keep the result as one valid deck specification with exactly two top-level keys: deck, slides
- Return YAML only

Revision instructions:
{revision_instructions}

Source deck YAML:
{source_deck_yaml}
""".strip()


def build_pptx_revision_repair_prompt(
    *,
    source_deck_yaml: str,
    revision_instructions: str,
    current_deck_yaml: str,
    validation_error: str,
    quality_feedback: str | None,
    attempt: int,
    max_attempts: int,
) -> str:
    quality_section = (
        f"\nQuality feedback to address:\n{quality_feedback}\n" if quality_feedback and quality_feedback.strip() else ""
    )
    return f"""
Repair the revised PPTX deck YAML.

Return the full revised deck YAML, not a patch.
Preserve the requested revision and keep unchanged source content stable unless a change is required.
Preserve existing image asset ids for unchanged visuals, and use any newly provided source image asset ids only when the revision asks to add or replace visuals.
Return YAML only.

Attempt: {attempt}/{max_attempts}

Revision instructions:
{revision_instructions}

Validation error to fix:
{validation_error}
{quality_section}
Source deck YAML:
{source_deck_yaml}

Current revised deck YAML:
{current_deck_yaml}
""".strip()


def build_pptx_revision_syntax_repair_prompt(
    *,
    source_deck_yaml: str,
    revision_instructions: str,
    current_deck_yaml: str,
    syntax_error: str,
    attempt: int,
    max_attempts: int,
) -> str:
    return f"""
Fix only the YAML syntax of the current revised PPTX deck YAML.

Do not rewrite the deck content unless required to make the YAML valid.
Preserve any existing image asset ids exactly, and keep any newly introduced source image asset ids unchanged.
Return the full revised deck YAML.
Return YAML only.

Attempt: {attempt}/{max_attempts}

Revision instructions:
{revision_instructions}

Syntax error to fix:
{syntax_error}

Source deck YAML:
{source_deck_yaml}

Current revised deck YAML:
{current_deck_yaml}
""".strip()


def build_pptx_deck_user_prompt(*, user_prompt: str, deck_plan_yaml: str) -> str:
    return f"""
Create the final YAML deck specification for the following request.

Original user request:
{user_prompt}

Approved deck plan:
{deck_plan_yaml}

Execution constraints:
- Return YAML only
- Follow the approved deck plan exactly
- Keep the same slide count and slide order as the plan
- Each planned slide must fulfill its stated goal
- Slides marked as informative in the plan must contain real substance, not placeholders
- Keep the visual direction coherent across the whole deck
- Prefer concise but concrete content
- Preserve deck.theme as the global visual layer and slides[].design only for local overrides
- Do not use custom slides in the standard generation flow
- When source image assets are listed in the request context, reference them only via their exact asset ids
- Use slide type `image` for pure visual slides and `image_text.asset_id` for image-plus-text slides
- If no source image assets are listed in the request context, do not emit `image` or `image_text` slides
- For compact data tables inside columns or comparison cards, use structured table.columns/table.rows instead of ASCII text
- If the data would exceed {TABLE_CARD_MAX_COLUMNS} columns or {TABLE_CARD_MAX_ROWS} rows, do not display a table
- Quote any inline string that contains `:`
- Use `|` for multi-line text
- Invalid example: `text: Progression Rapide : Astuces Essentielles`
- Valid example: `title: "Progression Rapide : Astuces Essentielles"`
""".strip()


def build_pptx_plan_repair_prompt(
    *,
    user_prompt: str,
    previous_plan_yaml: str,
    validation_error: str,
    attempt: int,
    max_attempts: int,
) -> str:
    return f"""
The previous deck plan must be corrected.

Original user request:
{user_prompt}

Previous deck plan:
{previous_plan_yaml}

Plan validation issues:
{validation_error}

Correct the deck plan while preserving the original intent.
Respect any exact slide-count request strictly.
Keep the plan compact, concrete, and executable by a later deck-generation pass.
If a slide type was rejected, replace it with the closest allowed slide type instead of inventing a synonym.

Allowed slide types:
{STANDARD_GENERATION_SLIDE_TYPES_PROMPT}

Critical reminders:
- PlannedSlide.type must use one of the exact allowed values above
- Do not invent new slide types
- Keep the same target_slide_count unless the validation error proves it is wrong
- Keep the slide order stable unless the validation error requires a small adjustment

Return YAML only.
This is plan repair attempt {attempt} of {max_attempts}.
""".strip()


def build_pptx_full_repair_prompt(
    *,
    user_prompt: str,
    deck_plan_yaml: str,
    current_deck_yaml: str,
    validation_error: str,
    quality_feedback: str | None = None,
    attempt: int,
    max_attempts: int,
) -> str:
    quality_section = ""
    if quality_feedback:
        quality_section = f"""
Quality gate findings:
{quality_feedback}
"""

    return f"""
The current deck YAML must be corrected.

Original user request:
{user_prompt}

Approved deck plan:
{deck_plan_yaml}

Current deck YAML:
{current_deck_yaml}

Validation or quality issues:
{validation_error}
{quality_section}

Correction rules:
- Return YAML only
- Preserve the approved slide count and slide order
- Preserve the global theme and overall narrative unless the error requires a local change
- Modify only what is needed to satisfy validation, constraints, and quality
- If a slide type is a poor fit, you may convert that slide to the closest better standard type
- If a 4-card slide contains narrative takeaways or action text rather than compact KPIs, convert kpi_grid to columns or comparison
- Keep informative slides dense and concrete
- If a card-level table is too large, do not display a table

Return corrected YAML only.
This is deck repair attempt {attempt} of {max_attempts}.
""".strip()


def build_pptx_targeted_repair_prompt(
    *,
    user_prompt: str,
    deck_plan_yaml: str,
    flagged_slides_yaml: str,
    quality_feedback: str,
    attempt: int,
    max_attempts: int,
) -> str:
    return f"""
Repair only the flagged slides from the current deck.

Original user request:
{user_prompt}

Approved deck plan:
{deck_plan_yaml}

Flagged slides:
{flagged_slides_yaml}

Quality gate findings:
{quality_feedback}

Return YAML only with this patch schema:
slides:
  - index: 1
    slide:
      type: section
      title: "Example"
      body: "Replacement content"

Patch rules:
- Return only the slides that must be replaced
- Each patch item must contain index and slide
- Preserve all other slides unchanged
- Keep the same slide indices
- You may change the slide type of a flagged slide if that is the cleanest way to satisfy the quality gate
- If a 4-card slide is using kpi_grid for narrative takeaways or action text, replace it with columns or comparison
- The replacement slide must satisfy the normal deck schema

Return YAML only.
This is targeted repair attempt {attempt} of {max_attempts}.
""".strip()


def build_pptx_syntax_repair_prompt(
    *,
    user_prompt: str,
    deck_plan_yaml: str,
    current_deck_yaml: str,
    syntax_error: str,
    attempt: int,
    max_attempts: int,
) -> str:
    return f"""
The current deck YAML is syntactically invalid and must be repaired.

Original user request:
{user_prompt}

Approved deck plan:
{deck_plan_yaml}

Current deck YAML:
{current_deck_yaml}

YAML syntax error:
{syntax_error}

Repair rules:
- Return YAML only
- Preserve the approved slide count and slide order exactly
- Preserve the same content, theme, and slide types unless a change is strictly required to make the YAML parseable
- Do not use custom slides in the standard generation flow
- Do not rewrite structured table objects into ASCII body text
- Quote any inline string that contains `:`
- Use `|` for multi-line text
- Do not rewrite the deck for style or content quality in this phase
- Fix serialization only
- Invalid example: `text: Progression Rapide : Astuces Essentielles`
- Valid example: `title: "Progression Rapide : Astuces Essentielles"`

Return corrected YAML only.
This is syntax repair attempt {attempt} of {max_attempts}.
""".strip()
