import time

from meshy.path_history import PathHistoryService, PathRecord
from meshy.storage import Storage

CONTACT_HEX = "cc" * 32


def _make_storage():
    return Storage(db_path=":memory:")


def _make_record(success=10, failure=0, trip_ms=500, last_success_at=None, **kwargs):
    return PathRecord(
        id=1,
        contact_key_hex=CONTACT_HEX,
        path_bytes="aabb",
        path_hash_size=1,
        hop_count=2,
        success_count=success,
        failure_count=failure,
        last_trip_ms=trip_ms,
        route_weight=2.5,
        last_success_at=last_success_at or time.time(),
        **kwargs,
    )


# ─── score() ──────────────────────────────────────────────────


class TestScore:
    def setup_method(self):
        self.storage = _make_storage()
        self.service = PathHistoryService(self.storage)

    def teardown_method(self):
        self.storage.close()

    def test_reliability_dominates(self):
        good = _make_record(success=100, failure=0)
        bad = _make_record(success=0, failure=100)
        assert self.service.score(good) > self.service.score(bad)

    def test_faster_path_scores_higher(self):
        fast = _make_record(success=10, failure=0, trip_ms=100)
        slow = _make_record(success=10, failure=0, trip_ms=10000)
        assert self.service.score(fast) > self.service.score(slow)

    def test_fresher_path_scores_higher(self):
        now = time.time()
        fresh = _make_record(last_success_at=now)
        stale = _make_record(last_success_at=now - 30 * 86400)
        assert self.service.score(fresh) > self.service.score(stale)

    def test_no_trip_ms_uses_default(self):
        rec = _make_record(trip_ms=None)
        score = self.service.score(rec)
        assert score > 0

    def test_no_last_success_zero_freshness(self):
        rec = _make_record(last_success_at=None)
        score = self.service.score(rec)
        assert score > 0


# ─── select_path() ────────────────────────────────────────────


class TestSelectPath:
    def setup_method(self):
        self.storage = _make_storage()
        self.service = PathHistoryService(self.storage)

    def teardown_method(self):
        self.storage.close()

    def test_empty_returns_none(self):
        assert self.service.select_path(CONTACT_HEX) is None

    def test_returns_best_scored(self):
        self.storage.upsert_path_record(CONTACT_HEX, "good", 1, 1, success=True, trip_ms=100)
        self.storage.upsert_path_record(CONTACT_HEX, "bad", 1, 3, success=False)
        # Add more successes to "good"
        for _ in range(5):
            self.storage.upsert_path_record(CONTACT_HEX, "good", 1, 1, success=True, trip_ms=100)
        best = self.service.select_path(CONTACT_HEX)
        assert best is not None
        assert best.path_bytes == "good"

    def test_exclude_paths(self):
        self.storage.upsert_path_record(CONTACT_HEX, "path_a", 1, 1, success=True, trip_ms=100)
        self.storage.upsert_path_record(CONTACT_HEX, "path_b", 1, 2, success=True, trip_ms=200)
        result = self.service.select_path(CONTACT_HEX, exclude_paths=["path_a"])
        assert result is not None
        assert result.path_bytes == "path_b"

    def test_exclude_all_returns_none(self):
        self.storage.upsert_path_record(CONTACT_HEX, "only", 1, 1, success=True)
        assert self.service.select_path(CONTACT_HEX, exclude_paths=["only"]) is None


# ─── record_result() ──────────────────────────────────────────


class TestRecordResult:
    def setup_method(self):
        self.storage = _make_storage()
        self.service = PathHistoryService(self.storage)

    def teardown_method(self):
        self.storage.close()

    def test_success_increments(self):
        self.service.record_result(CONTACT_HEX, "path1", 1, 2, success=True, trip_ms=300)
        self.service.record_result(CONTACT_HEX, "path1", 1, 2, success=True, trip_ms=250)
        history = self.storage.get_path_history(CONTACT_HEX)
        assert history[0]["success_count"] == 2

    def test_failure_increments(self):
        self.service.record_result(CONTACT_HEX, "path1", 1, 2, success=True)
        self.service.record_result(CONTACT_HEX, "path1", 1, 2, success=False)
        history = self.storage.get_path_history(CONTACT_HEX)
        assert history[0]["failure_count"] == 1
