"""Tests for review terminal formatter."""

from __future__ import annotations

from bandito.review.formatter import format_review
from bandito.review.models import (
    AggregateStats,
    Distribution,
    ModelBreakdown,
    Pattern,
    ReviewConfig,
    ReviewResult,
    SpanKindBreakdown,
)


def _make_result(**kwargs) -> ReviewResult:
    defaults = dict(
        config=ReviewConfig(project="test-project"),
        stats=AggregateStats(
            trace_count=10,
            span_count=25,
            cost=Distribution(
                min=0.001, max=0.1, mean=0.01, median=0.008,
                p95=0.05, p99=0.09, total=0.1, count=10,
            ),
            latency=Distribution(
                min=0.5, max=5.0, mean=2.0, median=1.8,
                p95=4.5, p99=4.9, total=20.0, count=10,
            ),
            tokens=Distribution(
                min=50, max=500, mean=200, median=180,
                p95=450, p99=490, total=2000, count=10,
            ),
            models=[
                ModelBreakdown(
                    model="gpt-4o", provider="openai", count=10,
                    total_cost=0.1, avg_cost=0.01, avg_latency=2.0, total_tokens=2000,
                ),
            ],
        ),
        mode="bootstrap",
        command="basics",
    )
    defaults.update(kwargs)
    return ReviewResult(**defaults)


class TestHeader:
    def test_contains_project(self):
        output = format_review(_make_result())
        assert "test-project" in output

    def test_contains_command_name(self):
        output = format_review(_make_result(command="basics"))
        assert "bandito observe basics" in output

    def test_contains_trace_count(self):
        output = format_review(_make_result())
        assert "10 traces" in output

    def test_contains_span_count(self):
        output = format_review(_make_result())
        assert "25 spans" in output

    def test_no_grades_shown(self):
        output = format_review(_make_result())
        assert "No grades yet" in output

    def test_grading_progress(self):
        stats = _make_result().stats
        stats.human_eval_count = 4
        stats.human_avg_grade = 0.75
        output = format_review(_make_result(stats=stats))
        assert "4 human-graded" in output
        assert "0.75" in output

    def test_filter_last_n(self):
        output = format_review(_make_result(
            config=ReviewConfig(project="p", last_n=50),
        ))
        assert "last 50" in output

    def test_error_count(self):
        stats = _make_result().stats
        stats.error_count = 5
        output = format_review(_make_result(stats=stats))
        assert "5 errors" in output


class TestCostSection:
    def test_cost_values(self):
        output = format_review(_make_result())
        assert "$0.1000" in output
        assert "$0.0100" in output

    def test_no_cost_data(self):
        stats = AggregateStats(trace_count=5, span_count=5, cost=Distribution())
        output = format_review(_make_result(stats=stats))
        assert "No cost data" in output
        assert "framework mapper" in output

    def test_partial_cost_shows_coverage(self):
        stats = AggregateStats(
            trace_count=20, span_count=40,
            cost=Distribution(min=0.001, max=0.01, mean=0.005, median=0.005,
                              p95=0.009, p99=0.01, total=0.06, count=12),
        )
        output = format_review(_make_result(stats=stats))
        assert "12 of 20" in output
        assert "8 traces missing cost" in output


class TestModelsSection:
    def test_model_name(self):
        output = format_review(_make_result())
        assert "gpt-4o" in output
        assert "openai" in output


class TestSpanKindsSection:
    def test_span_kinds_shown(self):
        stats = _make_result().stats
        stats.span_kinds = [
            SpanKindBreakdown(kind="llm", count=15, pct_of_total=80.0),
            SpanKindBreakdown(kind="tool", count=10, pct_of_total=20.0),
        ]
        output = format_review(_make_result(stats=stats))
        assert "Span Kinds" in output
        assert "llm" in output
        assert "tool" in output


class TestPatternsSection:
    def test_human_readable_name(self):
        output = format_review(_make_result(
            patterns=[
                Pattern(name="error_patterns", severity="warning",
                        description="Something", evidence=["e1"]),
            ]
        ))
        assert "Error clusters" in output

    def test_severity_marks(self):
        output = format_review(_make_result(
            patterns=[
                Pattern(name="p1", severity="critical", description="crit", evidence=[]),
                Pattern(name="p2", severity="warning", description="warn", evidence=[]),
                Pattern(name="p3", severity="info", description="info", evidence=[]),
            ]
        ))
        assert "!!" in output
        assert " ~" in output
        assert " i" in output


class TestNextSteps:
    def test_bootstrap_suggests_grading(self):
        output = format_review(_make_result(mode="bootstrap"))
        assert "bandito evaluate tui" in output

    def test_suggests_analyze_subcommands(self):
        output = format_review(_make_result())
        assert "bandito analyze cost" in output
        assert "bandito analyze errors" in output
        assert "bandito analyze models" in output

    def test_low_grading_coverage_nudges(self):
        stats = _make_result().stats
        stats.human_eval_count = 3
        output = format_review(_make_result(mode="enhanced", stats=stats))
        assert "Keep grading" in output
        assert "30%" in output


class TestNumberFormatting:
    def test_no_scientific_notation(self):
        import re
        output = format_review(_make_result())
        assert not re.search(r"\d+\.?\d*e[+-]\d+", output.lower())

    def test_large_token_count_has_commas(self):
        stats = _make_result().stats
        stats.models = [
            ModelBreakdown(
                model="gpt-4o", provider="openai", count=100,
                total_cost=1.0, avg_cost=0.01, avg_latency=2.0,
                total_tokens=68400,
            ),
        ]
        output = format_review(_make_result(stats=stats))
        assert "68,400" in output
