"""Brand guide generator — creates a PPTX showcasing a BrandConfig's visual identity."""

from __future__ import annotations

from pathlib import Path

from pptx import Presentation as PptxPresentation
from pptx.dml.color import RGBColor
from pptx.enum.shapes import MSO_SHAPE
from pptx.enum.text import PP_ALIGN
from pptx.util import Inches, Pt

from pptx_mcp.engine.generator import DeckGenerator
from pptx_mcp.engine.shapes import add_drop_shadow, parse_hex_color, set_corner_radius
from pptx_mcp.models.brand import BrandConfig
from pptx_mcp.models.slides import (
    CardDefinition,
    ChartDataSeries,
    ChartData,
    ChartElement,
    ColumnContent,
    KpiCard,
    PresentationDefinition,
    PresentationMetadata,
    ProcessStep,
    SlideDefinition,
    SlideType,
    TableCell,
    TableElement,
)


# ---------------------------------------------------------------------------
# Helper: add a titled blank slide
# ---------------------------------------------------------------------------

def _add_blank_slide(prs: PptxPresentation) -> object:
    """Add a blank slide using the presentation's blank layout."""
    blank_layout = prs.slide_layouts[6]  # standard blank layout
    return prs.slides.add_slide(blank_layout)


def _add_title_text(
    slide: object,
    text: str,
    brand: BrandConfig,
    *,
    left: float = 0.7,
    top: float = 0.4,
    width: float = 11.933,
    height: float = 0.6,
) -> None:
    """Add a title textbox to a raw slide."""
    txBox = slide.shapes.add_textbox(
        Inches(left), Inches(top), Inches(width), Inches(height),
    )
    tf = txBox.text_frame
    tf.word_wrap = True
    p = tf.paragraphs[0]
    p.text = text
    p.font.size = Pt(brand.fonts.heading_size)
    p.font.bold = True
    p.font.name = brand.fonts.heading_font
    p.font.color.rgb = parse_hex_color(brand.colors.text_primary)


def _add_label(
    slide: object,
    text: str,
    left: float,
    top: float,
    width: float,
    height: float,
    brand: BrandConfig,
    *,
    font_size: int = 10,
    alignment: PP_ALIGN = PP_ALIGN.CENTER,
    bold: bool = False,
    color: str | None = None,
    font_name: str | None = None,
) -> None:
    """Add a small label textbox."""
    txBox = slide.shapes.add_textbox(
        Inches(left), Inches(top), Inches(width), Inches(height),
    )
    tf = txBox.text_frame
    tf.word_wrap = True
    p = tf.paragraphs[0]
    p.text = text
    p.alignment = alignment
    p.font.size = Pt(font_size)
    p.font.bold = bold
    p.font.name = font_name or brand.fonts.body_font
    p.font.color.rgb = parse_hex_color(color or brand.colors.text_secondary)


# ---------------------------------------------------------------------------
# Visual specimen slide builders
# ---------------------------------------------------------------------------

def _build_color_swatches(slide: object, brand: BrandConfig) -> None:
    """Draw filled rectangles for each brand color with hex labels."""
    _add_title_text(slide, "Color Palette", brand)

    c = brand.colors
    palette = [
        ("Primary", c.primary),
        ("Secondary", c.secondary),
        ("Accent", c.accent),
        ("Background", c.background),
        ("Text Primary", c.text_primary),
        ("Text Secondary", c.text_secondary),
        ("Success", c.success),
        ("Danger", c.danger),
        ("Surface", c.surface),
        ("Border", c.border),
    ]

    cols = 5
    swatch_w = 1.5
    swatch_h = 1.0
    gap = 0.2
    start_x = 0.7
    start_y = 1.3

    for i, (label, hex_val) in enumerate(palette):
        col = i % cols
        row = i // cols
        x = start_x + col * (swatch_w + gap)
        y = start_y + row * (swatch_h + 0.55)

        shape = slide.shapes.add_shape(
            MSO_SHAPE.ROUNDED_RECTANGLE,
            Inches(x), Inches(y), Inches(swatch_w), Inches(swatch_h),
        )
        shape.fill.solid()
        shape.fill.fore_color.rgb = parse_hex_color(hex_val)
        shape.line.fill.background()
        set_corner_radius(shape, brand.shape_style.card_corner_radius)

        _add_label(
            slide, f"{label}\n{hex_val.upper()}",
            x, y + swatch_h + 0.02, swatch_w, 0.45, brand,
            font_size=10,
        )


