"""Tests for abreka.metrics."""

import pytest

from abreka.errors import MetricParseError
from abreka.metrics import (
    extract_metrics_from_file,
    extract_metrics_from_output,
    parse_metric_line,
    parse_metric_ref,
    parse_metric_spec,
)


class TestParseMetricLine:
    def test_full_format(self):
        assert parse_metric_line("METRIC:train:loss=0.005") == ("train", "loss", 0.005)

    def test_default_split(self):
        assert parse_metric_line("METRIC:accuracy=0.95") == ("val", "accuracy", 0.95)

    def test_scientific_notation(self):
        assert parse_metric_line("METRIC:train:lr=1e-4") == ("train", "lr", 1e-4)

    def test_no_match(self):
        assert parse_metric_line("INFO: training started") is None
        assert parse_metric_line("") is None

    def test_strips_whitespace(self):
        assert parse_metric_line("  METRIC:val:loss=0.028  ") == ("val", "loss", 0.028)


class TestParseMetricSpec:
    def test_with_split(self):
        assert parse_metric_spec("val:accuracy=0.9912") == ("val", "accuracy", 0.9912)

    def test_without_split(self):
        assert parse_metric_spec("accuracy=0.85") == ("val", "accuracy", 0.85)

    def test_invalid(self):
        with pytest.raises(MetricParseError):
            parse_metric_spec("not a metric")


class TestParseMetricRef:
    def test_with_split(self):
        assert parse_metric_ref("val:accuracy") == ("val", "accuracy")

    def test_without_split(self):
        assert parse_metric_ref("accuracy") == ("val", "accuracy")

    def test_invalid(self):
        with pytest.raises(MetricParseError):
            parse_metric_ref("123:bad")


class TestExtractMetrics:
    def test_multiline(self):
        output = """
Epoch 1/10
METRIC:train:loss=0.005
METRIC:train:accuracy=0.9985
Some other output
METRIC:val:loss=0.028
METRIC:val:accuracy=0.9934
"""
        metrics = extract_metrics_from_output(output)
        assert metrics["train"]["loss"] == 0.005
        assert metrics["train"]["accuracy"] == 0.9985
        assert metrics["val"]["loss"] == 0.028
        assert metrics["val"]["accuracy"] == 0.9934

    def test_override(self):
        output = "METRIC:val:accuracy=0.5\nMETRIC:val:accuracy=0.9\n"
        metrics = extract_metrics_from_output(output)
        assert metrics["val"]["accuracy"] == 0.9

    def test_empty(self):
        assert extract_metrics_from_output("no metrics here") == {}

    def test_extract_metrics_from_file(self, tmp_path):
        output_path = tmp_path / "run_output.txt"
        output_path.write_text(
            "hello\n"
            "METRIC:train:loss=0.005\n"
            "METRIC:val:accuracy=0.9\n"
            "METRIC:val:accuracy=0.95\n"
        )

        metrics = extract_metrics_from_file(output_path)
        assert metrics == {
            "train": {"loss": 0.005},
            "val": {"accuracy": 0.95},
        }
