"""Tests for ablevlabs.lana — run with:  pytest"""

import warnings
import pytest
from ablevlabs import lana


# ----------------------------- core behavior ----------------------------- #
def test_basic_numeric_winner():
    r = lana.compare({"x": 10}, {"x": 5}, "A", "B")
    assert r["fields"]["x"]["winner"] == "A"
    assert r["fields"]["x"]["delta"] == 5


def test_lower_better_flips_winner():
    r = lana.compare({"price": 10}, {"price": 5}, "A", "B", lower_better=["price"])
    assert r["fields"]["price"]["winner"] == "B"  # cheaper wins


def test_priority_drives_verdict():
    a = {"power": 100, "speed": 10}
    b = {"power": 90, "speed": 50}
    # power matters most -> A wins
    r = lana.compare(a, b, "A", "B", priority={"power": 5, "speed": 1})
    assert r["verdict"]["winner"] == "A"
    # speed matters most -> B wins (verdict flips on same data)
    r2 = lana.compare(a, b, "A", "B", priority={"power": 1, "speed": 5})
    assert r2["verdict"]["winner"] == "B"


def test_tie():
    r = lana.compare({"x": 5}, {"x": 5}, "A", "B")
    assert r["fields"]["x"]["winner"] == "tie"


# ------------------------- bug fixes / edge cases ------------------------- #
def test_booleans_are_not_numbers():
    """Regression: True/False must NOT be scored as 1/0."""
    r = lana.compare({"flag": True}, {"flag": False}, "A", "B")
    # should be a match-style comparison, not a numeric winner
    assert "winner" not in r["fields"]["flag"]
    assert r["fields"]["flag"]["match"] is False


def test_priority_out_of_range_raises():
    with pytest.raises(ValueError):
        lana.compare({"x": 1}, {"x": 2}, priority={"x": 0})
    with pytest.raises(ValueError):
        lana.compare({"x": 1}, {"x": 2}, priority={"x": 99})
    with pytest.raises(ValueError):
        lana.compare({"x": 1}, {"x": 2}, priority={"x": "5"})  # wrong type


def test_priority_typo_warns():
    with pytest.warns(UserWarning):
        lana.compare({"price": 1}, {"price": 2}, priority={"prce": 3})


def test_priority_on_text_field_warns():
    with pytest.warns(UserWarning):
        lana.compare({"race": "saiyan"}, {"race": "human"}, priority={"race": 5})


def test_missing_field_marked_only_in():
    r = lana.compare({"a": 1, "b": 2}, {"a": 1}, "X", "Y")
    assert r["fields"]["b"]["only_in"] == "X"


def test_empty_dicts_do_not_crash():
    r = lana.compare({}, {}, "X", "Y")
    assert r["fields"] == {}
    assert r["verdict"] is None


# ------------------------------ aliases ----------------------------------- #
def test_aliases_are_per_call_no_global_leak():
    r = lana.compare({"yrs": 5}, {"years": 3}, "X", "Y", aliases={"yrs": "years"})
    assert "years" in r["fields"]
    # a second call WITHOUT aliases must not remember the previous mapping
    r2 = lana.compare({"yrs": 5}, {"years": 3}, "X", "Y")
    assert "yrs" in r2["fields"] and "years" in r2["fields"]


# ---------------------------- auto-naming --------------------------------- #
def test_auto_name_from_record():
    r = lana.compare({"name": "Goku", "p": 9}, {"name": "Vegeta", "p": 8})
    assert r["matchup"] == ["Goku", "Vegeta"]


# ---------------------------- nested compare ------------------------------ #
def test_nested_dict_comparison():
    a = {"stats": {"hp": 100, "atk": 20}}
    b = {"stats": {"hp": 80, "atk": 30, "def": 5}}
    r = lana.compare(a, b, "A", "B")
    node = r["fields"]["stats"]
    assert node["nested"] is True
    assert node["shared"]["hp"]["winner"] == "A"
    assert node["shared"]["atk"]["winner"] == "B"
    assert "def" in node["only_B"]


