"""Design rules for presentation review."""

from __future__ import annotations

from pptx_mcp.models.brand import BrandConfig
from pptx_mcp.models.review import ReviewCategory, ReviewIssue, Severity
from pptx_mcp.models.slides import SlideDefinition, SlideType


def check_word_count(
    slide: SlideDefinition,
    slide_index: int,
    brand: BrandConfig,
) -> list[ReviewIssue]:
    """Check that slides don't have too many words."""
    issues: list[ReviewIssue] = []
    all_text = _gather_text(slide)
    word_count = len(all_text.split())

    max_words = brand.layout_rules.max_words_per_slide
    if word_count > max_words:
        issues.append(ReviewIssue(
            severity=Severity.WARNING,
            category=ReviewCategory.CONTENT,
            message=f"Slide has {word_count} words (max {max_words}). Consider reducing text.",
            slide_index=slide_index,
            suggestion="Break content into multiple slides or use visuals instead of text.",
        ))

    if word_count > max_words * 1.5:
        issues[-1].severity = Severity.ERROR

    return issues


def check_bullet_count(
    slide: SlideDefinition,
    slide_index: int,
    brand: BrandConfig,
) -> list[ReviewIssue]:
    """Check that slides don't have too many bullets."""
    issues: list[ReviewIssue] = []
    max_bullets = brand.layout_rules.max_bullets_per_slide

    if len(slide.bullets) > max_bullets:
        issues.append(ReviewIssue(
            severity=Severity.WARNING,
            category=ReviewCategory.CONTENT,
            message=(
                f"Slide has {len(slide.bullets)} bullets (max {max_bullets}). "
                "Audiences lose focus with too many points."
            ),
            slide_index=slide_index,
            suggestion="Group related points or split across slides.",
        ))

    for i, bullet in enumerate(slide.bullets):
        words = len(bullet.split())
        max_bw = brand.layout_rules.max_words_per_bullet
        if words > max_bw:
            issues.append(ReviewIssue(
                severity=Severity.INFO,
                category=ReviewCategory.CONTENT,
                message=f"Bullet {i + 1} has {words} words (max {max_bw}).",
                slide_index=slide_index,
                element_description=f'Bullet: "{bullet[:50]}..."' if len(bullet) > 50 else f'Bullet: "{bullet}"',
                suggestion="Shorten to a concise phrase.",
            ))

    return issues


def check_title_presence(
    slide: SlideDefinition,
    slide_index: int,
    brand: BrandConfig,
) -> list[ReviewIssue]:
    """Check that each slide has a title."""
    if brand.layout_rules.title_every_slide and not slide.title:
        if slide.slide_type not in (SlideType.BLANK,):
            return [ReviewIssue(
                severity=Severity.WARNING,
                category=ReviewCategory.LAYOUT,
                message="Slide is missing a title.",
                slide_index=slide_index,
                suggestion="Add a descriptive title to guide the audience.",
            )]
    return []


def check_empty_slide(
    slide: SlideDefinition,
    slide_index: int,
    brand: BrandConfig,
) -> list[ReviewIssue]:
    """Check for slides with no content."""
    all_text = _gather_text(slide)
    if not all_text.strip() and not slide.elements and slide.slide_type != SlideType.BLANK:
        return [ReviewIssue(
            severity=Severity.ERROR,
            category=ReviewCategory.CONTENT,
            message="Slide appears to be empty.",
            slide_index=slide_index,
            suggestion="Add content or remove this slide.",
        )]
    return []