def _build_typography_specimen(slide: object, brand: BrandConfig) -> None:
    """Show font samples at each size tier."""
    _add_title_text(slide, "Typography", brand)

    f = brand.fonts
    display_font = f.display_font or f.heading_font

    tiers = [
        (f"Display — {display_font}", display_font, f.display_size),
        (f"Title — {f.heading_font}", f.heading_font, f.title_size),
        (f"Heading — {f.heading_font}", f.heading_font, f.heading_size),
        (f"Subheading — {f.heading_font}", f.heading_font, f.subheading_size),
        (f"Body — {f.body_font}", f.body_font, f.body_size),
        (f"Caption — {f.body_font}", f.body_font, f.caption_size),
    ]

    y = 1.3
    for label, font_name, size in tiers:
        # Clamp displayed size to avoid oversized samples overflowing
        display_size = min(size, 32)
        row_h = max(display_size / 72 + 0.15, 0.35)

        txBox = slide.shapes.add_textbox(
            Inches(0.7), Inches(y), Inches(11.933), Inches(row_h),
        )
        tf = txBox.text_frame
        tf.word_wrap = True
        p = tf.paragraphs[0]
        p.text = f"{label} ({size}pt)"
        p.font.size = Pt(display_size)
        p.font.name = font_name
        p.font.color.rgb = parse_hex_color(brand.colors.text_primary)

        y += row_h + 0.08


def _build_spacing_diagram(slide: object, brand: BrandConfig) -> None:
    """Draw annotated rectangles showing spacing values."""
    _add_title_text(slide, "Spacing & Layout", brand)

    s = brand.spacing
    lr = brand.layout_rules

    # Outer "slide frame" rectangle
    frame_x, frame_y = 1.0, 1.5
    frame_w, frame_h = 4.0, 3.2

    frame = slide.shapes.add_shape(
        MSO_SHAPE.RECTANGLE,
        Inches(frame_x), Inches(frame_y), Inches(frame_w), Inches(frame_h),
    )
    frame.fill.solid()
    frame.fill.fore_color.rgb = parse_hex_color(brand.colors.surface)
    frame.line.color.rgb = parse_hex_color(brand.colors.border)
    frame.line.width = Pt(1)

    # Inner "content area" rectangle
    inner_x = frame_x + s.margin_left * 0.5
    inner_y = frame_y + s.margin_top * 0.5
    inner_w = frame_w - (s.margin_left + s.margin_right) * 0.5
    inner_h = frame_h - (s.margin_top + s.margin_bottom) * 0.5

    inner = slide.shapes.add_shape(
        MSO_SHAPE.RECTANGLE,
        Inches(inner_x), Inches(inner_y), Inches(inner_w), Inches(inner_h),
    )
    inner.fill.solid()
    inner.fill.fore_color.rgb = parse_hex_color(brand.colors.background)
    inner.line.color.rgb = parse_hex_color(brand.colors.accent)
    inner.line.width = Pt(0.75)

    # Content area label
    tf = inner.text_frame
    tf.word_wrap = True
    p = tf.paragraphs[0]
    p.text = "Content Area"
    p.alignment = PP_ALIGN.CENTER
    p.font.size = Pt(10)
    p.font.color.rgb = parse_hex_color(brand.colors.text_secondary)
    p.font.name = brand.fonts.body_font

    # Dimension annotations on the right side
    annotations = [
        f"Margins: L={s.margin_left}\"  R={s.margin_right}\"  T={s.margin_top}\"  B={s.margin_bottom}\"",
        f"Content Top: {s.content_top}\"",
        f"Element Gap: {s.element_gap}\"",
        f"Column Gap: {s.column_gap}\"",
        f"Padding: {s.padding}\"",
        "",
        f"Max Bullets: {lr.max_bullets_per_slide}",
        f"Max Words/Bullet: {lr.max_words_per_bullet}",
        f"Max Words/Slide: {lr.max_words_per_slide}",
        f"Min Font: {lr.min_font_size}pt",
    ]

    ann_x = 5.5
    ann_y = 1.5
    for line in annotations:
        if not line:
            ann_y += 0.15
            continue
        _add_label(
            slide, line,
            ann_x, ann_y, 4.0, 0.3, brand,
            font_size=10, alignment=PP_ALIGN.LEFT,
        )
        ann_y += 0.28