# ---------------------------- exporters ----------------------------------- #
def test_exporters_run():
    r = lana.compare({"name": "A", "x": 1}, {"name": "B", "x": 2}, priority={"x": 3})
    assert isinstance(lana.to_json(r), str)
    assert "field" in lana.to_csv(r)
    assert "|" in lana.to_markdown(r)


def test_to_df_optional_pandas():
    pd = pytest.importorskip("pandas")
    r = lana.compare({"x": 1}, {"x": 2}, "A", "B")
    df = lana.to_df(r)
    assert list(df.columns) == ["field", "A", "B", "delta", "winner", "priority"]


def test_save_picks_format_by_extension(tmp_path):
    r = lana.compare({"x": 1}, {"x": 2}, "A", "B")
    j = lana.save(r, str(tmp_path / "out.json"))
    c = lana.save(r, str(tmp_path / "out.csv"))
    m = lana.save(r, str(tmp_path / "out.md"))
    assert open(j).read().lstrip().startswith("{")
    assert open(c).read().startswith("field,")
    assert open(m).read().startswith("| field")


# ---------------------------- rank() (v0.2.0) ----------------------------- #
def test_rank_basic_leaderboard():
    items = [
        {"name": "Frieza", "power": 10500, "attack": 24},
        {"name": "Vegeta", "power": 9000, "attack": 25},
        {"name": "Goku", "power": 9100, "attack": 22},
    ]
    r = lana.rank(items, priority={"power": 5, "attack": 4})
    names = [e["name"] for e in r["ranking"]]
    assert names[0] == "Frieza"  # wins power (highest priority)
    assert r["winner"] == "Frieza"
    assert [e["rank"] for e in r["ranking"]] == [1, 2, 3]


def test_rank_two_items_matches_compare():
    a = {"name": "Zoro", "power": 100, "speed": 10}
    b = {"name": "Sanji", "power": 90, "speed": 50}
    prio = {"power": 5, "speed": 1}
    cmp = lana.compare(a, b, priority=prio)
    rnk = lana.rank([a, b], priority=prio)
    assert cmp["verdict"]["winner"] == rnk["winner"]  # same winner at N=2


def test_rank_accepts_dict_of_named_items():
    items = {"X": {"score": 9}, "Y": {"score": 5}, "Z": {"score": 7}}
    r = lana.rank(items, priority={"score": 3})
    assert [e["name"] for e in r["ranking"]] == ["X", "Z", "Y"]


def test_rank_lower_better():
    items = [{"name": "Cheap", "price": 100}, {"name": "Pricey", "price": 300}]
    r = lana.rank(items, priority={"price": 3}, lower_better=["price"])
    assert r["winner"] == "Cheap"


def test_rank_default_priority_uses_all_numeric():
    items = [{"name": "A", "x": 10, "y": 1}, {"name": "B", "x": 1, "y": 10}]
    r = lana.rank(items)  # no priority -> all numeric fields equal
    assert set(r["scored_fields"]) == {"x", "y"}


def test_rank_requires_two():
    import pytest

    with pytest.raises(ValueError):
        lana.rank([{"name": "Solo", "x": 1}])


def test_rank_exporters_and_show(capsys):
    items = [{"name": "A", "x": 9}, {"name": "B", "x": 5}, {"name": "C", "x": 7}]
    r = lana.rank(items, priority={"x": 3})
    assert "rank" in lana.to_csv(r)
    assert "|" in lana.to_markdown(r)
    assert isinstance(lana.to_json(r), str)
    lana.show(r)
    out = capsys.readouterr().out
    assert "LEADERBOARD" in out


def test_rank_to_df_columns():
    import pytest

    pytest.importorskip("pandas")
    items = [{"name": "A", "x": 9, "y": 2}, {"name": "B", "x": 5, "y": 8}]
    df = lana.to_df(lana.rank(items, priority={"x": 3, "y": 2}))
    assert list(df.columns) == ["rank", "name", "x", "y", "score"]
