"""Unit tests for the pure card/badge renderers (harness.card) — phase lvl5 semantics.

Covers the deterministic HTML + SVG string builders (the PNG step needs Playwright + is exercised
live). The cardinal check: the card REPORTS the data verbatim — level/marks come straight from the
dict, never recomputed — the badge is NUMBER + COLOUR ONLY, and the per-rung table rows (incl.
intentional-skip / unverified) are the sole carrier of "why isn't the level higher". Old schema-1
artifacts (4-rung ladder, cap fields present) must render without error and without relabeling.
Run cold:  cc-ci-run -m pytest tests/unit/test_card.py -q
"""

from __future__ import annotations

import os
import sys

sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "runner"))
from harness import card as C  # noqa: E402


def _data(level=5, **kw):
    d = {
        "schema": 2,
        "recipe": "uptime-kuma",
        "version": "1.23.0",
        "level": level,
        "rungs": {
            "install": "pass",
            "upgrade": "pass",
            "backup_restore": "pass",
            "functional": "pass",
            "lint": "pass",
        },
        "flags": {"clean_teardown": True, "no_secret_leak": True},
        "screenshot": "screenshot.png",
        "stages": [
            {
                "name": "install",
                "status": "pass",
                "tests": [{"name": "test_serving", "status": "pass", "ms": 168}],
            },
            {
                "name": "custom",
                "status": "fail",
                "tests": [
                    {"name": "test_health", "status": "pass", "ms": 17},
                    {"name": "test_broken", "status": "fail", "ms": 5},
                ],
            },
            {
                "name": "lint",
                "status": "pass",
                "tests": [{"name": "abra recipe lint", "status": "pass", "ms": 0}],
            },
        ],
    }
    d.update(kw)
    return d


def test_level_color_ramp():
    # 0 (red) … 5 (bright green — full 5-rung climb); unknown → grey.
    assert C.level_color(0) != C.level_color(5)
    assert C.level_color(5) == "#3fb950"
    assert C.level_color(99) == "#8b949e"


def test_badge_svg_is_number_and_color_only():
    svg = C.level_badge_svg(4)
    assert svg.startswith("<svg") and svg.endswith("</svg>")
    assert "level 4" in svg
    assert C.level_color(4) in svg
    # operator-specified (phase lvl5): NOTHING but the level on the badge — no annotation
    # segment of any kind, whatever the rung situation.
    assert "expected" not in svg and "gap?" not in svg and "skip" not in svg


def test_badge_svg_level5():
    svg = C.level_badge_svg(5)
    assert "level 5" in svg and "#3fb950" in svg


def test_skip_rows_intentional_and_unverified():
    html_out = C._skip_rows(
        {"intentional": {"backup_restore": "no persistent data"}, "unintentional": ["functional"]}
    )
    # intentional skip: labelled row (muted green) + the reason on its own line
    assert "intentional skip" in html_out and C.SKIP_GREEN in html_out
    assert "backup/restore" in html_out and "no persistent data" in html_out
    # unverified rung: amber row + the blocks-the-level explanation
    assert "unverified" in html_out and C.GAP_COLOR in html_out
    assert "functional" in html_out and "cannot rise above" in html_out


def test_skip_rows_lint_label_known():
    html_out = C._skip_rows({"intentional": {}, "unintentional": ["lint"]})
    assert ">lint<" in html_out.replace("</b>", "<")  # rung label renders, not a KeyError


def test_skip_rows_empty_when_no_skips():
    assert C._skip_rows({"intentional": {}, "unintentional": []}) == ""


def test_card_html_reports_level_verbatim():
    html = C.render_card_html(_data(level=2))
    assert "uptime-kuma" in html
    assert "1.23.0" in html
    # the level shown is exactly what was passed (no recompute)
    assert ">2<" in html
    assert "level 2 of 5" in html
    assert C.level_color(2) in html


def test_card_html_no_cap_language():
    html = C.render_card_html(_data())
    assert "capped" not in html and "cap_reason" not in html
    assert "level 5 of 5" in html


def test_card_html_old_schema1_artifact_renders():
    # history compatibility: a pre-lvl5 results.json (4-rung ladder, cap fields, "na" statuses)
    # renders without KeyError and shows ITS OWN ladder height (no retroactive relabeling).
    old = {
        "schema": 1,
        "recipe": "legacy",
        "version": "0.9",
        "level": 4,
        "level_cap_reason": "",
        "level_cap_rung": None,
        "rungs": {
            "install": "pass",
            "upgrade": "pass",
            "backup_restore": "pass",
            "functional": "pass",
        },
        "skips": {"intentional": {}, "unintentional": []},
        "flags": {"clean_teardown": True, "no_secret_leak": True},
        "screenshot": None,
        "stages": [],
    }
    html = C.render_card_html(old)
    assert "legacy" in html
    assert "level 4 of 4" in html  # the old top, not 5
    assert "capped" not in html


def test_card_html_shows_stage_and_test_marks_incl_lint():
    html = C.render_card_html(_data())
    assert "install" in html and "custom" in html
    assert "abra recipe lint" in html
    assert "test_serving" in html and "test_broken" in html
    assert C.STATUS_MARK["pass"] in html and C.STATUS_MARK["fail"] in html


def test_card_html_unver_stage_mark_renders():
    d = _data()
    d["stages"][2] = {
        "name": "lint",
        "status": "unver",
        "tests": [{"name": "abra recipe lint", "status": "unver", "ms": 0, "message": "timed out"}],
    }
    html = C.render_card_html(d)
    assert C.STATUS_MARK["unver"] in html
    assert C.STATUS_COLOR["unver"] in html


def test_card_html_flags_rendered():
    html = C.render_card_html(_data())
    assert "clean teardown" in html and "no secret leak" in html


def test_card_html_no_screenshot_placeholder():
    d = _data()
    d["screenshot"] = None
    html = C.render_card_html(d)
    assert "no screenshot" in html


def test_card_html_escapes_recipe_name():
    d = _data()
    d["recipe"] = "<script>x</script>"
    html = C.render_card_html(d)
    assert "<script>x" not in html
    assert "&lt;script&gt;" in html
