"""Review engine — evaluates presentations against design rules."""

from __future__ import annotations

from pathlib import Path
from typing import Optional

from pptx_mcp.brand.config import resolve_brand_config
from pptx_mcp.engine.parser import parse_presentation
from pptx_mcp.models.brand import BrandConfig
from pptx_mcp.models.review import (
    ReviewIssue,
    ReviewResult,
    Severity,
    SlideReview,
)
from pptx_mcp.models.slides import PresentationDefinition
from pptx_mcp.review.rules import DECK_RULES, SLIDE_RULES, _gather_text
from pptx_mcp.utils.logging import get_logger

logger = get_logger("review.engine")


class ReviewEngine:
    """Evaluates presentations against design and content rules."""

    def __init__(self, brand: Optional[BrandConfig] = None) -> None:
        self.brand = brand or resolve_brand_config()

    def review_file(self, path: str | Path) -> ReviewResult:
        """Review a PPTX file.

        Args:
            path: Path to the .pptx file.

        Returns:
            ReviewResult with issues and scores.
        """
        definition = parse_presentation(path)
        return self.review_definition(definition)

    def review_definition(self, definition: PresentationDefinition) -> ReviewResult:
        """Review a PresentationDefinition.

        Args:
            definition: The parsed presentation.

        Returns:
            ReviewResult with issues and scores.
        """
        all_issues: list[ReviewIssue] = []
        slide_reviews: list[SlideReview] = []

        # Per-slide rules
        for i, slide in enumerate(definition.slides):
            slide_issues: list[ReviewIssue] = []
            for rule in SLIDE_RULES:
                slide_issues.extend(rule(slide, i, self.brand))

            text = _gather_text(slide)
            word_count = len(text.split())
            element_count = len(slide.elements) + len(slide.bullets) + len(slide.columns)

            score = self._score_slide(slide_issues)
            slide_reviews.append(SlideReview(
                slide_index=i,
                slide_type=slide.slide_type.value,
                title=slide.title,
                word_count=word_count,
                element_count=element_count,
                issues=slide_issues,
                score=score,
            ))
            all_issues.extend(slide_issues)

        # Deck-level rules
        for rule in DECK_RULES:
            all_issues.extend(rule(definition.slides, self.brand))

        overall_score = self._compute_overall_score(slide_reviews, all_issues)
        summary = self._generate_summary(all_issues, slide_reviews, overall_score)
        strengths = self._identify_strengths(definition, slide_reviews)
        improvements = self._identify_improvements(all_issues)

        return ReviewResult(
            overall_score=overall_score,
            total_slides=len(definition.slides),
            total_issues=len(all_issues),
            issues=all_issues,
            slide_reviews=slide_reviews,
            summary=summary,
            strengths=strengths,
            improvements=improvements,
        )

    def _score_slide(self, issues: list[ReviewIssue]) -> float:
        """Score a single slide based on its issues."""
        score = 10.0
        for issue in issues:
            if issue.severity == Severity.ERROR:
                score -= 3.0
            elif issue.severity == Severity.WARNING:
                score -= 1.5
            elif issue.severity == Severity.INFO:
                score -= 0.5
            elif issue.severity == Severity.SUGGESTION:
                score -= 0.25
        return max(0.0, min(10.0, score))

    def _compute_overall_score(
        self,
        slide_reviews: list[SlideReview],
        all_issues: list[ReviewIssue],
    ) -> float:
        """Compute the overall deck score."""
        if not slide_reviews:
            return 0.0

        slide_avg = sum(sr.score for sr in slide_reviews) / len(slide_reviews)

        # Penalize deck-level issues
        deck_issues = [i for i in all_issues if i.slide_index is None]
        deck_penalty = 0.0
        for issue in deck_issues:
            if issue.severity == Severity.ERROR:
                deck_penalty += 2.0
            elif issue.severity == Severity.WARNING:
                deck_penalty += 1.0
            elif issue.severity == Severity.INFO:
                deck_penalty += 0.3

        return max(0.0, min(10.0, round(slide_avg - deck_penalty, 1)))

    def _generate_summary(
        self,
        issues: list[ReviewIssue],
        slide_reviews: list[SlideReview],
        score: float,
    ) -> str:
        """Generate a human-readable review summary."""
        error_count = sum(1 for i in issues if i.severity == Severity.ERROR)
        warning_count = sum(1 for i in issues if i.severity == Severity.WARNING)
        info_count = sum(1 for i in issues if i.severity in (Severity.INFO, Severity.SUGGESTION))

        parts: list[str] = []
        parts.append(f"Overall score: {score}/10.0 across {len(slide_reviews)} slides.")

        if error_count:
            parts.append(f"{error_count} error(s) need immediate attention.")
        if warning_count:
            parts.append(f"{warning_count} warning(s) should be addressed.")
        if info_count:
            parts.append(f"{info_count} suggestion(s) for improvement.")

        if score >= 8.0:
            parts.append("The deck is in good shape overall.")
        elif score >= 5.0:
            parts.append("The deck needs some refinement.")
        else:
            parts.append("The deck requires significant rework.")

        return " ".join(parts)

    def _identify_strengths(
        self,
        definition: PresentationDefinition,
        slide_reviews: list[SlideReview],
    ) -> list[str]:
        """Identify positive aspects of the deck."""
        strengths: list[str] = []

        types_used = {s.slide_type for s in definition.slides}
        if len(types_used) >= 3:
            strengths.append("Good variety of slide types used.")

        high_scoring = [sr for sr in slide_reviews if sr.score >= 9.0]
        if len(high_scoring) > len(slide_reviews) * 0.5:
            strengths.append("Majority of slides score well on design rules.")

        has_titles = all(s.title for s in definition.slides if s.slide_type.value != "blank")
        if has_titles:
            strengths.append("All slides have clear titles.")

        return strengths

    def _identify_improvements(self, issues: list[ReviewIssue]) -> list[str]:
        """Identify top improvement areas."""
        category_counts: dict[str, int] = {}
        for issue in issues:
            key = issue.category.value
            category_counts[key] = category_counts.get(key, 0) + 1

        sorted_categories = sorted(category_counts.items(), key=lambda x: x[1], reverse=True)

        improvements: list[str] = []
        for category, count in sorted_categories[:3]:
            improvements.append(f"Address {count} {category} issue(s).")

        return improvements
