"""Tests for parse_brand_guide — markdown/YAML → BrandConfig."""

from __future__ import annotations

import json
from pathlib import Path

import pytest

from pptx_mcp.engine.spec_parser import parse_brand_guide
from pptx_mcp.models.brand import BrandConfig, ToneStyle


# ---------------------------------------------------------------------------
# Markdown parsing
# ---------------------------------------------------------------------------


FULL_MARKDOWN = """\
# Acme Corp Brand

## Colors
- Primary: #1B365D
- Secondary: #2E86AB
- Accent: #E8630A
- Background: #FFFFFF
- Text Primary: #1A1A2E
- Surface: #F0F0F0
- Border: #CCCCCC
- Success: #28A745
- Danger: #DC3545

## Typography
- Heading font: Aptos
- Body font: Calibri
- Mono font: Consolas
- Title size: 36pt
- Heading size: 28pt
- Body size: 16pt
- Caption size: 12pt
- Line spacing: 1.2
- Paragraph spacing: 6pt
- Display font: Georgia
- Display size: 44pt
- Heading weight: Bold
- Body weight: Regular

## Shape Style
- Corner radius: 0.08
- Card shadow: true
- Shadow blur: 4pt
- Shadow offset: 3pt
- Shadow opacity: 25
- Border width: 0.75
- Accent bar: true
- Shadow preset: subtle
- Edge bar: left
- Edge bar width: 0.15
- Divider style: thin_line
- Line dash style: solid
- Text auto fit: false

## Spacing
- Margin left: 0.7
- Margin right: 0.7
- Margin top: 0.8
- Margin bottom: 0.5
- Element gap: 0.3
- Column gap: 0.4
- Padding: 0.2
- Base unit: 8pt

## Header/Footer
- Slide numbers: true
- Show date: true
- Date text: Q4 2025
- Company name: true
- Confidential: CONFIDENTIAL
- Skip title slide: true

## Logo
- Path: /images/logo.png
- Placement: top_right
- Max height: 0.6
- Skip on: title, closing

## Tone
- Style: consulting
"""


def test_full_markdown_colors():
    brand = parse_brand_guide(FULL_MARKDOWN)
    assert brand.colors.primary == "#1B365D"
    assert brand.colors.secondary == "#2E86AB"
    assert brand.colors.accent == "#E8630A"
    assert brand.colors.background == "#FFFFFF"
    assert brand.colors.text_primary == "#1A1A2E"
    assert brand.colors.surface == "#F0F0F0"
    assert brand.colors.border == "#CCCCCC"


def test_full_markdown_name():
    brand = parse_brand_guide(FULL_MARKDOWN)
    assert brand.name == "Acme Corp Brand"


def test_full_markdown_typography():
    brand = parse_brand_guide(FULL_MARKDOWN)
    assert brand.fonts.heading_font == "Aptos"
    assert brand.fonts.body_font == "Calibri"
    assert brand.fonts.mono_font == "Consolas"
    assert brand.fonts.title_size == 36
    assert brand.fonts.heading_size == 28
    assert brand.fonts.body_size == 16
    assert brand.fonts.caption_size == 12
    assert brand.fonts.line_spacing == 1.2
    assert brand.fonts.display_font == "Georgia"
    assert brand.fonts.display_size == 44
    assert brand.fonts.font_weight_heading == "Bold"
    assert brand.fonts.font_weight_body == "Regular"


def test_full_markdown_shape_style():
    brand = parse_brand_guide(FULL_MARKDOWN)
    assert brand.shape_style.card_corner_radius == 0.08
    assert brand.shape_style.card_shadow is True
    assert brand.shape_style.shadow_blur_pt == 4.0
    assert brand.shape_style.shadow_offset_pt == 3.0
    assert brand.shape_style.shadow_opacity_pct == 25
    assert brand.shape_style.card_border_width_pt == 0.75
    assert brand.shape_style.show_accent_bar is True
    assert brand.shape_style.shadow_preset == "subtle"
    assert brand.shape_style.edge_bar == "left"
    assert brand.shape_style.edge_bar_width == 0.15
    assert brand.shape_style.divider_style == "thin_line"
    assert brand.shape_style.line_dash_style == "solid"
    assert brand.shape_style.text_auto_fit is False


def test_full_markdown_spacing():
    brand = parse_brand_guide(FULL_MARKDOWN)
    assert brand.spacing.margin_left == 0.7
    assert brand.spacing.margin_right == 0.7
    assert brand.spacing.margin_top == 0.8
    assert brand.spacing.margin_bottom == 0.5
    assert brand.spacing.element_gap == 0.3
    assert brand.spacing.column_gap == 0.4
    assert brand.spacing.padding == 0.2
    assert brand.spacing.base_unit_pt == 8


def test_full_markdown_header_footer():
    brand = parse_brand_guide(FULL_MARKDOWN)
    assert brand.header_footer.show_slide_numbers is True
    assert brand.header_footer.show_date is True
    assert brand.header_footer.date_text == "Q4 2025"
    assert brand.header_footer.show_company_name is True
    assert brand.header_footer.confidential_marking == "CONFIDENTIAL"
    assert brand.header_footer.skip_title_slide is True


def test_full_markdown_logo():
    brand = parse_brand_guide(FULL_MARKDOWN)
    assert brand.logo.path == "/images/logo.png"
    assert brand.logo.placement == "top_right"
    assert brand.logo.max_height == 0.6
    assert brand.logo.skip_on == ["title", "closing"]


