"""クラス定義のテスト（SPEC_01「クラス定義」・TEST_01「フォルダ構成とラベル対応」）."""

import pytest

from tias_judge_model.data.classes import (
    ALL_DIR_NAMES,
    CLASS_DISPLAY_NAMES,
    CLASS_SLUGS,
    NUM_CLASSES,
    id_to_label,
    is_valid_combination,
    label_to_id,
    make_dir_name,
    parse_dir_name,
)

# SPEC_01 の固定表（ID・slug・表示名）
SPEC_CLASSES = [
    (0, "pale", "淡白"),
    (1, "light_red", "淡紅"),
    (2, "red", "紅"),
    (3, "dark_red", "暗赤紅"),
    (4, "purple", "紫"),
]

# TEST_01 の対応表（ディレクトリ名は SPEC_01 の `<label>_<severity>` スラッグ）
SPEC_DIRS = {
    "light_red_normal": ("light_red", "normal"),
    "dark_red_strong": ("dark_red", "strong"),
    "dark_red_mild": ("dark_red", "mild"),
    "purple_mild": ("purple", "mild"),
    "red_strong": ("red", "strong"),
    "red_mild": ("red", "mild"),
    "purple_strong": ("purple", "strong"),
    "pale_strong": ("pale", "strong"),
    "pale_mild": ("pale", "mild"),
}


def test_class_ids_follow_fixed_spec_order() -> None:
    assert CLASS_SLUGS == tuple(slug for _, slug, _ in SPEC_CLASSES)
    assert NUM_CLASSES == 5


@pytest.mark.parametrize(("class_id", "slug", "display"), SPEC_CLASSES)
def test_label_id_mapping_matches_spec(class_id: int, slug: str, display: str) -> None:
    assert label_to_id(slug) == class_id
    assert id_to_label(class_id) == slug
    assert CLASS_DISPLAY_NAMES[slug] == display


@pytest.mark.parametrize("label", ["", "Pale", "orange", "淡白", "light-red"])
def test_label_to_id_rejects_unknown_label(label: str) -> None:
    with pytest.raises(ValueError):
        label_to_id(label)


@pytest.mark.parametrize("class_id", [-1, 5, 100])
def test_id_to_label_rejects_out_of_range(class_id: int) -> None:
    with pytest.raises(ValueError):
        id_to_label(class_id)


def test_all_dir_names_are_exactly_the_nine_spec_dirs() -> None:
    assert len(ALL_DIR_NAMES) == 9
    assert set(ALL_DIR_NAMES) == set(SPEC_DIRS)


@pytest.mark.parametrize(("name", "expected"), sorted(SPEC_DIRS.items()))
def test_parse_dir_name_follows_test01_table(name: str, expected: tuple[str, str]) -> None:
    assert parse_dir_name(name) == expected


@pytest.mark.parametrize(("name", "expected"), sorted(SPEC_DIRS.items()))
def test_make_dir_name_is_inverse_of_parse(name: str, expected: tuple[str, str]) -> None:
    assert make_dir_name(*expected) == name


@pytest.mark.parametrize(
    "name",
    [
        "light_red_strong",  # 淡紅は normal のみ
        "light_red_mild",
        "pale_normal",  # normal は淡紅のみ
        "dark_red_normal",
        "red",
        "light_red",
        "",
        "_",
        "orange_strong",
        "red_Strong",
        "purple_mild_extra",
        "淡紅（正常）",  # 日本語のフォルダ名はスラッグ化してから置く
        "暗赤紅＿あり",
    ],
)
def test_parse_dir_name_rejects_invalid_name(name: str) -> None:
    with pytest.raises(ValueError):
        parse_dir_name(name)


@pytest.mark.parametrize(
    ("label", "severity"),
    [("light_red", "strong"), ("pale", "normal"), ("orange", "mild"), ("red", "weak")],
)
def test_make_dir_name_rejects_invalid_combination(label: str, severity: str) -> None:
    with pytest.raises(ValueError):
        make_dir_name(label, severity)


def test_is_valid_combination_matches_test01_table() -> None:
    valid = set(SPEC_DIRS.values())
    for label in CLASS_SLUGS:
        for severity in ("strong", "mild", "normal"):
            assert is_valid_combination(label, severity) == ((label, severity) in valid)
    assert not is_valid_combination("orange", "strong")
    assert not is_valid_combination("red", "weak")
