"""Tests for TrainingDisplay."""

from io import StringIO

from rich.console import Console

from soup_cli.config.schema import SoupConfig
from soup_cli.monitoring.display import TrainingDisplay


def _render_to_str(panel) -> str:
    """Render a Rich Panel to a plain string for assertion."""
    buf = StringIO()
    console = Console(file=buf, width=120, force_terminal=True)
    console.print(panel)
    return buf.getvalue()


def _make_config():
    """Create a minimal SoupConfig for display testing."""
    return SoupConfig(
        base="test-model",
        data={"train": "./data.jsonl"},
        training={"epochs": 3},
    )


def test_display_init():
    """Display should initialize with default values."""
    display = TrainingDisplay(_make_config(), device_name="cuda")
    assert display.current_step == 0
    assert display.total_steps == 0
    assert display.loss == 0.0
    assert display.device_name == "cuda"


def test_display_update():
    """Update should store new metric values."""
    display = TrainingDisplay(_make_config())
    display.total_steps = 100

    display.update(step=50, epoch=1.5, loss=0.876, lr=1e-5, speed=3.2, gpu_mem="12/24 GB")

    assert display.current_step == 50
    assert display.current_epoch == 1.5
    assert display.loss == 0.876
    assert display.lr == 1e-5
    assert display.speed == 3.2
    assert display.gpu_mem == "12/24 GB"


def test_display_render_panel():
    """_render should produce a Panel with correct content."""
    display = TrainingDisplay(_make_config(), device_name="cuda:0")
    display.total_steps = 100
    display.update(step=62, epoch=2.0, loss=0.847, lr=1.4e-5, speed=3.2, gpu_mem="18/24 GB")

    panel = display._render()
    rendered = _render_to_str(panel)
    assert "62/100" in rendered
    assert "0.847" in rendered


def test_display_render_zero_steps():
    """_render with total_steps=0 should not crash (division by zero)."""
    display = TrainingDisplay(_make_config())
    display.total_steps = 0
    panel = display._render()
    assert panel is not None


def test_display_experiment_name():
    """Display should use experiment_name in panel title if set."""
    config = SoupConfig(
        base="test-model",
        data={"train": "./data.jsonl"},
        experiment_name="my-experiment",
    )
    display = TrainingDisplay(config)
    display.total_steps = 10
    panel = display._render()
    rendered = _render_to_str(panel)
    assert "my-experiment" in rendered


def test_display_start_stop():
    """Start and stop should not crash (we don't test actual terminal rendering)."""
    display = TrainingDisplay(_make_config())
    display.start(total_steps=100)
    assert display.total_steps == 100
    assert display._live is not None
    display.stop()


def test_display_update_without_live():
    """Update without calling start should not crash."""
    display = TrainingDisplay(_make_config())
    display.update(step=1, epoch=0.1, loss=2.0, lr=1e-4)
    assert display.current_step == 1