def check_column_balance(
    slide: SlideDefinition,
    slide_index: int,
    brand: BrandConfig,
) -> list[ReviewIssue]:
    """Check that multi-column slides have balanced content."""
    issues: list[ReviewIssue] = []

    if slide.slide_type in (SlideType.TWO_COLUMN, SlideType.COMPARISON) and slide.columns:
        lengths = [len(col.bullets) for col in slide.columns]
        if lengths and max(lengths) > 0:
            ratio = min(lengths) / max(lengths) if max(lengths) > 0 else 1.0
            if ratio < 0.3:
                issues.append(ReviewIssue(
                    severity=Severity.INFO,
                    category=ReviewCategory.WHITESPACE,
                    message="Column content is significantly unbalanced.",
                    slide_index=slide_index,
                    suggestion="Try to balance the amount of content across columns.",
                ))

    if slide.slide_type == SlideType.PROS_CONS:
        pro_count = len(slide.pros)
        con_count = len(slide.cons)
        if pro_count > 0 and con_count > 0:
            ratio = min(pro_count, con_count) / max(pro_count, con_count)
            if ratio < 0.3:
                issues.append(ReviewIssue(
                    severity=Severity.INFO,
                    category=ReviewCategory.WHITESPACE,
                    message=(
                        f"Pros ({pro_count}) and Cons ({con_count}) are significantly unbalanced."
                    ),
                    slide_index=slide_index,
                    suggestion="Consider balancing or using a different layout.",
                ))

    return issues


def check_narrative_flow(
    slides: list[SlideDefinition],
    brand: BrandConfig,
) -> list[ReviewIssue]:
    """Check deck-level narrative flow."""
    issues: list[ReviewIssue] = []

    if not slides:
        return [ReviewIssue(
            severity=Severity.ERROR,
            category=ReviewCategory.NARRATIVE,
            message="Presentation has no slides.",
            suggestion="Add slides to create a presentation.",
        )]

    # First slide should be a title
    if slides[0].slide_type != SlideType.TITLE:
        issues.append(ReviewIssue(
            severity=Severity.SUGGESTION,
            category=ReviewCategory.NARRATIVE,
            message="Presentation does not start with a title slide.",
            slide_index=0,
            suggestion="Start with a title slide to set context.",
        ))

    # Check for consecutive identical types (monotony)
    for i in range(1, len(slides)):
        if (
            slides[i].slide_type == slides[i - 1].slide_type
            and slides[i].slide_type == SlideType.CONTENT
        ):
            if i >= 2 and slides[i - 2].slide_type == SlideType.CONTENT:
                issues.append(ReviewIssue(
                    severity=Severity.INFO,
                    category=ReviewCategory.NARRATIVE,
                    message=f"Three consecutive content slides (slides {i - 1}-{i + 1}). Consider varying layout.",
                    slide_index=i,
                    suggestion="Insert a visual, chart, or section break for variety.",
                ))

    # Deck length
    if len(slides) > 25:
        issues.append(ReviewIssue(
            severity=Severity.WARNING,
            category=ReviewCategory.NARRATIVE,
            message=f"Presentation has {len(slides)} slides. Executive decks work best under 20.",
            suggestion="Consider moving detailed content to an appendix.",
        ))

    return issues


def check_consistency(
    slides: list[SlideDefinition],
    brand: BrandConfig,
) -> list[ReviewIssue]:
    """Check for cross-slide consistency issues."""
    issues: list[ReviewIssue] = []

    # Collect title patterns
    titles = [s.title for s in slides if s.title]
    if titles:
        cases = set()
        for t in titles:
            if t == t.upper():
                cases.add("upper")
            elif t == t.title():
                cases.add("title")
            else:
                cases.add("mixed")
        if len(cases) > 1:
            issues.append(ReviewIssue(
                severity=Severity.INFO,
                category=ReviewCategory.CONSISTENCY,
                message=f"Inconsistent title casing detected: {', '.join(sorted(cases))}.",
                suggestion="Use consistent title casing throughout the deck.",
            ))

    return issues


def _gather_text(slide: SlideDefinition) -> str:
    """Gather all text content from a slide definition."""
    parts: list[str] = []
    if slide.title:
        parts.append(slide.title)
    if slide.subtitle:
        parts.append(slide.subtitle)
    if slide.body:
        parts.append(slide.body)
    parts.extend(slide.bullets)
    parts.extend(slide.pros)
    parts.extend(slide.cons)
    for col in slide.columns:
        if col.title:
            parts.append(col.title)
        parts.extend(col.bullets)
    return " ".join(parts)


# Registry of all per-slide rule functions
SLIDE_RULES = [
    check_word_count,
    check_bullet_count,
    check_title_presence,
    check_empty_slide,
    check_column_balance,
]

# Registry of all deck-level rule functions
DECK_RULES = [
    check_narrative_flow,
    check_consistency,
]
