"""Tests on Lab 09's recorded harness runs: T07 (refund one torn $79.50 sleeping pad), a payment provider
slowed to 2 s on purpose, a harness that waits 1 s, and the 30 tasks behind the approval check.
The call IDs and the inputs are the model's real ones. Run: pytest test_harness_practice.py"""
import json

from harness_practice import Shop, make_key, needs_person

RUN = "t07-0"
CALL_1, CALL_2 = "call_JGsyJdOejA7RqXIPBnkgcu33", "call_YRsJzcbg5C6OwiaDuNAMsnKh"   # the model's first ask, and its retry
PAD = {"order_id": "A-1001", "sku": "PAD-UL", "amount": 79.5,
       "reason": "Customer reports one of two sleeping pads arrived torn; refund for 1 pad per item price."}


def timeout_story(mode):
    """Call 1 reaches the provider; the harness gives up at 1 s and says "timed out"; the model asks again
    (call 2, a new tool call with the same inputs) while call 1 is still running; then the provider finishes."""
    shop = Shop()
    k1 = make_key(mode, RUN, CALL_1, "issue_refund", PAD)
    k2 = make_key(mode, RUN, CALL_2, "issue_refund", PAD)
    first, second = shop.start(k1), shop.start(k2)
    for k, status in ((k1, first), (k2, second)):
        if status == "started":
            shop.finish(k, PAD["amount"])
    return second, shop.refunds


def test_keys():
    assert make_key(None, RUN, CALL_1, "issue_refund", PAD) is None
    assert make_key("call_id", RUN, CALL_1, "issue_refund", PAD) == "t07-0:call_JGsyJdOejA7RqXIPBnkgcu33"
    assert make_key("call_id", RUN, CALL_1, "issue_refund", PAD) != make_key("call_id", RUN, CALL_2, "issue_refund", PAD)
    content = make_key("content", RUN, CALL_1, "issue_refund", PAD)
    assert content == 't07-0:issue_refund:' + json.dumps(PAD, sort_keys=True)
    assert content == make_key("content", RUN, CALL_2, "issue_refund", PAD)   # same tool, same inputs
    assert make_key("content", RUN, CALL_1, "get_refunds", {"order_id": "A-1001"}) is None   # reads get no key


def test_a_timeout_is_not_a_no():
    # No key, or a key from the call's ID: the retry is a new refund. $159 for one $79.50 pad, as in 3 of 3 runs.
    assert timeout_story(None) == ("started", [79.5, 79.5])
    assert timeout_story("call_id") == ("started", [79.5, 79.5])
    # A key from the call's content: the copy is refused while the first refund is still running.
    assert timeout_story("content") == ("in_progress", [79.5])


def test_crash_and_resume():
    # The program crashes after the refund ran, before the checkpoint was saved. The restarted run
    # replays the saved call, with its saved ID.
    for mode, want in ((None, "started"), ("call_id", "duplicate"), ("content", "duplicate")):
        shop = Shop()
        key = make_key(mode, RUN, CALL_1, "issue_refund", PAD)
        assert shop.start(key) == "started"
        shop.finish(key, PAD["amount"])
        assert shop.start(make_key(mode, RUN, CALL_1, "issue_refund", PAD)) == want, mode


def test_a_content_key_also_blocks_a_real_repeat():
    # Two torn pads, refunded one at a time with the same details: two separate, finished calls.
    for mode, second in (("content", "duplicate"), ("call_id", "started")):
        shop = Shop()
        k1 = make_key(mode, RUN, CALL_1, "issue_refund", PAD)
        shop.start(k1)
        shop.finish(k1, PAD["amount"])
        assert shop.start(make_key(mode, RUN, CALL_2, "issue_refund", PAD)) == second, mode


def test_who_asks_a_person():
    # The 8 writes that waited for an answer across the 30 tasks (3 were denied: T08, T09, T14).
    asked = [("issue_refund", 249), ("issue_refund", 329), ("issue_refund", 119), ("issue_refund", 175),
             ("issue_refund", 249), ("cancel_order", None), ("cancel_order", None), ("cancel_order", None)]
    for tool, amount in asked:
        args = {"order_id": "A-1000"} | ({"amount": amount} if amount else {})
        assert needs_person(tool, args), (tool, amount)
    # These went straight through: the torn pad ($79.50), four mugs ($96), an address change, a lookup.
    assert not needs_person("issue_refund", {"order_id": "A-1001", "amount": 79.5})
    assert not needs_person("issue_refund", {"order_id": "A-1006", "amount": 96})
    assert not needs_person("issue_refund", {"order_id": "A-1006", "amount": 100})
    assert not needs_person("update_address", {"order_id": "A-1011", "address": "48 Alder Lane, Portland, OR 97211"})
    assert not needs_person("get_order", {"order_id": "A-1001"})
