"""Markdown report generation for evaluation results.

This module generates human-readable Markdown reports from evaluation
and comparison results, suitable for documentation and review.
"""

from datetime import datetime
from pathlib import Path

from embergraphrouter.evals.runners.base import (
    AggregateMetrics,
    EvaluationResult,
    SliceMetrics,
)
from embergraphrouter.evals.runners.comparison import (
    ComparisonResult,
    PairedTest,
)


def _format_metric(metrics: AggregateMetrics, precision: int = 3) -> str:
    """Format metric with confidence interval."""
    if metrics.ci_lower is not None and metrics.ci_upper is not None:
        lower = f"{metrics.ci_lower:.{precision}f}"
        upper = f"{metrics.ci_upper:.{precision}f}"
        return f"{metrics.mean:.{precision}f} [{lower}, {upper}]"
    return f"{metrics.mean:.{precision}f}"


def _format_pct(value: float) -> str:
    """Format as percentage."""
    return f"{value * 100:.1f}%"


def _significance_marker(p_value: float) -> str:
    """Return significance marker based on p-value."""
    if p_value < 0.001:
        return "***"
    elif p_value < 0.01:
        return "**"
    elif p_value < 0.05:
        return "*"
    return ""


class EvaluationReport:
    """Generate Markdown report for a single evaluation.

    Example:
        result = await run_evaluation(strategy, benchmark)
        report = EvaluationReport(result)
        report.save("results/report.md")
    """

    def __init__(self, result: EvaluationResult):
        """Initialize with evaluation result.

        Args:
            result: EvaluationResult to report on.
        """
        self._result = result

    def generate(self) -> str:
        """Generate complete Markdown report."""
        sections = [
            self._header(),
            self._summary_section(),
            self._quality_section(),
            self._efficiency_section(),
            self._routing_section(),
            self._difficulty_breakdown(),
            self._domain_breakdown(),
            self._tier_breakdown(),
            self._footer(),
        ]
        return "\n\n".join(s for s in sections if s)

    def save(self, path: Path | str) -> None:
        """Save report to file."""
        path = Path(path)
        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_text(self.generate())

    def _header(self) -> str:
        """Generate report header."""
        return f"""# Evaluation Report

**Strategy:** {self._result.strategy_name}
**Benchmark:** {self._result.benchmark_name}
**Examples:** {self._result.n_examples}
**Run ID:** {self._result.run_id}
**Timestamp:** {self._result.timestamp}"""

    def _summary_section(self) -> str:
        """Generate executive summary."""
        quality = self._result.quality
        efficiency = self._result.efficiency

        acc = quality.get("accuracy", AggregateMetrics(mean=0.0))
        cost = efficiency.get("cost_usd", AggregateMetrics(mean=0.0))
        latency = efficiency.get("latency_ms", AggregateMetrics(mean=0.0))

        return f"""## Summary

| Metric | Value |
|--------|-------|
| Accuracy | {_format_metric(acc)} |
| Mean Cost | ${cost.mean:.4f} |
| Mean Latency | {latency.mean:.0f} ms |"""

    def _quality_section(self) -> str:
        """Generate quality metrics section."""
        quality = self._result.quality
        if not quality:
            return ""

        lines = [
            "## Quality Metrics",
            "",
            "| Metric | Mean | 95% CI |",
            "|--------|------|--------|",
        ]

        for name, metrics in sorted(quality.items()):
            ci = f"[{metrics.ci_lower:.3f}, {metrics.ci_upper:.3f}]" if metrics.ci_lower else "-"
            lines.append(f"| {name} | {metrics.mean:.3f} | {ci} |")

        return "\n".join(lines)

    def _efficiency_section(self) -> str:
        """Generate efficiency metrics section."""
        efficiency = self._result.efficiency
        if not efficiency:
            return ""

        lines = [
            "## Efficiency Metrics",
            "",
            "| Metric | Mean | Std |",
            "|--------|------|-----|",
        ]

        for name, metrics in sorted(efficiency.items()):
            std = f"{metrics.std:.3f}" if metrics.std else "-"
            if "cost" in name.lower():
                lines.append(f"| {name} | ${metrics.mean:.4f} | {std} |")
            elif "latency" in name.lower() or "ms" in name.lower():
                lines.append(f"| {name} | {metrics.mean:.0f} ms | {std} |")
            else:
                lines.append(f"| {name} | {metrics.mean:.1f} | {std} |")

        return "\n".join(lines)

    def _routing_section(self) -> str:
        """Generate routing-specific metrics."""
        routing = self._result.routing
        if not routing:
            return ""

        lines = ["## Routing Analysis", ""]

        # Tier distribution
        tier_dist = routing.get("tier_distribution", {})
        if tier_dist:
            lines.append("### Tier Distribution")
            lines.append("")
            lines.append("| Tier | Percentage |")
            lines.append("|------|------------|")
            for tier, pct in sorted(tier_dist.items()):
                lines.append(f"| {tier} | {_format_pct(pct)} |")
            lines.append("")

        # Model distribution
        model_dist = routing.get("model_distribution", {})
        if model_dist:
            lines.append("### Model Distribution")
            lines.append("")
            lines.append("| Model | Percentage |")
            lines.append("|-------|------------|")
            for model, pct in sorted(model_dist.items(), key=lambda x: -x[1]):
                lines.append(f"| {model} | {_format_pct(pct)} |")
            lines.append("")

        # Fraction of oracle
        frac_oracle = routing.get("frac_of_oracle")
        if frac_oracle:
            lines.append(f"**Fraction of Oracle:** {_format_pct(frac_oracle)}")

        return "\n".join(lines)

    def _difficulty_breakdown(self) -> str:
        """Generate breakdown by difficulty."""
        by_diff = self._result.by_difficulty
        if not by_diff:
            return ""

        return self._slice_table(by_diff, "Difficulty", "Performance by Difficulty")

    def _domain_breakdown(self) -> str:
        """Generate breakdown by domain."""
        by_domain = self._result.by_domain
        if not by_domain:
            return ""

        return self._slice_table(by_domain, "Domain", "Performance by Domain")

    def _tier_breakdown(self) -> str:
        """Generate breakdown by routing tier."""
        by_tier = self._result.by_tier
        if not by_tier:
            return ""

        return self._slice_table(by_tier, "Tier", "Performance by Routing Tier")

    def _slice_table(
        self,
        slices: dict[str, SliceMetrics],
        dim_name: str,
        title: str,
    ) -> str:
        """Generate table for a slicing dimension."""
        lines = [
            f"## {title}",
            "",
            f"| {dim_name} | N | Accuracy | Avg Cost | Avg Latency |",
            "|" + "-" * (len(dim_name) + 2) + "|-----|----------|----------|-------------|",
        ]

        for slice_val, metrics in sorted(slices.items()):
            n = metrics.n_examples
            acc = metrics.quality.get("accuracy", AggregateMetrics(mean=0.0))
            cost = metrics.efficiency.get("cost_usd", AggregateMetrics(mean=0.0))
            latency = metrics.efficiency.get("latency_ms", AggregateMetrics(mean=0.0))

            lines.append(
                f"| {slice_val} | {n} | {acc.mean:.3f} | ${cost.mean:.4f} | {latency.mean:.0f} ms |"
            )

        return "\n".join(lines)

    def _footer(self) -> str:
        """Generate report footer."""
        errors = self._result.errors
        if errors:
            error_lines = [
                "## Errors",
                "",
                f"**Total Errors:** {len(errors)}",
                "",
                "```",
            ]
            for err in errors[:10]:  # Show first 10
                error_lines.append(err)
            if len(errors) > 10:
                error_lines.append(f"... and {len(errors) - 10} more")
            error_lines.append("```")
            return "\n".join(error_lines)

        return ""