def _build_shape_style(slide: object, brand: BrandConfig) -> None:
    """Show rounded rectangles with corner radius, shadows, border."""
    _add_title_text(slide, "Shape Style", brand)

    ss = brand.shape_style
    c = brand.colors

    # Showcase shapes
    shapes_data = [
        ("Default Card", c.surface, c.border, ss.card_corner_radius, ss.card_shadow),
        ("Primary Fill", c.primary, None, ss.card_corner_radius, False),
        ("With Shadow", c.surface, c.border, ss.card_corner_radius, True),
        ("Sharp Corners", c.surface, c.border, 0.0, False),
        ("Pill Shape", c.accent, None, 0.5, False),
    ]

    start_x = 0.7
    y = 1.4
    card_w = 1.7
    card_h = 1.3
    gap = 0.15

    for i, (label, fill, border_clr, radius, shadow) in enumerate(shapes_data):
        x = start_x + i * (card_w + gap)
        shape = slide.shapes.add_shape(
            MSO_SHAPE.ROUNDED_RECTANGLE,
            Inches(x), Inches(y), Inches(card_w), Inches(card_h),
        )
        shape.fill.solid()
        shape.fill.fore_color.rgb = parse_hex_color(fill)
        if border_clr:
            shape.line.color.rgb = parse_hex_color(border_clr)
            shape.line.width = Pt(ss.card_border_width_pt)
        else:
            shape.line.fill.background()

        set_corner_radius(shape, radius)
        if shadow:
            add_drop_shadow(shape)

        # Shape label
        tf = shape.text_frame
        tf.word_wrap = True
        p = tf.paragraphs[0]
        p.text = label
        p.alignment = PP_ALIGN.CENTER
        p.font.size = Pt(10)
        p.font.name = brand.fonts.body_font
        # Use light text on dark fills
        if fill == c.primary or fill == c.accent:
            p.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
        else:
            p.font.color.rgb = parse_hex_color(c.text_primary)

    # Properties summary below the shapes
    props = [
        f"Corner Radius: {ss.card_corner_radius}",
        f"Border Width: {ss.card_border_width_pt}pt",
        f"Shadow Preset: {ss.shadow_preset}",
        f"Accent Bar: {'Yes' if ss.show_accent_bar else 'No'}",
        f"Edge Bar: {ss.edge_bar}",
    ]
    prop_y = y + card_h + 0.4
    for line in props:
        _add_label(
            slide, line,
            0.7, prop_y, 5.0, 0.25, brand,
            font_size=10, alignment=PP_ALIGN.LEFT,
        )
        prop_y += 0.28


def _build_dos_and_donts(slide: object, brand: BrandConfig) -> None:
    """Two columns with green checkmarks and red X marks."""
    _add_title_text(slide, "Do's and Don'ts", brand)

    c = brand.colors

    dos = [
        "Use brand colors consistently",
        "Maintain visual hierarchy",
        "Keep slides clean and focused",
        "Use high-quality imagery",
        "Follow spacing guidelines",
    ]
    donts = [
        "Overload slides with text",
        "Use off-brand colors",
        "Ignore margin boundaries",
        "Mix too many font styles",
        "Stretch or distort images",
    ]

    col_w = 5.7
    left_x = 0.7
    right_x = 6.9
    header_y = 1.3

    # Column headers
    for x, title, color in [
        (left_x, "Do", c.success),
        (right_x, "Don't", c.danger),
    ]:
        txBox = slide.shapes.add_textbox(
            Inches(x), Inches(header_y), Inches(col_w), Inches(0.4),
        )
        tf = txBox.text_frame
        p = tf.paragraphs[0]
        p.text = title
        p.font.size = Pt(18)
        p.font.bold = True
        p.font.name = brand.fonts.heading_font
        p.font.color.rgb = parse_hex_color(color)

    item_y = 1.85
    row_h = 0.35

    for i, text in enumerate(dos):
        _add_label(
            slide, f"\u2713  {text}",
            left_x, item_y + i * row_h, col_w, row_h, brand,
            font_size=12, alignment=PP_ALIGN.LEFT,
            color=c.text_primary,
        )

    for i, text in enumerate(donts):
        _add_label(
            slide, f"\u2717  {text}",
            right_x, item_y + i * row_h, col_w, row_h, brand,
            font_size=12, alignment=PP_ALIGN.LEFT,
            color=c.text_primary,
        )