def test_full_markdown_tone():
    brand = parse_brand_guide(FULL_MARKDOWN)
    assert brand.tone == ToneStyle.EXECUTIVE  # "consulting" is not a ToneStyle — falls back

    # Test with a valid tone
    md = "# Brand\n\n## Tone\n- Style: creative\n\n## Colors\n- Primary: #000000\n- Secondary: #111111\n- Accent: #222222\n"
    brand2 = parse_brand_guide(md)
    assert brand2.tone == ToneStyle.CREATIVE


# ---------------------------------------------------------------------------
# YAML auto-detection
# ---------------------------------------------------------------------------


YAML_INPUT = """\
name: YAML Brand
colors:
  primary: "#FF0000"
  secondary: "#00FF00"
  accent: "#0000FF"
fonts:
  heading_font: Arial
  body_font: Helvetica
tone: minimal
"""


def test_yaml_autodetection():
    brand = parse_brand_guide(YAML_INPUT)
    assert brand.name == "YAML Brand"
    assert brand.colors.primary == "#FF0000"
    assert brand.colors.secondary == "#00FF00"
    assert brand.colors.accent == "#0000FF"
    assert brand.fonts.heading_font == "Arial"
    assert brand.fonts.body_font == "Helvetica"
    assert brand.tone == ToneStyle.MINIMAL


def test_yaml_invalid_not_mapping():
    with pytest.raises(ValueError, match="mapping"):
        parse_brand_guide("- item1\n- item2\n")


# ---------------------------------------------------------------------------
# Edge cases
# ---------------------------------------------------------------------------


def test_missing_sections_use_defaults():
    md = "# Minimal Brand\n\n## Colors\n- Primary: #123456\n- Secondary: #654321\n- Accent: #ABCDEF\n"
    brand = parse_brand_guide(md)
    assert brand.name == "Minimal Brand"
    assert brand.colors.primary == "#123456"
    # Fonts should be defaults
    assert brand.fonts.heading_font == "Calibri"
    assert brand.fonts.body_font == "Calibri"
    # Spacing defaults
    assert brand.spacing.margin_left == 0.7
    # Shape style defaults
    assert brand.shape_style.card_corner_radius == 0.08


def test_no_colors_section_gets_fallback():
    md = "# Empty Brand\n\n## Typography\n- Heading font: Arial\n"
    brand = parse_brand_guide(md)
    # Should get fallback colors
    assert brand.colors.primary == "#1B365D"
    assert brand.fonts.heading_font == "Arial"


def test_custom_tokens():
    md = """\
# Token Brand

## Colors
- Primary: #111111
- Secondary: #222222
- Accent: #333333
- Brand Blue: #4488CC
- Warm Gray: #998877
"""
    brand = parse_brand_guide(md)
    assert brand.colors.primary == "#111111"
    # Extra colors go to custom_tokens
    assert len(brand.colors.custom_tokens) >= 1


def test_chart_colors():
    md = """\
# Chart Brand

## Colors
- Primary: #111111
- Secondary: #222222
- Accent: #333333
- Chart colors: #FF0000 #00FF00 #0000FF
"""
    brand = parse_brand_guide(md)
    assert brand.colors.chart_colors == ["#FF0000", "#00FF00", "#0000FF"]


def test_color_token_regex_variants():
    """Color tokens can use backticks, bold, or plain text."""
    md = """\
# Regex Brand

## Colors
- `primary`: `#AABBCC`
- **secondary** - #DDEEFF
- accent: #112233
"""
    brand = parse_brand_guide(md)
    assert brand.colors.primary == "#AABBCC"
    assert brand.colors.secondary == "#DDEEFF"
    assert brand.colors.accent == "#112233"


# ---------------------------------------------------------------------------
# MCP tool integration (import-level test)
# ---------------------------------------------------------------------------


def test_mcp_tool_returns_json():
    """The server-level parse_brand_guide tool should return valid JSON."""
    from pptx_mcp.server import parse_brand_guide as tool_fn

    result = tool_fn(markdown=FULL_MARKDOWN)
    data = json.loads(result)
    assert data["name"] == "Acme Corp Brand"
    assert data["colors"]["primary"] == "#1B365D"


def test_mcp_tool_save_json(tmp_path: Path):
    """parse_brand_guide with save_path=.json should write a file."""
    from pptx_mcp.server import parse_brand_guide as tool_fn

    save = str(tmp_path / "brand.json")
    result = tool_fn(markdown=FULL_MARKDOWN, save_path=save)
    data = json.loads(result)
    assert data["status"] == "success"
    assert Path(save).exists()
    saved = json.loads(Path(save).read_text())
    assert saved["name"] == "Acme Corp Brand"


def test_mcp_tool_save_yaml(tmp_path: Path):
    """parse_brand_guide with save_path=.yaml should write a file."""
    import yaml

    from pptx_mcp.server import parse_brand_guide as tool_fn

    save = str(tmp_path / "brand.yaml")
    result = tool_fn(markdown=FULL_MARKDOWN, save_path=save)
    data = json.loads(result)
    assert data["status"] == "success"
    assert Path(save).exists()
    saved = yaml.safe_load(Path(save).read_text())
    assert saved["name"] == "Acme Corp Brand"


def test_mcp_tool_save_invalid_extension():
    """save_path with wrong extension should raise."""
    from pptx_mcp.server import parse_brand_guide as tool_fn
    from mcp.server.fastmcp.exceptions import ToolError

    with pytest.raises(ToolError, match="(?i)save path must end"):
        tool_fn(markdown=FULL_MARKDOWN, save_path="/tmp/brand.txt")
