"""Tests for the standard OBD services + trip/performance (no hardware)."""
import os
import sys

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

from obdcore import obdservices as svc
from obdcore.trip import TripComputer, PerformanceMeter, KMH_PER_MPH


def test_decode_vin():
    vin = "1FMZU73E12ZA12345"
    data = [0x49, 0x02, 0x01] + [ord(c) for c in vin]
    assert svc.decode_vin(data) == vin
    assert svc.decode_vin([0x41, 0x00]) is None        # not a mode-09 response
    print("  VIN decode: OK")


def test_decode_readiness():
    # A=MIL off/0 DTCs, B=3 continuous supported+ready, C=Cat/O2/O2htr supported,
    # D=O2 sensor incomplete
    r = svc.decode_readiness([0x00, 0x07, 0x61, 0x20])
    assert r["mil"] is False and r["dtc_count"] == 0 and r["ignition"] == "spark"
    by = {m["name"]: m["ready"] for m in r["monitors"]}
    assert by["Misfire"] and by["Fuel System"] and by["Components"]
    assert by["Catalyst"] is True and by["O2 Sensor"] is False
    assert r["total"] == 6 and r["ready_count"] == 5
    # diesel flag
    rc = svc.decode_readiness([0x80, 0x0F, 0x00, 0x00])
    assert rc["mil"] is True and rc["ignition"] == "compression"
    print(f"  readiness decode: {r['ready_count']}/{r['total']} ready, O2 not ready: OK")


def test_trip_computer():
    tc = TripComputer()
    t = 0.0
    for _ in range(360):                # 6 min at 1 Hz, 96.56 km/h (60 mph), 12 g/s
        tc.update(t, 96.56, 12.0)
        t += 1.0
    s = tc.stats()
    assert 5.5 < s["distance_mi"] < 6.5, s          # 60mph * 0.1h = 6 mi
    assert s["avg_mpg"] > 5 and s["avg_mpg"] < 60, s
    inst = tc.instant_mpg(96.56, 12.0)
    assert inst > 0
    print(f"  trip: {s['distance_mi']}mi, {s['avg_mpg']} avg mpg, "
          f"{inst:.1f} inst: OK")


def test_performance_meter():
    pm = PerformanceMeter()
    t = 0.0
    # parked
    for _ in range(3):
        pm.update(t, 0.0); t += 0.5
    # accelerate 0 -> 70 mph over 7s (mph), then cruise to cover 1/4 mile
    for i in range(1, 200):
        t = 1.5 + i * 0.1
        mph = min(70.0, (t - 1.5) * 10.0)            # 10 mph/s
        pm.update(t, mph * KMH_PER_MPH)
    assert pm.best_0_60 is not None, "should have timed 0-60"
    assert 5.0 < pm.best_0_60 < 8.0, pm.best_0_60    # ~6s at 10mph/s
    assert pm.best_quarter is not None, "should have timed 1/4 mile"
    print(f"  performance: 0-60 {pm.best_0_60}s, 1/4mi {pm.best_quarter}s: OK")


if __name__ == "__main__":
    for fn in [test_decode_vin, test_decode_readiness, test_trip_computer,
               test_performance_meter]:
        fn()
    print("\nALL SERVICE TESTS PASS")