# ---------------------------------------------------------------------------
# Slide insertion helper
# ---------------------------------------------------------------------------

def _move_slide_to(prs: PptxPresentation, slide: object, target_idx: int) -> None:
    """Move a slide (currently at the end) to target_idx position."""
    slide_id_list = prs.slides._sldIdLst  # type: ignore[attr-defined]
    entries = list(slide_id_list)
    last = entries[-1]
    slide_id_list.remove(last)
    if target_idx >= len(entries) - 1:
        slide_id_list.append(last)
    else:
        slide_id_list.insert(target_idx, last)


# ---------------------------------------------------------------------------
# Main entry point
# ---------------------------------------------------------------------------

def generate_brand_guide(brand: BrandConfig, output_path: Path) -> Path:
    """Generate a brand guide PPTX showcasing the given BrandConfig.

    Creates a 13-slide deck that demonstrates the brand's colors, typography,
    spacing rules, shape styles, sample slide types, and design guidelines.

    Slides 1-5 and 12 use raw python-pptx for visual specimens.
    Slides 6-11 and 13 are definition-based via DeckGenerator.

    Args:
        brand: The BrandConfig to showcase.
        output_path: Where to save the .pptx file.

    Returns:
        The output path.
    """
    # ----- Step 1: Build definition-based slides (8 total) ----- #
    slides: list[SlideDefinition] = []

    # 1. Title slide
    slides.append(SlideDefinition(
        slide_type=SlideType.TITLE,
        title=f"{brand.name} Brand Guide",
        subtitle="Visual identity, typography, and layout reference",
    ))

    # 2. Sample dashboard
    slides.append(SlideDefinition(
        slide_type=SlideType.DASHBOARD,
        title="Sample Dashboard",
        kpis=[
            KpiCard(value="$2.4M", label="Revenue", change="+18%", trend="up"),
            KpiCard(value="94%", label="Retention", change="+3%", trend="up"),
            KpiCard(value="72", label="NPS Score", change="+5", trend="up"),
            KpiCard(value="1.8s", label="Response Time", change="-0.4s", trend="down"),
        ],
    ))

    # 3. Sample card grid
    slides.append(SlideDefinition(
        slide_type=SlideType.CARD_GRID,
        title="Sample Cards",
        cards=[
            CardDefinition(
                title="Strategy",
                icon="\U0001F3AF",
                body="Strategic advisory and planning",
                bullets=["Market analysis", "Growth roadmap"],
            ),
            CardDefinition(
                title="Analytics",
                icon="\U0001F4CA",
                body="Data-driven insights and reporting",
                bullets=["Dashboards", "Predictive models"],
            ),
            CardDefinition(
                title="Delivery",
                icon="\U0001F680",
                body="Hands-on implementation",
                bullets=["Agile methodology", "Change management"],
            ),
            CardDefinition(
                title="Support",
                icon="\U0001F6E1\uFE0F",
                body="Ongoing operational support",
                bullets=["24/7 monitoring", "Incident response"],
            ),
        ],
    ))

    # 4. Sample chart
    slides.append(SlideDefinition(
        slide_type=SlideType.CHART,
        title="Sample Chart",
        elements=[
            ChartElement(
                type="chart",
                chart_type="bar",
                data=ChartData(
                    categories=["Q1", "Q2", "Q3", "Q4"],
                    series=[
                        ChartDataSeries(name="Revenue", values=[120, 145, 160, 195]),
                        ChartDataSeries(name="Costs", values=[80, 85, 90, 100]),
                    ],
                ),
            ),
        ],
    ))

    # 5. Sample table
    slides.append(SlideDefinition(
        slide_type=SlideType.TABLE,
        title="Sample Table",
        elements=[
            TableElement(
                type="table",
                rows=[
                    [TableCell(text="Category"), TableCell(text="Target"), TableCell(text="Actual"), TableCell(text="Status")],
                    [TableCell(text="Revenue"), TableCell(text="$2.0M"), TableCell(text="$2.4M"), TableCell(text="Exceeded")],
                    [TableCell(text="Margin"), TableCell(text="35%"), TableCell(text="38%"), TableCell(text="On Track")],
                    [TableCell(text="Headcount"), TableCell(text="50"), TableCell(text="48"), TableCell(text="Under")],
                    [TableCell(text="NPS"), TableCell(text="65"), TableCell(text="72"), TableCell(text="Exceeded")],
                ],
            ),
        ],
    ))

    # 6. Sample process
    slides.append(SlideDefinition(
        slide_type=SlideType.PROCESS,
        title="Sample Process",
        steps=[
            ProcessStep(title="Discover", description="Gather requirements", status="completed"),
            ProcessStep(title="Design", description="Create architecture", status="active"),
            ProcessStep(title="Build", description="Implement solution", status="future"),
            ProcessStep(title="Deploy", description="Launch to production", status="future"),
        ],
        active_step=1,
    ))

    # 7. Sample two-column
    slides.append(SlideDefinition(
        slide_type=SlideType.TWO_COLUMN,
        title="Sample Two-Column Layout",
        columns=[
            ColumnContent(
                title="Design Principles",
                bullets=["Clear visual hierarchy", "Consistent brand colors", "Professional typography"],
            ),
            ColumnContent(
                title="Layout Guidelines",
                bullets=["Balanced whitespace", "Aligned elements", "Purposeful contrast"],
            ),
        ],
    ))

    # 8. Closing
    hf = brand.header_footer
    ss = brand.shape_style
    closing_contact = [
        f"Slide Numbers: {'Yes' if hf.show_slide_numbers else 'No'}",
        f"Date Footer: {'Yes' if hf.show_date else 'No'}",
        f"Corner Radius: {ss.card_corner_radius}",
        f"Shadows: {'Yes' if ss.card_shadow else 'No'}",
        f"Accent Bar: {'Yes' if ss.show_accent_bar else 'No'}",
    ]
    if hf.confidential_marking:
        closing_contact.append(f"Marking: {hf.confidential_marking}")

    slides.append(SlideDefinition(
        slide_type=SlideType.CLOSING,
        title="Brand Guide Complete",
        subtitle=f"Tone: {brand.tone.value}",
        contact_info=closing_contact,
    ))

    definition = PresentationDefinition(
        metadata=PresentationMetadata(
            title=f"{brand.name} Brand Guide",
            author="pptx-mcp",
            subject="Brand guide",
        ),
        slides=slides,
    )

    generator = DeckGenerator(brand=brand)
    result_path = generator.generate(definition, str(output_path))

    # ----- Step 2: Reopen PPTX and insert visual specimen slides ----- #
    prs = PptxPresentation(str(result_path))

    # Insert visual specimen slides at positions 2-5 (after TITLE, before DASHBOARD)
    # and position 12 (after TWO_COLUMN, before CLOSING)
    # We insert them at the end then move to the correct position.

    specimen_builders = [
        (1, _build_color_swatches),      # insert at index 1 → slide 2
        (2, _build_typography_specimen),  # insert at index 2 → slide 3
        (3, _build_spacing_diagram),      # insert at index 3 → slide 4
        (4, _build_shape_style),          # insert at index 4 → slide 5
    ]

    for target_idx, builder in specimen_builders:
        new_slide = _add_blank_slide(prs)
        builder(new_slide, brand)
        _move_slide_to(prs, new_slide, target_idx)

    # Do's and Don'ts goes at index 11 (slide 12, before the closing slide)
    dos_slide = _add_blank_slide(prs)
    _build_dos_and_donts(dos_slide, brand)
    _move_slide_to(prs, dos_slide, 11)

    prs.save(str(result_path))
    return result_path
