"""Bi-directional action framework tests (against MockLink, no hardware)."""
import os
import sys

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from obdcore import load_default, load_profile, profiles_dir
from obdcore.actions import Action, ActionStep, run_action, validate_action
from obdcore.mock import MockLink
import time


def _mock():
    return MockLink(clock=time.time)


def test_simple_action():
    a = Action("PING", "Tester Present", steps=[ActionStep("3E00")])
    r = run_action(a, _mock())
    assert r["ok"], r
    print("  simple action (3E00): OK")


def test_session_and_reset():
    a = Action("RESET", "ECU Reset", session="03", steps=[ActionStep("1101")])
    r = run_action(a, _mock())
    assert r["ok"], r
    print("  session + reset: OK")


def test_security_known_algo():
    a = Action("LOCKED", "Secured routine", security={"level": "01", "algorithm": "xor-ff"},
               steps=[ActionStep("31010203")])          # start routine
    r = run_action(a, _mock())
    assert r["ok"], r
    print("  security handshake (xor-ff seed->key): OK")


def test_security_unknown_algo_blocked():
    a = Action("SECRET", "Vendor routine", security={"level": "01", "algorithm": "ford-2005-secret"},
               steps=[ActionStep("31010203")])
    r = run_action(a, _mock())
    assert not r["ok"] and "not available" in r["message"], r
    print("  unknown security algorithm blocked (fails safe): OK")


def test_validate_rejects_bad_hex():
    bad = Action("BAD", "bad", steps=[ActionStep("ZZ")])
    try:
        validate_action(bad)
        raise AssertionError("should reject non-hex send")
    except ValueError:
        pass
    print("  malformed hex rejected at load: OK")


def test_profile_actions_load():
    prof = load_profile(os.path.join(profiles_dir(), "generic-obd2.json"))
    keys = {a.key for a in (prof.actions or [])}
    assert "TESTER_PRESENT" in keys and "ECU_RESET" in keys, keys
    # the reset is risk-gated
    reset = next(a for a in prof.actions if a.key == "ECU_RESET")
    assert reset.risk == "caution" and reset.warning
    print(f"  generic profile loads {len(prof.actions)} actions (risk-tagged): OK")


if __name__ == "__main__":
    for fn in [test_simple_action, test_session_and_reset, test_security_known_algo,
               test_security_unknown_algo_blocked, test_validate_rejects_bad_hex,
               test_profile_actions_load]:
        fn()
    print("\nALL ACTION TESTS PASS")
