import unittest
from stop_rule import run, scripted


class StopRuleTests(unittest.TestCase):
    def test_complete_and_keep_evidence(self):
        r = run(scripted('complete'))
        self.assertEqual((r['status'], r['decisions'], r['tool_calls']), ('complete', 2, 1))
        self.assertIn('Fictional Harbor note', r['events'][0]['result'])

    def test_cap_before_fourth_decision(self):
        r = run(scripted('step-limit'))
        self.assertEqual((r['status'], r['decisions'], r['tool_calls']), ('step_limit', 3, 3))

    def test_repeated_call_not_dispatched(self):
        r = run(scripted('repeat'))
        self.assertEqual((r['status'], r['decisions'], r['tool_calls']), ('repeat_limit', 3, 2))

    def test_send_denied_before_dispatch(self):
        r = run(scripted('denied'))
        self.assertEqual((r['status'], r['tool_calls']), ('denied_action', 0))

    def test_done_on_last_permitted_decision(self):
        r = run(scripted('complete'), max_steps=2)
        self.assertEqual(r['status'], 'complete')

    def test_no_budget_does_not_call_decider(self):
        with self.assertRaises(ValueError):
            run(lambda _: self.fail('must not call'), max_steps=0)

    def test_invalid_limits(self):
        for limits in [{'max_steps': True}, {'max_repeats': 0}, {'seconds': float('nan')}, {'seconds': -1}]:
            with self.subTest(limits=limits), self.assertRaises(ValueError):
                run(scripted('complete'), **limits)

    def test_bad_decision_stops_without_dispatch(self):
        for decision in [None, {'action': 'read_note'}, {'action': 'read_note', 'key': []}]:
            with self.subTest(decision=decision):
                r = run(lambda _: decision)
                self.assertEqual((r['status'], r['tool_calls']), ('invalid_decision', 0))

    def test_deadline_checked_before_decider(self):
        ticks = iter([0.0, 6.0])
        r = run(lambda _: self.fail('must not call'), seconds=5, clock=lambda: next(ticks))
        self.assertEqual((r['status'], r['decisions']), ('time_limit', 0))

    def test_deadline_checked_after_decider(self):
        ticks = iter([0.0, 0.0, 6.0])
        r = run(scripted('complete'), seconds=5, clock=lambda: next(ticks))
        self.assertEqual((r['status'], r['decisions'], r['tool_calls']), ('time_limit', 1, 0))


if __name__ == '__main__':
    unittest.main()
