"""観測結果の解析と使い方の型のテスト（SPEC_03「取得」「使い方の型」「表示」）."""

from __future__ import annotations

import pytest
from probe_helpers import PMON_HEADER

from tias_judge_model.remote.observe import (
    GpuSample,
    UsagePattern,
    classify_usage_pattern,
    format_elapsed,
    parse_gpu_samples,
    parse_pmon,
    summarize_samples,
)


def _samples(utils: list[int], memory: int = 10) -> list[GpuSample]:
    return [GpuSample(util, memory) for util in utils]


def _pattern(utils: list[int], process_count: int | None) -> UsagePattern | None:
    usage = summarize_samples(_samples(utils))
    assert usage is not None
    return classify_usage_pattern(usage, process_count)


class TestParseGpuSamples:
    def test_groups_samples_by_gpu_index_in_order(self) -> None:
        text = "0, 5, 300\n1, 0, 10\n0, 74, 310\n1, 0, 12\n"
        samples = parse_gpu_samples(text)
        assert samples[0] == [GpuSample(5, 300), GpuSample(74, 310)]
        assert samples[1] == [GpuSample(0, 10), GpuSample(0, 12)]

    def test_empty_output_has_no_samples(self) -> None:
        assert parse_gpu_samples("") == {}


class TestSummarizeSamples:
    def test_mean_and_max(self) -> None:
        usage = summarize_samples(
            [GpuSample(0, 100), GpuSample(74, 300), GpuSample(10, 200), GpuSample(0, 50)]
        )
        assert usage is not None
        assert usage.sample_count == 4
        assert usage.active_count == 2
        assert usage.mean_utilization_percent == pytest.approx(21.0)
        assert usage.max_utilization_percent == 74
        assert usage.max_memory_used_mib == 300

    def test_no_samples(self) -> None:
        assert summarize_samples([]) is None


class TestUsagePattern:
    @pytest.mark.parametrize(
        ("utils", "expected"),
        [
            ([1] * 10, UsagePattern.CONSTANT),
            ([1] * 8 + [0] * 2, UsagePattern.CONSTANT),  # ちょうど 80%
            ([1] * 4 + [0], UsagePattern.CONSTANT),  # 4/5 = 80%
            ([1] * 7 + [0] * 3, UsagePattern.INTERMITTENT),  # 70%
            ([1] * 79 + [0] * 21, UsagePattern.INTERMITTENT),  # 79%
            ([1] + [0] * 9, UsagePattern.INTERMITTENT),  # 1 件
        ],
    )
    def test_active_ratio_boundaries(self, utils: list[int], expected: UsagePattern) -> None:
        # 使用率が 0% を超えたサンプルがあれば，計算プロセスの有無によらず常時／断続的
        assert _pattern(utils, 0) is expected
        assert _pattern(utils, 2) is expected

    def test_memory_only(self) -> None:
        assert _pattern([0, 0, 0], 1) is UsagePattern.MEMORY_ONLY

    def test_idle(self) -> None:
        assert _pattern([0, 0, 0], 0) is UsagePattern.IDLE


class TestParsePmon:
    def test_max_sm_per_compute_process(self) -> None:
        text = "\n".join(
            [
                *PMON_HEADER,
                "    0       1234     C     10      1      -      -      -      -    python",
                "    1       5678     C      0      0      -      -      -      -    python",
                "    0       1234     C     74      9      -      -      -      -    python",
                "    0       1234     C      3      9      -      -      -      -    python",
            ]
        )
        assert parse_pmon(text) == {1234: 74, 5678: 0}

    def test_header_dash_and_graphics_rows_are_excluded(self) -> None:
        text = "\n".join(
            [
                *PMON_HEADER,
                "    0          -     -      -      -      -      -      -      -    -",
                "    0        999     G     50      2      -      -      -      -    Xorg",
                "    0       1234     C      -      -      -      -      -      -    python",
                "    0       1234     C     20      -      -      -      -      -    python",
            ]
        )
        peaks = parse_pmon(text)
        assert peaks == {1234: 20}

    def test_without_header_is_unavailable(self) -> None:
        assert parse_pmon("") is None


class TestFormatElapsed:
    def test_spec_example_hours(self) -> None:
        # SPEC_03「表示」の例: 27.7 時間
        assert format_elapsed(99720) == "27.7 時間"