class ComparisonReport:
    """Generate Markdown report comparing multiple strategies.

    Example:
        result = await run_comparison(strategies, benchmark)
        report = ComparisonReport(result)
        print(report.generate())
    """

    def __init__(self, result: ComparisonResult):
        """Initialize with comparison result.

        Args:
            result: ComparisonResult to report on.
        """
        self._result = result

    def generate(self) -> str:
        """Generate complete Markdown report."""
        sections = [
            self._header(),
            self._summary_section(),
            self._comparison_table(),
            self._statistical_tests_section(),
            self._per_strategy_details(),
            self._footer(),
        ]
        return "\n\n".join(s for s in sections if s)

    def save(self, path: Path | str) -> None:
        """Save report to file."""
        path = Path(path)
        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_text(self.generate())

    def _header(self) -> str:
        """Generate report header."""
        strategies = ", ".join(self._result.strategy_names)
        return f"""# Strategy Comparison Report

**Benchmark:** {self._result.benchmark_name}
**Strategies:** {strategies}
**Examples:** {self._result.n_examples}
**Generated:** {datetime.now().isoformat()}"""

    def _summary_section(self) -> str:
        """Generate executive summary."""
        summary = self._result.summary
        best = summary.get("best_strategy", "N/A")
        n_sig = summary.get("n_significant_differences", 0)

        lines = [
            "## Summary",
            "",
            f"**Best Strategy:** {best}",
            f"**Significant Differences Found:** {n_sig}",
        ]

        # Add ranking
        ranking = summary.get("accuracy_ranking", [])
        if ranking:
            lines.append("")
            lines.append("### Accuracy Ranking")
            lines.append("")
            for i, r in enumerate(ranking, 1):
                lines.append(f"{i}. **{r['strategy']}**: {r['accuracy']:.3f}")

        return "\n".join(lines)

    def _comparison_table(self) -> str:
        """Generate main comparison table."""
        lines = [
            "## Comparison Table",
            "",
            "| Strategy | Accuracy | Cost | Latency | Tokens Out |",
            "|----------|----------|------|---------|------------|",
        ]

        for name, result in self._result.strategy_results.items():
            acc = result.quality.get("accuracy", AggregateMetrics(mean=0.0))
            cost = result.efficiency.get("cost_usd", AggregateMetrics(mean=0.0))
            latency = result.efficiency.get("latency_ms", AggregateMetrics(mean=0.0))
            tokens = result.efficiency.get("tokens_out", AggregateMetrics(mean=0.0))

            lines.append(
                f"| {name} | {_format_metric(acc)} | "
                f"${cost.mean:.4f} | {latency.mean:.0f} ms | {tokens.mean:.0f} |"
            )

        return "\n".join(lines)

    def _statistical_tests_section(self) -> str:
        """Generate statistical tests section."""
        tests = self._result.paired_tests
        if not tests:
            return ""

        # Group by metric
        by_metric: dict[str, list[PairedTest]] = {}
        for t in tests:
            by_metric.setdefault(t.metric, []).append(t)

        sections = ["## Statistical Tests"]

        for metric, metric_tests in sorted(by_metric.items()):
            sections.append(f"\n### {metric.replace('_', ' ').title()}")
            sections.append("")
            sections.append("| Comparison | Diff | p-value | Sig |")
            sections.append("|------------|------|---------|-----|")

            for t in metric_tests:
                sig = _significance_marker(t.p_value)
                if "cost" in metric or "latency" in metric:
                    diff_str = f"{t.mean_diff:+.4f}" if "cost" in metric else f"{t.mean_diff:+.0f}"
                else:
                    diff_str = f"{t.mean_diff:+.3f}"

                sections.append(
                    f"| {t.strategy_a} vs {t.strategy_b} | "
                    f"{diff_str} | {t.p_value:.4f} | {sig} |"
                )

        sections.append("")
        sections.append("*Significance: \\* p<0.05, \\*\\* p<0.01, \\*\\*\\* p<0.001*")

        return "\n".join(sections)

    def _per_strategy_details(self) -> str:
        """Generate per-strategy detail sections."""
        sections = ["## Per-Strategy Details"]

        for name, result in self._result.strategy_results.items():
            sections.append(f"\n### {name}")
            sections.append("")

            # Quality metrics
            if result.quality:
                sections.append("**Quality:**")
                for metric, value in sorted(result.quality.items()):
                    sections.append(f"- {metric}: {_format_metric(value)}")

            # Routing info
            routing = result.routing
            if routing:
                tier_dist = routing.get("tier_distribution", {})
                if tier_dist:
                    sections.append("")
                    sections.append("**Tier Distribution:**")
                    for tier, pct in sorted(tier_dist.items()):
                        sections.append(f"- {tier}: {_format_pct(pct)}")

        return "\n".join(sections)

    def _footer(self) -> str:
        """Generate report footer."""
        return """---

*Report generated by EB1 Evaluation System*"""


def generate_evaluation_report(
    result: EvaluationResult,
    output_path: Path | str | None = None,
) -> str:
    """Generate evaluation report.

    Args:
        result: EvaluationResult to report on.
        output_path: Optional path to save report.

    Returns:
        Markdown report string.
    """
    report = EvaluationReport(result)
    content = report.generate()

    if output_path:
        report.save(output_path)

    return content


def generate_comparison_report(
    result: ComparisonResult,
    output_path: Path | str | None = None,
) -> str:
    """Generate comparison report.

    Args:
        result: ComparisonResult to report on.
        output_path: Optional path to save report.

    Returns:
        Markdown report string.
    """
    report = ComparisonReport(result)
    content = report.generate()

    if output_path:
        report.save(output_path)

    return content


__all__ = [
    "ComparisonReport",
    "EvaluationReport",
    "generate_comparison_report",
    "generate_evaluation_report",
]
