import json
from services.archive import (
    Archive,
    Material,
    DEFAULT_BUTTON_LABEL,
    add_material,
    channel_url,
    format_material_response,
    format_list_materials,
    load_archive,
    save_archive,
    set_required_channel,
)


def _mat(id_="a", title="Aaa", url="https://x", label=DEFAULT_BUTTON_LABEL):
    return Material(id=id_, title=title, description="d", url=url, button_label=label)


def test_material_default_button_label():
    assert Material(id="a", title="t", description="d", url="https://x").button_label == DEFAULT_BUTTON_LABEL


def test_archive_required_channel_defaults_none():
    assert Archive().required_channel is None


def test_add_material_empty_label_uses_default():
    arc = Archive()
    m = add_material(arc, title="Rec", description="d", url="https://x", button_label="")
    assert m.button_label == DEFAULT_BUTTON_LABEL


def test_add_material_custom_label_kept():
    arc = Archive()
    m = add_material(arc, title="Skill", description="d", url="https://gh", button_label="💻 GitHub")
    assert m.button_label == "💻 GitHub"


def test_format_material_response_open_uses_url_button():
    text, kb = format_material_response(_mat(label="💻 GitHub"), gated=False)
    assert "🎬" not in text
    assert text == "<b>Aaa</b>\n\nd"
    btn = kb.inline_keyboard[0][0]
    assert btn.text == "💻 GitHub"
    assert btn.url == "https://x"
    assert btn.callback_data is None


def test_format_material_response_gated_uses_watch_callback():
    text, kb = format_material_response(_mat(id_="rec", label="🎬 Смотреть"), gated=True)
    btn = kb.inline_keyboard[0][0]
    assert btn.text == "🎬 Смотреть"
    assert btn.callback_data == "watch:rec"
    assert btn.url is None


def test_watch_callback_within_limit():
    long_id = "x" * 53
    _, kb = format_material_response(_mat(id_=long_id), gated=True)
    assert len(kb.inline_keyboard[0][0].callback_data.encode("utf-8")) <= 64


def test_channel_url():
    assert channel_url("@myChan") == "https://t.me/myChan"


def test_set_required_channel():
    arc = Archive()
    set_required_channel(arc, "@c")
    assert arc.required_channel == "@c"
    set_required_channel(arc, None)
    assert arc.required_channel is None


def test_format_list_materials_shows_label():
    arc = Archive(materials=[_mat(label="💻 GitHub")])
    out = format_list_materials(arc.materials)
    assert "label: 💻 GitHub" in out


def test_save_load_roundtrip_new_fields(tmp_path):
    arc = Archive(materials=[_mat(label="💻 GitHub")], required_channel="@c")
    p = str(tmp_path / "archive.json")
    save_archive(p, arc)
    loaded = load_archive(p)
    assert loaded.required_channel == "@c"
    assert loaded.materials[0].button_label == "💻 GitHub"


def test_format_material_response_escapes_title():
    mat = _mat(title="<b>Evil</b> Title")
    text, _kb = format_material_response(mat, gated=False)
    assert "<b>Evil</b> Title" not in text
    assert text == "<b>&lt;b&gt;Evil&lt;/b&gt; Title</b>\n\nd"


def test_format_material_response_preserves_description_html():
    # description comes from message.html_text — formatting is kept on purpose.
    mat = _mat()
    mat.description = "<b>Bold</b> desc"
    text, _kb = format_material_response(mat, gated=False)
    assert "<b>Bold</b> desc" in text


def test_load_legacy_material_gets_default_label(tmp_path):
    p = str(tmp_path / "archive.json")
    with open(p, "w", encoding="utf-8") as f:
        json.dump({"welcome": None, "materials": [
            {"id": "a", "title": "T", "description": "d", "url": "https://x"}
        ]}, f)
    loaded = load_archive(p)
    assert loaded.materials[0].button_label == DEFAULT_BUTTON_LABEL
    assert loaded.required_channel is None
