"""Unit tests for the IO-free utils module (#82 Phase 1).

These tests depend only on `:utils` so they re-run only when
utils.py itself changes — handler / parser / github edits do not
invalidate them.
"""
from __future__ import annotations

import pytest

from active_claude_github_mcp.utils import (
    SessionActivity,
    check_overwrite_size,
    compose_session_summary,
    meets_confidence_threshold,
)


class TestMeetsConfidenceThreshold:
    def test_high_meets_high(self):
        assert meets_confidence_threshold("high", "high")

    def test_medium_does_not_meet_high(self):
        assert not meets_confidence_threshold("medium", "high")

    def test_medium_meets_medium(self):
        assert meets_confidence_threshold("medium", "medium")

    def test_low_does_not_meet_medium(self):
        assert not meets_confidence_threshold("low", "medium")

    def test_low_meets_low(self):
        assert meets_confidence_threshold("low", "low")

    def test_high_meets_low(self):
        assert meets_confidence_threshold("high", "low")

    def test_invalid_observed_raises(self):
        with pytest.raises(ValueError, match="observed"):
            meets_confidence_threshold("certain", "high")

    def test_invalid_threshold_raises(self):
        with pytest.raises(ValueError, match="threshold"):
            meets_confidence_threshold("high", "bogus")


class TestComposeSessionSummary:
    def test_empty_activity_returns_explanatory_line(self):
        activity = SessionActivity()
        out = compose_session_summary(
            activity, skill_update_candidates=0, has_pending_buffer=False
        )
        assert "No tracked activity" in out

    def test_full_activity(self):
        activity = SessionActivity(
            turns_posted=12,
            turns_by_thread={"D_1": 5, "I_2": 7},
            discussions_created=1,
            issues_created=2,
            forks_executed=1,
            discussions_decided=1,
            tool_errors=0,
        )
        out = compose_session_summary(
            activity, skill_update_candidates=2, has_pending_buffer=False
        )
        assert "12 turn(s)" in out
        assert "2 thread(s)" in out
        assert "1 Discussion(s)" in out
        assert "2 Issue(s)" in out
        assert "1 fork(s)" in out
        assert "1 Discussion(s) decided" in out
        assert "2 skill update candidate(s)" in out
        assert "caveats" not in out.lower()

    def test_caveats_included(self):
        activity = SessionActivity(
            turns_posted=3,
            turns_by_thread={"D_1": 3},
            tool_errors=2,
        )
        out = compose_session_summary(activity, 0, has_pending_buffer=True)
        assert "2 tool error(s)" in out
        assert "pending buffer non-empty at close" in out

    def test_only_candidates(self):
        activity = SessionActivity()
        out = compose_session_summary(
            activity, skill_update_candidates=3, has_pending_buffer=False
        )
        assert "3 skill update candidate(s)" in out


class TestCheckOverwriteSize:
    def test_force_skips_check(self):
        check_overwrite_size("a" * 10000, "", force=True)

    def test_small_current_is_unconstrained(self):
        check_overwrite_size("tiny", "x", force=False)

    def test_hard_cliff_fires(self):
        # Regression for #48: 10K current overwritten with a 38-char
        # placeholder. Must refuse.
        with pytest.raises(ValueError, match="Refusing overwrite"):
            check_overwrite_size("a" * 10000, "x" * 38, force=False)

    def test_ratio_cliff_fires(self):
        # current > 1000 AND new < 50% of current → refuse
        with pytest.raises(ValueError, match="Refusing overwrite"):
            check_overwrite_size("a" * 2000, "x" * 999, force=False)

    def test_ratio_cliff_allows_at_50_percent(self):
        # new exactly 50% of current → allowed
        check_overwrite_size("a" * 2000, "x" * 1000, force=False)

    def test_modest_shrink_allowed(self):
        # 25% shrink — above the 50% floor
        check_overwrite_size("a" * 2000, "x" * 1500, force=False)

    def test_growth_always_allowed(self):
        check_overwrite_size("a" * 10000, "x" * 15000, force=False)

    def test_force_bypasses_ratio_cliff(self):
        check_overwrite_size("a" * 2000, "x" * 100, force=True)


if __name__ == "__main__":
    import sys
    sys.exit(pytest.main([__file__, "-v"]))
