import pytest
from aix.pipeline import scoring_math as sm

W = sm.effective_weights({"correctness": 50, "completeness": 25, "spelling": 10, "grammar": 10, "handwriting": 5}, None, None)


def test_appendix_c_example():
    # Spec Appendix C: ratings 0.7/0.6/0.8/0.9/0.7, max 3 -> raw 2.115 -> 2.0 at step 0.5
    errors = [{"type": "spelling", "as_written": "সমাজিক"}, {"type": "spelling", "as_written": "পরিবেস"},
              {"type": "grammar"}]
    us = sm.compute_unit(max_mark=3, weights=W, correctness=0.7, completeness=0.6, error_items=errors,
                         legibility_avg=7, step=0.5, rounding="nearest", content_gate=True)
    assert us.ratings["spelling"] == pytest.approx(0.8) and us.ratings["grammar"] == pytest.approx(0.9)
    assert us.raw == pytest.approx(2.115) and us.final == 2.0


@pytest.mark.parametrize("raw,step,mode,expected", [
    (2.25, 0.5, "nearest", 2.5), (2.24, 0.5, "nearest", 2.0), (2.125, 0.25, "nearest", 2.25),
    (2.9, 1, "down", 2.0), (2.1, 1, "up", 3.0), (0.74, 0.25, "nearest", 0.75), (3.375, 0.25, "nearest", 3.5)])
def test_rounding(raw, step, mode, expected):
    assert sm.round_to_step(raw, step, mode) == expected


def test_content_gate_zeroes_presentation_marks():
    us = sm.compute_unit(max_mark=4, weights=W, correctness=0, completeness=0.5, error_items=[],
                         legibility_avg=10, step=0.5, rounding="nearest", content_gate=True)
    assert us.final == 0 and us.gated
    us2 = sm.compute_unit(max_mark=4, weights=W, correctness=0, completeness=0.5, error_items=[],
                          legibility_avg=10, step=0.5, rounding="nearest", content_gate=False)
    assert us2.final > 0


def test_never_exceeds_max():
    us = sm.compute_unit(max_mark=1, weights=W, correctness=1, completeness=1, error_items=[],
                         legibility_avg=10, step=0.25, rounding="up", content_gate=True)
    assert us.final == 1.0


def test_repeated_misspelling_counts_once_and_penalty_floor():
    errs = [{"type": "spelling", "as_written": "বাংলাদেস"}] * 4
    assert sm.count_errors(errs)["spelling"] == 1
    many = [{"type": "spelling", "as_written": f"w{i}"} for i in range(20)]
    us = sm.compute_unit(max_mark=2, weights=W, correctness=1, completeness=1, error_items=many,
                         legibility_avg=10, step=0.5, rounding="nearest", content_gate=True)
    assert us.ratings["spelling"] == 0.5        # floor = 1 - max_spelling_penalty


def test_missing_legibility_redistributes_weight():
    us = sm.compute_unit(max_mark=2, weights=W, correctness=1, completeness=1, error_items=[],
                         legibility_avg=None, step=0.5, rounding="nearest", content_gate=True)
    assert us.final == 2.0 and us.weights["handwriting"] == 0


def test_weight_levels_and_validation():
    cat = {"correctness": 70, "completeness": 25, "spelling": 0, "grammar": 0, "handwriting": 5}
    unit = {"correctness": 100, "completeness": 0, "spelling": 0, "grammar": 0, "handwriting": 0}
    assert sm.effective_weights(cat, None, unit)["correctness"] == 1.0
    with pytest.raises(ValueError):
        sm.effective_weights({"correctness": 50}, None, None)


def test_choice_rule_best_and_first():
    answered = [("q2", 2.0, 10), ("q3", 2.5, 20)]
    assert sm.apply_choice_rule(answered, 1, "best") == {"q3"}
    assert sm.apply_choice_rule(answered, 1, "first") == {"q2"}
    assert sm.apply_choice_rule(answered, 2, "best") == {"q2", "q3"}
    tie = [("q2", 2.0, 10), ("q3", 2.0, 5)]
    assert sm.apply_choice_rule(tie, 1, "best") == {"q3"}   # tie -> earlier answer


@pytest.mark.parametrize("value,err", [(2.3, True), (2.5, False), (-0.5, True), (4.5, True), (4, False)])
def test_validate_override(value, err):
    assert (sm.validate_override(value, max_mark=4, step=0.5) is not None) == err
